diff --git a/README.md b/README.md index 7c51ad12..65746db0 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,11 @@ interface TransferArgsOpts { * Optional assetId that will be used to pay for fees. Used with the `dryRunCall` option to determine fees in the specified asset. */ xcmFeeAsset?: string; + + /** + * Optionally sets the pallet to be used for the current tx. + */ + xcmPalletOverride?: XcmPallet; } ``` diff --git a/src/AssetTransferApi.spec.ts b/src/AssetTransferApi.spec.ts index 326964e9..562b9f02 100644 --- a/src/AssetTransferApi.spec.ts +++ b/src/AssetTransferApi.spec.ts @@ -583,6 +583,7 @@ describe('AssetTransferAPI', () => { { format: 'call', keepAlive: false, + xcmPalletOverride: 'xTokens', }, ); @@ -604,6 +605,7 @@ describe('AssetTransferAPI', () => { format: 'payload', keepAlive: false, sendersAddr: 'FBeL7DanUDs5SZrxZY1CizMaPgG9vZgJgvr52C2dg81SsF1', + xcmPalletOverride: 'xTokens', }, ); @@ -622,6 +624,7 @@ describe('AssetTransferAPI', () => { ['10000000000000'], { format: 'submittable', + xcmPalletOverride: 'xTokens', }, ); diff --git a/src/AssetTransferApi.ts b/src/AssetTransferApi.ts index 07e34566..20dc43aa 100644 --- a/src/AssetTransferApi.ts +++ b/src/AssetTransferApi.ts @@ -202,6 +202,7 @@ export class AssetTransferApi { customXcmOnDest, dryRunCall, xcmFeeAsset, + xcmPalletOverride, } = opts; if (!this.registryConfig.registryInitialized) { @@ -242,7 +243,7 @@ export class AssetTransferApi { const xcmDirection = this.establishDirection(isLocalTx, chainOriginDestInfo); const isForeignAssetsTransfer = await this.checkContainsForeignAssets(api, assetIds); const isPrimaryParachainNativeAsset = isParachainPrimaryNativeAsset(registry, specName, xcmDirection, assetIds[0]); - const xcmPallet = establishXcmPallet(api, xcmDirection); + const xcmPallet = establishXcmPallet(api, xcmDirection, xcmPalletOverride); const declaredXcmVersion = xcmVersion === undefined ? safeXcmVersion : xcmVersion; checkXcmVersion(declaredXcmVersion); // Throws an error when the xcmVersion is not supported. diff --git a/src/config/disabledOpts.ts b/src/config/disabledOpts.ts index b94de532..c403834a 100644 --- a/src/config/disabledOpts.ts +++ b/src/config/disabledOpts.ts @@ -100,4 +100,9 @@ export const disabledOpts: DisabledOptions = { chains: [], error: (opt: string, chain: string) => callError(opt, chain), }, + xcmPalletOverride: { + disabled: false, + chains: [], + error: (opt: string, chain: string) => callError(opt, chain), + }, }; diff --git a/src/createXcmCalls/util/establishXcmPallet.spec.ts b/src/createXcmCalls/util/establishXcmPallet.spec.ts index b717ccda..3ce74b80 100644 --- a/src/createXcmCalls/util/establishXcmPallet.spec.ts +++ b/src/createXcmCalls/util/establishXcmPallet.spec.ts @@ -13,4 +13,9 @@ describe('establishXcmPallet', () => { const res = establishXcmPallet(mockSystemApi); expect(res).toEqual('polkadotXcm'); }); + it('Should correctly throw an error when an overrided pallet is not found for the given runtime', () => { + const xcmPalletOverride = 'xTokens'; + const err = () => establishXcmPallet(mockSystemApi, undefined, xcmPalletOverride); + expect(err).toThrow('Pallet xTokens not found in the current runtime.'); + }); }); diff --git a/src/createXcmCalls/util/establishXcmPallet.ts b/src/createXcmCalls/util/establishXcmPallet.ts index 7d1bcf4b..b0f9bb7b 100644 --- a/src/createXcmCalls/util/establishXcmPallet.ts +++ b/src/createXcmCalls/util/establishXcmPallet.ts @@ -4,7 +4,7 @@ import type { ApiPromise } from '@polkadot/api'; import { SUPPORTED_XCM_PALLETS } from '../../consts'; import { BaseError, BaseErrorsEnum } from '../../errors'; -import { Direction } from '../../types'; +import { Direction, XcmPallet } from '../../types'; export enum XcmPalletName { xcmPallet = 'xcmPallet', @@ -20,7 +20,28 @@ export enum XcmPalletName { * * @param api ApiPromise */ -export const establishXcmPallet = (api: ApiPromise, direction?: Direction): XcmPalletName => { +export const establishXcmPallet = ( + api: ApiPromise, + direction?: Direction, + xcmPalletOverride?: XcmPallet, +): XcmPalletName => { + if (xcmPalletOverride) { + if (api.tx[xcmPalletOverride]) { + return XcmPalletName[xcmPalletOverride]; + } else { + throw new BaseError( + `Pallet ${xcmPalletOverride} not found in the current runtime.`, + BaseErrorsEnum.PalletNotFound, + ); + } + } + + if (api.tx.polkadotXcm) { + return XcmPalletName.polkadotXcm; + } else if (api.tx.xcmPallet) { + return XcmPalletName.xcmPallet; + } + let xPallet: XcmPalletName | undefined; if (api.tx.xTokens) { @@ -33,12 +54,6 @@ export const establishXcmPallet = (api: ApiPromise, direction?: Direction): XcmP return xPallet as XcmPalletName; } - if (api.tx.polkadotXcm) { - return XcmPalletName.polkadotXcm; - } else if (api.tx.xcmPallet) { - return XcmPalletName.xcmPallet; - } - const supportedPallets = SUPPORTED_XCM_PALLETS.map((pallet) => { return pallet; }).join(', '); diff --git a/src/integrationTests/parachains/bifrost.spec.ts b/src/integrationTests/parachains/bifrost.spec.ts index f2432dbc..9e19f353 100644 --- a/src/integrationTests/parachains/bifrost.spec.ts +++ b/src/integrationTests/parachains/bifrost.spec.ts @@ -9,12 +9,143 @@ import type { TestMultiasset, TestMultiassets, TestMultiassetWithFormat } from ' import { paraTransferMultiasset as bifrostTransferMultiasset } from '../util'; import { paraTransferMultiassets as bifrostTransferMultiassets } from '../util'; import { paraTransferMultiassetWithFee as bifrostTransferMultiassetWithFee } from '../util'; -import { paraTeleportNativeAsset as bifrsotTeleportNativeAsset } from '../util'; +import { paraTransferAssets as bifrostTransferAssets } from '../util'; const bifrostATA = new AssetTransferApi(adjustedMockBifrostParachainApi, 'bifrost', 2, { registryType: 'NPM' }); describe('Bifrost', () => { describe('ParaToPara', () => { + describe('transferAssets', () => { + describe('XCM V2', () => { + it('Should correctly construct a transferAssets call from Bifrost to Moonriver', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '2023', + 'vKSM', + 'call', + '0x290b010101009d1f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400010200451f0608010400025a62020000000000', + ], + [ + '2023', + 'vBNC', + 'payload', + '0x0d01290b010101009d1f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400010200451f0608010100025a620200000000004502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await bifrostTransferAssets( + bifrostATA, + format as Format, + 2, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V2 transferAssets submittable', async () => { + const res = await bifrostTransferAssets(bifrostATA, 'submittable', 2, '2023', ['vKSM'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + describe('XCM V3', () => { + it('Should correctly construct a transferAssets call from Bifrost to Moonriver', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '2023', + 'movr', + 'call', + '0x290b030101009d1f0300010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b0304000102009d1f040a00025a62020000000000', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await bifrostTransferAssets( + bifrostATA, + format as Format, + 3, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V3 transferAssets submittable from Bifrost to Moonriver', async () => { + const res = await bifrostTransferAssets(bifrostATA, 'submittable', 3, '2023', ['BNC'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + describe('XCM V4', () => { + it('Should correctly construct a transferAssets call from Bifrost to Moonriver', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '2023', + 'movr', + 'call', + '0x290b040101009d1f0400010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b04040102009d1f040a00025a62020000000000', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await bifrostTransferAssets( + bifrostATA, + format as Format, + 4, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V4 transferAssets submittable from Bifrost to Moonriver', async () => { + const res = await bifrostTransferAssets(bifrostATA, 'submittable', 4, '2023', ['BNC'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + }); describe('transferMultiasset', () => { describe('XCM V2', () => { it('Should correctly build xTokens transferMultiasset txs from Bifrost Kusama', async () => { @@ -321,16 +452,16 @@ describe('Bifrost', () => { 'ksm', '0xe8460101000100000700e40b540201010200a10f0100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b01a10f411f4502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', ], - // [ - // '1000', - // 'rmrk', - // '0x050146010100010300a10f04320520000700e40b540201010200a10f0100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b01a10f411f4502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', - // ], - // [ - // '1000', - // 'usdt', - // '0x090146010100010300a10f043205011f000700e40b540201010200a10f0100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b01a10f411f4502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', - // ], + [ + '1000', + 'rmrk', + '0x050146010100010300a10f04320520000700e40b540201010200a10f0100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b01a10f411f4502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', + ], + [ + '1000', + 'usdt', + '0x090146010100010300a10f043205011f000700e40b540201010200a10f0100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b01a10f411f4502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', + ], ]; for (const test of tests) { @@ -534,34 +665,38 @@ describe('Bifrost', () => { }); }); }); - describe('limitedTeleportAssets', () => { + describe('transferAssets', () => { describe('XCM V2', () => { - it('Should correctly construct a limitedTeleportAssets call when sending Bifrosts primary native asset', async () => { + it('Should correctly construct a transferAssets call from Bifrost to AssetHub', async () => { const tests: TestMultiassetWithFormat[] = [ [ '1000', - 'BNC', + 'vKSM', 'call', - '0x460101000000000700e40b540201010200a10f01007369626c2708000000000000000000000000000000000000000000000000000001a10f411f', + '0x290b01010100a10f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400010200451f0608010400025a62020000000000', ], [ '1000', - 'BNC', + 'vBNC', 'payload', - '0xe8460101000000000700e40b540201010200a10f01007369626c2708000000000000000000000000000000000000000000000000000001a10f411f4502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', + '0x0d01290b01010100a10f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400010200451f0608010100025a620200000000004502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', ], ]; for (const test of tests) { - const [, assetId, format, expectedResult] = test; - const res = await bifrsotTeleportNativeAsset(bifrostATA, format as Format, assetId, 2, { - weightLimit: { - refTime: '1000', - proofSize: '2000', + const [destChainId, assetId, format, expectedResult] = test; + const res = await bifrostTransferAssets( + bifrostATA, + format as Format, + 2, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, }, - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); + ); if (format === 'call') { expect(res.tx).toEqual(expectedResult); @@ -570,12 +705,8 @@ describe('Bifrost', () => { } } }); - it('Should correctly build a V2 limitedTeleportAssets submittable containing the native parachain asset', async () => { - const res = await bifrsotTeleportNativeAsset(bifrostATA, 'submittable', 'BNC', 2, { - weightLimit: { - refTime: '1000', - proofSize: '2000', - }, + it('Should correctly build a V2 transferAssets submittable', async () => { + const res = await bifrostTransferAssets(bifrostATA, 'submittable', 2, '1000', ['vKSM'], ['10000000'], { isForeignAssetsTransfer: false, isLiquidTokenTransfer: false, }); @@ -583,32 +714,36 @@ describe('Bifrost', () => { }); }); describe('XCM V3', () => { - it('Should correctly construct a limitedTeleportAssets call when sending Bifrosts primary native asset', async () => { + it('Should correctly construct a transferAssets call from Bifrost to AssetHub', async () => { const tests: TestMultiassetWithFormat[] = [ [ '1000', - 'BNC', + 'USDT', 'call', - '0x460103000000000700e40b540203010200a10f01007369626c2708000000000000000000000000000000000000000000000000000001a10f411f', + '0x290b03010100a10f0300010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b030400010300a10f043205011f00025a62020000000000', ], [ '1000', - 'BNC', + 'RMRK', 'payload', - '0xe8460103000000000700e40b540203010200a10f01007369626c2708000000000000000000000000000000000000000000000000000001a10f411f4502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', + '0x0d01290b03010100a10f0300010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b030400010300a10f0432052000025a620200000000004502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', ], ]; for (const test of tests) { - const [, assetId, format, expectedResult] = test; - const res = await bifrsotTeleportNativeAsset(bifrostATA, format as Format, assetId, 3, { - weightLimit: { - refTime: '1000', - proofSize: '2000', + const [destChainId, assetId, format, expectedResult] = test; + const res = await bifrostTransferAssets( + bifrostATA, + format as Format, + 3, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, }, - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); + ); if (format === 'call') { expect(res.tx).toEqual(expectedResult); @@ -617,82 +752,39 @@ describe('Bifrost', () => { } } }); - it('Should correctly build a V3 limitedTeleportAssets submittable containing the native parachain asset', async () => { - const res = await bifrsotTeleportNativeAsset(bifrostATA, 'submittable', 'BNC', 3, { - weightLimit: { - refTime: '1000', - proofSize: '2000', - }, + it('Should correctly build a V3 transferAssets submittable from Bifrost to AssetHub', async () => { + const res = await bifrostTransferAssets(bifrostATA, 'submittable', 3, '1000', ['KSM'], ['10000000'], { isForeignAssetsTransfer: false, isLiquidTokenTransfer: false, }); expect(res.tx.toRawType()).toEqual('Extrinsic'); }); }); - }); - describe('teleportAssets', () => { - describe('XCM V2', () => { - it('Should correctly construct a teleportAssets call when sending Bifrosts primary native asset', async () => { + describe('XCM V4', () => { + it('Should correctly construct a transferAssets call from Bifrost to AssetHub', async () => { const tests: TestMultiassetWithFormat[] = [ [ '1000', - 'BNC', - 'call', - '0x460101000000000700e40b540201010200a10f01007369626c2708000000000000000000000000000000000000000000000000000000', - ], - [ - '1000', - 'BNC', - 'payload', - '0xd8460101000000000700e40b540201010200a10f01007369626c27080000000000000000000000000000000000000000000000000000004502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', - ], - ]; - - for (const test of tests) { - const [, assetId, format, expectedResult] = test; - const res = await bifrsotTeleportNativeAsset(bifrostATA, format as Format, assetId, 2, { - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - - if (format === 'call') { - expect(res.tx).toEqual(expectedResult); - } else { - expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); - } - } - }); - it('Should correctly build a V2 teleportAssets submittable containing the native parachain asset', async () => { - const res = await bifrsotTeleportNativeAsset(bifrostATA, 'submittable', 'BNC', 2, { - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - expect(res.tx.toRawType()).toEqual('Extrinsic'); - }); - }); - describe('XCM V3', () => { - it('Should correctly construct a teleportAssets call when sending Bifrosts primary native asset', async () => { - const tests: TestMultiassetWithFormat[] = [ - [ - '1000', - 'BNC', + 'bnc', 'call', - '0x460103000000000700e40b540203010200a10f01007369626c2708000000000000000000000000000000000000000000000000000000', - ], - [ - '1000', - 'BNC', - 'payload', - '0xd8460103000000000700e40b540203010200a10f01007369626c27080000000000000000000000000000000000000000000000000000004502280000fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d450300', + '0x290b04010100a10f0400010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b0404000000025a62020000000000', ], ]; for (const test of tests) { - const [, assetId, format, expectedResult] = test; - const res = await bifrsotTeleportNativeAsset(bifrostATA, format as Format, assetId, 3, { - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); + const [destChainId, assetId, format, expectedResult] = test; + const res = await bifrostTransferAssets( + bifrostATA, + format as Format, + 4, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); if (format === 'call') { expect(res.tx).toEqual(expectedResult); @@ -701,8 +793,8 @@ describe('Bifrost', () => { } } }); - it('Should correctly build a V3 teleportAssets submittable containing the native parachain asset', async () => { - const res = await bifrsotTeleportNativeAsset(bifrostATA, 'submittable', 'BNC', 3, { + it('Should correctly build a V3 transferAssets submittable from Bifrost to Moonriver', async () => { + const res = await bifrostTransferAssets(bifrostATA, 'submittable', 4, '1000', ['BNC'], ['10000000'], { isForeignAssetsTransfer: false, isLiquidTokenTransfer: false, }); diff --git a/src/integrationTests/parachains/moonriver.spec.ts b/src/integrationTests/parachains/moonriver.spec.ts index 0c329a64..019568ea 100644 --- a/src/integrationTests/parachains/moonriver.spec.ts +++ b/src/integrationTests/parachains/moonriver.spec.ts @@ -9,12 +9,101 @@ import type { TestMultiassetsWithFormat, TestMultiassetWithFormat } from '../uti import { paraTransferMultiasset as moonriverTransferMultiasset } from '../util'; import { paraTransferMultiassets as moonriverTransferMultiassets } from '../util'; import { paraTransferMultiassetWithFee as moonriverTransferMultiassetWithFee } from '../util'; -import { paraTeleportNativeAsset as moonriverTeleportNativeAsset } from '../util'; - +import { paraTransferAssets as moonriverTransferAssets } from '../util'; const moonriverATA = new AssetTransferApi(adjustedMockMoonriverParachainApi, 'moonriver', 2, { registryType: 'NPM' }); describe('Moonriver', () => { describe('ParaToPara', () => { + describe('limitedReserveTransferAssets', () => { + describe('XCM V2', () => { + it('Should correctly construct a limitedReserveTransferAssets call from Moonriver to Bifrost', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '2001', + '319623561105283008236062145480775032445', // xcBNC + 'call', + '0x670801010100451f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400010200451f0608000100025a62020000000000', + ], + [ + '2001', + 'vBNC', + 'payload', + '0x0d01670801010100451f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400010200451f0608010100025a6202000000000045022800fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d4503', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await moonriverTransferAssets( + moonriverATA, + format as Format, + 2, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V2 limitedReserveTransferAssets submittable', async () => { + const res = await moonriverTransferAssets(moonriverATA, 'submittable', 2, '2001', ['vKSM'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + describe('XCM V3', () => { + it('Should correctly construct a limitedReserveTransferAssets call from Bifrost to Moonriver', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '2001', + 'movr', + 'call', + '0x670803010100451f0300010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b030400000000025a62020000000000', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await moonriverTransferAssets( + moonriverATA, + format as Format, + 3, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V3 limitedReserveTransferAssets submittable from Moonriver to Bifrost', async () => { + const res = await moonriverTransferAssets(moonriverATA, 'submittable', 3, '2001', ['MOVR'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + }); describe('transferMultiasset', () => { describe('XCM V2', () => { it('Should correctly build xTokens transferMultiasset txs from Moonriver', async () => { @@ -373,6 +462,180 @@ describe('Moonriver', () => { }); }); describe('ParaToSystem', () => { + describe('limitedTeleportAssets', () => { + describe('XCM V2', () => { + it('Should correctly construct a limitedTeleportAssets call from Moonriver to AssetHub', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '1000', + 'movr', + 'call', + '0x670901010100a10f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400000000025a62020000000000', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await moonriverTransferAssets( + moonriverATA, + format as Format, + 2, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V2 limitedTeleportAssets submittable', async () => { + const res = await moonriverTransferAssets(moonriverATA, 'submittable', 2, '1000', ['movr'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + describe('XCM V3', () => { + it('Should correctly construct a limitedTeleportAssets call from Moonriver to AssetHub', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '1000', + 'movr', + 'call', + '0x670903010100a10f0300010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b030400000000025a62020000000000', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await moonriverTransferAssets( + moonriverATA, + format as Format, + 3, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V3 limitedTeleportAssets submittable', async () => { + const res = await moonriverTransferAssets(moonriverATA, 'submittable', 3, '1000', ['movr'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + }); + describe('limitedReserveTransferAssets', () => { + describe('XCM V2', () => { + it('Should correctly construct a limitedReserveTransferAssets call from Moonriver to AssetHub', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '1000', + 'xcKSM', + 'call', + '0x670801010100a10f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400010000025a62020000000000', + ], + [ + '1000', + 'xcUSDT', + 'payload', + '0x1101670801010100a10f0100010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b010400010300a10f043205011f00025a6202000000000045022800fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d4503', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await moonriverTransferAssets( + moonriverATA, + format as Format, + 2, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V2 limitedReserveTransferAssets submittable', async () => { + const res = await moonriverTransferAssets(moonriverATA, 'submittable', 2, '1000', ['ksm'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + describe('XCM V3', () => { + it('Should correctly construct a limitedReserveTransferAssets call from Moonriver to AssetHub', async () => { + const tests: TestMultiassetWithFormat[] = [ + [ + '1000', + 'ksm', + 'call', + '0x670803010100a10f0300010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b030400010000025a62020000000000', + ], + ]; + + for (const test of tests) { + const [destChainId, assetId, format, expectedResult] = test; + const res = await moonriverTransferAssets( + moonriverATA, + format as Format, + 3, + destChainId, + [assetId], + ['10000000'], + { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }, + ); + + if (format === 'call') { + expect(res.tx).toEqual(expectedResult); + } else { + expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); + } + } + }); + it('Should correctly build a V3 limitedReserveTransferAssets submittable from Moonriver to AssetHub', async () => { + const res = await moonriverTransferAssets(moonriverATA, 'submittable', 3, '1000', ['xcUsdt'], ['10000000'], { + isForeignAssetsTransfer: false, + isLiquidTokenTransfer: false, + }); + expect(res.tx.toRawType()).toEqual('Extrinsic'); + }); + }); + }); describe('transferMultiasset', () => { describe('XCM V2', () => { it('Should correctly build xTokens transferMultiasset txs from Moonriver', async () => { @@ -645,182 +908,6 @@ describe('Moonriver', () => { }); }); }); - describe('limitedTeleportAssets', () => { - describe('XCM V2', () => { - it('Should correctly construct a limitedTeleportAssets call when sending Moonrivers primary native asset', async () => { - const tests: TestMultiassetWithFormat[] = [ - [ - '1000', - 'MOVR', - 'call', - '0x6a0101000000000700e40b540201010200a10f01007369626c2708000000000000000000000000000000000000000000000000000001a10f411f', - ], - [ - '1000', - 'MOVR', - 'payload', - '0xe86a0101000000000700e40b540201010200a10f01007369626c2708000000000000000000000000000000000000000000000000000001a10f411f45022800fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d4503', - ], - ]; - - for (const test of tests) { - const [, assetId, format, expectedResult] = test; - const res = await moonriverTeleportNativeAsset(moonriverATA, format as Format, assetId, 2, { - weightLimit: { - refTime: '1000', - proofSize: '2000', - }, - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - - if (format === 'call') { - expect(res.tx).toEqual(expectedResult); - } else { - expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); - } - } - }); - it('Should correctly build a V2 limitedTeleportAssets submittable containing the native parachain asset', async () => { - const res = await moonriverTeleportNativeAsset(moonriverATA, 'submittable', 'MOVR', 2, { - weightLimit: { - refTime: '1000', - proofSize: '2000', - }, - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - expect(res.tx.toRawType()).toEqual('Extrinsic'); - }); - }); - describe('XCM V3', () => { - it('Should correctly construct a limitedTeleportAssets call when sending Moonrivers primary native asset', async () => { - const tests: TestMultiassetWithFormat[] = [ - [ - '1000', - 'MOVR', - 'call', - '0x6a0103000000000700e40b540203010200a10f01007369626c2708000000000000000000000000000000000000000000000000000001a10f411f', - ], - [ - '1000', - 'MOVR', - 'payload', - '0xe86a0103000000000700e40b540203010200a10f01007369626c2708000000000000000000000000000000000000000000000000000001a10f411f45022800fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d4503', - ], - ]; - - for (const test of tests) { - const [, assetId, format, expectedResult] = test; - const res = await moonriverTeleportNativeAsset(moonriverATA, format as Format, assetId, 3, { - weightLimit: { - refTime: '1000', - proofSize: '2000', - }, - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - - if (format === 'call') { - expect(res.tx).toEqual(expectedResult); - } else { - expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); - } - } - }); - it('Should correctly build a V3 limitedTeleportAssets submittable containing the native parachain asset', async () => { - const res = await moonriverTeleportNativeAsset(moonriverATA, 'submittable', 'MOVR', 3, { - weightLimit: { - refTime: '1000', - proofSize: '2000', - }, - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - expect(res.tx.toRawType()).toEqual('Extrinsic'); - }); - }); - }); - describe('teleportAssets', () => { - describe('XCM V2', () => { - it('Should correctly construct a teleportAssets call when sending Moonrivers primary native asset', async () => { - const tests: TestMultiassetWithFormat[] = [ - [ - '1000', - 'MOVR', - 'call', - '0x6a0101000000000700e40b540201010200a10f01007369626c2708000000000000000000000000000000000000000000000000000000', - ], - [ - '1000', - 'MOVR', - 'payload', - '0xd86a0101000000000700e40b540201010200a10f01007369626c270800000000000000000000000000000000000000000000000000000045022800fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d4503', - ], - ]; - - for (const test of tests) { - const [, assetId, format, expectedResult] = test; - const res = await moonriverTeleportNativeAsset(moonriverATA, format as Format, assetId, 2, { - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - - if (format === 'call') { - expect(res.tx).toEqual(expectedResult); - } else { - expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); - } - } - }); - it('Should correctly build a V2 teleportAssets submittable containing the native parachain asset', async () => { - const res = await moonriverTeleportNativeAsset(moonriverATA, 'submittable', 'MOVR', 2, { - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - expect(res.tx.toRawType()).toEqual('Extrinsic'); - }); - }); - describe('XCM V3', () => { - it('Should correctly construct a teleportAssets call when sending Moonrivers primary native asset', async () => { - const tests: TestMultiassetWithFormat[] = [ - [ - '1000', - 'MOVR', - 'call', - '0x6a0103000000000700e40b540203010200a10f01007369626c2708000000000000000000000000000000000000000000000000000000', - ], - [ - '1000', - 'MOVR', - 'payload', - '0xd86a0103000000000700e40b540203010200a10f01007369626c270800000000000000000000000000000000000000000000000000000045022800fe080000040000000000000000000000000000000000000000000000000000000000000000000000be2554aa8a0151eb4d706308c47d16996af391e4c5e499c7cbef24259b7d4503', - ], - ]; - - for (const test of tests) { - const [, assetId, format, expectedResult] = test; - const res = await moonriverTeleportNativeAsset(moonriverATA, format as Format, assetId, 3, { - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - - if (format === 'call') { - expect(res.tx).toEqual(expectedResult); - } else { - expect((res.tx as GenericExtrinsicPayload).toHex()).toEqual(expectedResult); - } - } - }); - it('Should correctly build a V3 teleportAssets submittable containing the native parachain asset', async () => { - const res = await moonriverTeleportNativeAsset(moonriverATA, 'submittable', 'MOVR', 3, { - isForeignAssetsTransfer: false, - isLiquidTokenTransfer: false, - }); - expect(res.tx.toRawType()).toEqual('Extrinsic'); - }); - }); - }); }); describe('ParaToRelay', () => { describe('transferMultiasset', () => { diff --git a/src/integrationTests/util.ts b/src/integrationTests/util.ts index 115e8406..5e5d3609 100644 --- a/src/integrationTests/util.ts +++ b/src/integrationTests/util.ts @@ -16,6 +16,29 @@ export type TestMultiassetsWithFormat = [ expected: `0x${string}`, ]; +export const paraTransferAssets = async ( + parachainATA: AssetTransferApi, + format: T, + xcmVersion: number, + destChainId: string, + assetIds: string[], + amounts: string[], + opts: CreateXcmCallOpts, +): Promise> => { + return await parachainATA.createTransferTransaction( + destChainId, + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + assetIds, + amounts, + { + format, + xcmVersion, + weightLimit: opts.weightLimit, + sendersAddr: 'FBeL7DanUDs5SZrxZY1CizMaPgG9vZgJgvr52C2dg81SsF1', + }, + ); +}; + export const paraTransferMultiasset = async ( parachainATA: AssetTransferApi, format: T, @@ -34,6 +57,7 @@ export const paraTransferMultiasset = async ( xcmVersion, weightLimit: opts.weightLimit, sendersAddr: 'FBeL7DanUDs5SZrxZY1CizMaPgG9vZgJgvr52C2dg81SsF1', + xcmPalletOverride: 'xTokens', }, ); }; @@ -58,6 +82,7 @@ export const paraTransferMultiassets = async ( xcmVersion, weightLimit: opts.weightLimit, sendersAddr: 'FBeL7DanUDs5SZrxZY1CizMaPgG9vZgJgvr52C2dg81SsF1', + xcmPalletOverride: 'xTokens', }, ); }; @@ -82,27 +107,7 @@ export const paraTransferMultiassetWithFee = async ( xcmVersion, weightLimit: opts.weightLimit, sendersAddr: 'FBeL7DanUDs5SZrxZY1CizMaPgG9vZgJgvr52C2dg81SsF1', - }, - ); -}; - -export const paraTeleportNativeAsset = async ( - parachainATA: AssetTransferApi, - format: T, - nativeAssetId: string, - xcmVersion: number, - opts: CreateXcmCallOpts, -): Promise> => { - return await parachainATA.createTransferTransaction( - '1000', // `1000` indicating the dest chain is a system chain. - 'FBeL7DanUDs5SZrxZY1CizMaPgG9vZgJgvr52C2dg81SsF1', - [nativeAssetId], - ['10000000000'], - { - format, - xcmVersion, - weightLimit: opts.weightLimit, - sendersAddr: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + xcmPalletOverride: 'xTokens', }, ); }; diff --git a/src/testHelpers/adjustedMockBifrostParachainApi.ts b/src/testHelpers/adjustedMockBifrostParachainApi.ts index 6df67a9e..abdb8c6f 100644 --- a/src/testHelpers/adjustedMockBifrostParachainApi.ts +++ b/src/testHelpers/adjustedMockBifrostParachainApi.ts @@ -74,6 +74,7 @@ export const adjustedMockBifrostParachainApi = { reserveTransferAssets: mockBifrostParachainApi.tx['polkadotXcm'].reserveTransferAssets, teleportAssets: mockBifrostParachainApi.tx['polkadotXcm'].teleportAssets, limitedTeleportAssets: mockBifrostParachainApi.tx['polkadotXcm'].limitedTeleportAssets, + transferAssets: mockBifrostParachainApi.tx['polkadotXcm'].transferAssets, }, xTokens: { transferMultiasset: mockBifrostParachainApi.tx['xTokens'].transferMultiasset, diff --git a/src/types.ts b/src/types.ts index bde1cb8d..e2f6e59e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -360,8 +360,15 @@ export interface TransferArgsOpts { * Optional assetId that will be used to pay for fees. Used with the `dryRunCall` option to determine fees in the specified asset. */ xcmFeeAsset?: string; + + /** + * Optionally sets the pallet to be used for the current tx. + */ + xcmPalletOverride?: XcmPallet; } +export type XcmPallet = 'xcmPallet' | 'polkadotXcm' | 'xtokens' | 'xTokens'; + export interface ChainInfo { specName: string; specVersion: string;