diff --git a/README-es.md b/README-es.md index c98b1fd..50543b9 100644 --- a/README-es.md +++ b/README-es.md @@ -56,7 +56,10 @@ const provider = new Provider(rpc, sender); Si quieres firmar con Alice en un nodo local: ```ts -const rpc = "ws://127.0.0.1:37345"; +import { Keyring } from '@polkadot/keyring' +import { cryptoWaitReady } from '@polkadot/util-crypto' + +const rpc = "ws://127.0.0.1:37345"; // ws del nodo local await cryptoWaitReady(); const keyring = new Keyring({ type: "sr25519" }); @@ -68,9 +71,27 @@ const provider = new Provider(rpc, sender); Si quieres firmar con una semilla mnemotécnica ```ts +import { Keyring } from '@polkadot/keyring' + + const sender = keyring.addFromMnemonic(""); ``` +Si quieres firmar con la extensión de polkadotjs +```ts +import { web3FromAddress, web3Accounts, web3Enable } from "@polkadot/extension-dapp"; + +const extensions = await web3Enable(""); +const accounts = await web3Accounts(); +const accountId = accounts[0].address; + +const injector = await web3FromAddress(accountId); + +const provider = new Provider(rpc, accountId); +provider.setSigner(injector.signer); +``` + + ## Metodos soportados Reserve Asset Transfer con los metodos reserveTransferAsset y LimitedReserveTransferAsset y Asset teleportation con los metodos teleportAsset y LimitedTeleportAsset. @@ -96,6 +117,7 @@ provider.reserveTransferAssets(params); destination El destino para transferir el activo. Si desea transferir activos de la cadena 'relay/principal' a una cadena 'parachain', configure 'Parachain'. Predeterminado 'Here'. + destinationParents 0 es el valor predeterminado, 1 cuando desea transferir de parachain a relaychain o de parachain a parachain @@ -107,7 +129,7 @@ provider.reserveTransferAssets(params); beneficiary objetivo del beneficiario, una accountId32 - + beneficiaryParents 0 por defecto @@ -119,6 +141,7 @@ provider.reserveTransferAssets(params); amount cantidad de tokens a transferir + assetId El identificador del asset para transferir desde una parachain, asegúrese de que la parachain admita el activo y que la cuenta del remitente tenga suficientes activos para transferir diff --git a/README.md b/README.md index a365730..18689eb 100644 --- a/README.md +++ b/README.md @@ -56,22 +56,41 @@ const provider = new Provider(rpc, sender) If you want to sign with Alice in a local node: ```ts - const rpc = "ws://127.0.0.1:37345" - await cryptoWaitReady(); +import { Keyring } from '@polkadot/keyring' +import { cryptoWaitReady } from '@polkadot/util-crypto' - const keyring = new Keyring({ type: "sr25519" }); - const sender = keyring.addFromUri("//Alice"); +const rpc = "ws://127.0.0.1:37345" // local node ws +await cryptoWaitReady(); - const provider = new Provider(rpc, sender); +const keyring = new Keyring({ type: "sr25519" }); +const sender = keyring.addFromUri("//Alice"); + +const provider = new Provider(rpc, sender); ``` If you want to sign with mnemonic ```ts - const sender = keyring.addFromMnemonic( - "" - ); +import { Keyring } from '@polkadot/keyring' + +const sender = keyring.addFromMnemonic( +"" +); +``` + +If you want to sign with polkadotjs extension +```ts +import { web3FromAddress, web3Accounts, web3Enable } from "@polkadot/extension-dapp"; + +const extensions = await web3Enable(""); +const accounts = await web3Accounts(); +const accountId = accounts[0].address; + +const injector = await web3FromAddress(accountId); + +const provider = new Provider(rpc, accountId); +provider.setSigner(injector.signer); ``` ## Supported Methods @@ -99,6 +118,7 @@ provider.reserveTransferAssets(params) destination The destination to transfer the asset. If you want to transfer asset from relaychain to a parachain set 'Parachain'. Default 'Here'. + destinationParents 0 is default, 1 when you want to transfer from parachain to relaychain or parachain to parachain @@ -110,7 +130,7 @@ provider.reserveTransferAssets(params) beneficiary beneficary target, an accountId32 - + beneficiaryParents 0 is default @@ -122,6 +142,7 @@ provider.reserveTransferAssets(params) amount token amount to transfer + assetId AssetId to transfer from parachain, make sure the parchain support the asset and the sender account have enough asset to transfer @@ -136,7 +157,7 @@ Depends on the parachain or relay chain configuration you have to use Asset tele ## Rococo examples -If you want to tests in Testnet, you have Rococo. +If you want to tests in Testnet, you have Rococo.
Get some assets: Rococo faucet diff --git a/src/provider.ts b/src/provider.ts index 8ad5e9f..48a6408 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -1,4 +1,5 @@ import { ApiPromise, WsProvider } from '@polkadot/api' +import { Signer } from '@polkadot/types/types' import { AddressOrPair, MultiLocationTypes } from './interfaces/generics' import { TransferAssetsProps, LimitedTransferAssetsProps } from './interfaces/methods' import { getPallet } from './utils' @@ -7,6 +8,7 @@ import { makeXcmVersionedMultiLocation, makeAsssetMultiAsset, formatExtrinsicRes export class Provider { rpc: string signer: AddressOrPair + injectorSigner: Signer | null = null constructor(rpc: string, signer: AddressOrPair) { this.rpc = rpc @@ -14,9 +16,15 @@ export class Provider { } private async getApi() { - return await ApiPromise.create({ + const api = await ApiPromise.create({ provider: new WsProvider(this.rpc), }) + + if (this.injectorSigner) { + api.setSigner(this.injectorSigner) + } + + return api } private prepareExtrinsic(props: LimitedTransferAssetsProps) { @@ -68,6 +76,10 @@ export class Provider { } } + public async setSigner(signer: Signer) { + this.injectorSigner = signer + } + public async reserveTransferAssets(props: TransferAssetsProps) { const api = await this.getApi() diff --git a/src/tests/mocks/provider-mocks.ts b/src/tests/mocks/provider-mocks.ts index 8090474..d2b8056 100644 --- a/src/tests/mocks/provider-mocks.ts +++ b/src/tests/mocks/provider-mocks.ts @@ -66,11 +66,28 @@ export const xcmPalletMock = { }), limitedTeleportAssets: () => ({ signAndSend: (signer: any, cb: any) => { - const status = { isInBlock: true } + const status = { isInBlock: true, isFinalized: true } const txHash = XCM_PALLET_RESPONSES.limitedTeleportAssets const dispatchError = '' const dispatchInfo = {} + return cb({ + status, + txHash, + dispatchError, + dispatchInfo, + }) + }, + }), + limitedTeleportAssetsWithError: () => ({ + signAndSend: (signer: any, cb: any) => { + const status = { isInBlock: true, isFinalized: true } + const txHash = XCM_PALLET_RESPONSES.limitedTeleportAssets + const dispatchError = { + toString: () => 'tx error', + } + const dispatchInfo = '' + return cb({ status, txHash, @@ -82,3 +99,11 @@ export const xcmPalletMock = { } export const RPC_MOCK = 'ws://localhost:32011' + +export const injectorMock = { + signer: { + signPayload: () => null, + signRaw: () => null, + update: () => null, + }, +} diff --git a/src/tests/provider.spec.ts b/src/tests/provider.spec.ts index 81ca50f..3248bc4 100644 --- a/src/tests/provider.spec.ts +++ b/src/tests/provider.spec.ts @@ -3,7 +3,7 @@ import * as Keyring from '@polkadot/keyring' import { assert, expect } from 'chai' import sinon from 'sinon' import { Provider } from '../provider' -import { chainSpecsMock, SIGNER_MOCK, xcmPalletMock, XCM_PALLET_RESPONSES } from './mocks/provider-mocks' +import { chainSpecsMock, injectorMock, SIGNER_MOCK, xcmPalletMock, XCM_PALLET_RESPONSES } from './mocks/provider-mocks' describe('Provider', () => { beforeEach(() => { @@ -26,6 +26,16 @@ describe('Provider', () => { expect(provider.signer).to.equal(SIGNER_MOCK) }) + it('should save injecto signer', () => { + const rpc = chainSpecsMock.rpc + const accountId = '0x12345' + + const provider = new Provider(rpc, accountId) + provider.setSigner(injectorMock.signer as any) + + expect(JSON.stringify(provider.injectorSigner)).to.equal(JSON.stringify(injectorMock.signer)) + }) + describe('limited teleport assets', () => { it('should send teleport asset from relaychain to parachain', async () => { sinon.stub(ApiPromise, 'create').returns({ @@ -58,6 +68,39 @@ describe('Provider', () => { expect(res).to.equal(XCM_PALLET_RESPONSES.limitedTeleportAssets) }) + it('should send teleport asset from relaychain to parachain with injector', async () => { + sinon.stub(ApiPromise, 'create').returns({ + tx: { + xcmPallet: { + limitedTeleportAssets: xcmPalletMock.limitedTeleportAssets, + }, + }, + setSigner: (signer: any) => null, + } as any) + + const sender = '0x12345' + + const rpc = chainSpecsMock.rpc + const destination = 'Parachain' + const destinationValue = chainSpecsMock.parachainId + const beneficiary = 'AccountId32' + const beneficiaryValue = chainSpecsMock.parachainAccount + const amount = 50000000000 + + const provider = new Provider(rpc, sender) + + provider.setSigner(injectorMock.signer as any) + + const res = await provider.limitedTeleportAssets({ + destination, + destinationValue, + beneficiary, + beneficiaryValue, + amount, + }) + expect(res).to.equal(XCM_PALLET_RESPONSES.limitedTeleportAssets) + }) + it('should send teleport asset from parachain to relaychain', async () => { sinon.stub(ApiPromise, 'create').returns({ tx: { @@ -89,6 +132,72 @@ describe('Provider', () => { expect(res).to.equal(XCM_PALLET_RESPONSES.limitedTeleportAssets) }) + it('should send teleport asset from parachain to relaychain account native format', async () => { + sinon.stub(ApiPromise, 'create').returns({ + tx: { + xcmPallet: { + limitedTeleportAssets: xcmPalletMock.limitedTeleportAssets, + }, + }, + } as any) + + const keyring = new Keyring.default({ type: 'sr25519' }) + const sender = keyring.addFromMnemonic(chainSpecsMock.senderMnemonic) + + const rpc = chainSpecsMock.parachainRpc + const destinationParents = 1 + const beneficiary = 'AccountId32' + const beneficiaryValue = 'FtyTjdPJkMFnF9UjQ1g6owwRGsmMGGF11FSZnq84P3yYKRD' + const assetParents = 1 + const amount = 50000000000 + + const provider = new Provider(rpc, sender) + + const res = await provider.limitedTeleportAssets({ + destinationParents, + beneficiary, + beneficiaryValue, + assetParents, + amount, + }) + expect(res).to.equal(XCM_PALLET_RESPONSES.limitedTeleportAssets) + }) + + it('should show error after send tx', async () => { + sinon.stub(ApiPromise, 'create').returns({ + tx: { + xcmPallet: { + limitedTeleportAssets: xcmPalletMock.limitedTeleportAssetsWithError, + }, + }, + } as any) + + const keyring = new Keyring.default({ type: 'sr25519' }) + const sender = keyring.addFromMnemonic(chainSpecsMock.senderMnemonic) + + const rpc = chainSpecsMock.parachainRpc + const destinationParents = 1 + const beneficiary = 'AccountId32' + const beneficiaryValue = 'FtyTjdPJkMFnF9UjQ1g6owwRGsmMGGF11FSZnq84P3yYKRD' + const assetParents = 1 + const amount = 50000000000 + + const provider = new Provider(rpc, sender) + + try { + await provider.limitedTeleportAssets({ + destinationParents, + beneficiary, + beneficiaryValue, + assetParents, + amount, + }) + assert.fail('actual', 'expected', "It shouldn't work ") + } catch (error) { + expect(error).to.equal('tx error') + } + }) + it('should show error', async () => { sinon.stub(ApiPromise, 'create').returns({ tx: { diff --git a/src/utils.ts b/src/utils.ts index 3b75a66..24e0fd4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -21,7 +21,7 @@ export const makeXcmVersionedMultiLocation = ({ if (target === 'AccountId32') { const account = String(value) - const isHex = account?.startsWith('0x') + const isHex = account.startsWith('0x') const accountId = isHex ? value : u8aToHex(decodeAddress(account)) @@ -82,14 +82,7 @@ export const makeAsssetMultiAsset = ({ export const formatExtrinsicResponse = ({ api, res, rej, status, txHash, dispatchError, dispatchInfo }: any) => { if (status.isInBlock || status.isFinalized) { if (dispatchError) { - if (dispatchError.isModule) { - const decoded = api.registry.findMetaError(dispatchError.asModule) - const { docs, name, section } = decoded - - rej(`${section}.${name}: ${docs.join(' ')}`) - } else { - rej(dispatchError.toString()) - } + rej(dispatchError.toString()) } else if (dispatchInfo) { res(txHash.toString()) } @@ -97,7 +90,9 @@ export const formatExtrinsicResponse = ({ api, res, rej, status, txHash, dispatc } export const getPallet = (api: ApiPromise) => { - if (!api.tx?.xcmPallet && !api.tx.polkadotXcm) { + const palletIsIncluded = Object.keys(api.tx).some((p) => ['xcmPallet', 'polkadotXcm'].includes(p)) + + if (!palletIsIncluded) { throw new Error('xcmPallet or polkadotXcm unsupported') }