Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement transfer all #434

Merged
merged 4 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ interface TransferArgsOpts<T extends Format> {
* to a `transfer`.
*/
keepAlive?: boolean;
/**
* For creating local asset transfers, this will allow for a `transferAll` as opposed
* to a `transfer`.
*/
transferAll?: boolean;
/**
* Boolean to declare if this will transfer liquidity tokens.
* Default is false.
Expand Down
4 changes: 2 additions & 2 deletions src/AssetTransferApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { adjustedMockMoonriverNoXTokensParachainApi } from './testHelpers/adjust
import { adjustedMockRelayApiNoLimitedReserveTransferAssets } from './testHelpers/adjustedMockRelayApiNoLimitedReserveTransferAssets';
import { adjustedMockRelayApi } from './testHelpers/adjustedMockRelayApiV9420';
import { adjustedMockSystemApi } from './testHelpers/adjustedMockSystemApiV1004000';
import { adjustedMockSystemApiV1014000 } from './testHelpers/adjustedMockSystemApiV1014000';
import { adjustedMockSystemApiV1016000 } from './testHelpers/adjustedMockSystemApiV1016000';
import { mockSystemApi } from './testHelpers/mockSystemApi';
import { mockWeightInfo } from './testHelpers/mockWeightInfo';
import { AssetCallType, Direction, ResolvedCallInfo, UnsignedTransaction, XcmBaseArgs, XcmDirection } from './types';
Expand Down Expand Up @@ -50,7 +50,7 @@ const bifrostAssetsApi = new AssetTransferApi(adjustedMockBifrostParachainApi, '
const moonriverAssetsNoXTokensApi = new AssetTransferApi(adjustedMockMoonriverNoXTokensParachainApi, 'moonriver', 2, {
registryType: 'NPM',
});
const westmintAssetsApi = new AssetTransferApi(adjustedMockSystemApiV1014000, 'westmint', 4, {
const westmintAssetsApi = new AssetTransferApi(adjustedMockSystemApiV1016000, 'westmint', 4, {
registryType: 'NPM',
});

Expand Down
100 changes: 67 additions & 33 deletions src/AssetTransferApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
ConstructedFormat,
Direction,
Format,
LocalMethodName,
LocalTransferTypes,
LocalTxChainType,
LocalTxOpts,
Expand All @@ -82,6 +83,7 @@ import {
} from './types';
import { callExistsInRuntime } from './util/callExistsInRuntime';
import { deepEqual } from './util/deepEqual';
import { resolveMultiLocation } from './util/resolveMultiLocation';
import { sanitizeKeys } from './util/sanitizeKeys';
import { validateNumber } from './validate';

Expand Down Expand Up @@ -982,6 +984,8 @@ export class AssetTransferApi {
opts: LocalTxOpts,
) {
const { api, specName } = this;
const { isForeignAssetsTransfer, isLiquidTokenTransfer, keepAlive, transferAll } = opts;
const transferKeepAlive = keepAlive ? keepAlive : false;
let assetId = assetIds[0];
const amount = amounts[0];
const isValidNumber = validateNumber(assetId);
Expand All @@ -1003,7 +1007,15 @@ export class AssetTransferApi {
opts.isForeignAssetsTransfer,
);
}
const method = opts.keepAlive ? 'transferKeepAlive' : 'transfer';
let method: LocalMethodName;
if (transferAll) {
method = 'transferAll';
} else if (keepAlive) {
method = 'transferKeepAlive';
} else {
method = 'transfer';
}
let tx: SubmittableExtrinsic<'promise', ISubmittableResult> | undefined;

if (localTxChainType === LocalTxChainType.System) {
const localAssetType = await checkLocalSystemParachainInput(
Expand All @@ -1013,36 +1025,49 @@ export class AssetTransferApi {
this.specName,
this.registry,
declaredXcmVersion,
opts.isForeignAssetsTransfer,
opts.isLiquidTokenTransfer,
isForeignAssetsTransfer,
isLiquidTokenTransfer,
); // Throws an error when any of the inputs are incorrect.
let tx: SubmittableExtrinsic<'promise', ISubmittableResult> | undefined;
let palletMethod: LocalTransferTypes | undefined;

if (localAssetType === LocalTxType.Balances) {
tx =
method === 'transferKeepAlive'
? balances.transferKeepAlive(api, addr, amount)
: balances.transfer(api, addr, amount);
if (method === 'transferKeepAlive') {
tx = balances.transferKeepAlive(api, addr, amount);
} else if (method === 'transferAll') {
tx = balances.transferAll(api, addr, transferKeepAlive);
} else {
tx = balances.transfer(api, addr, amount);
}
palletMethod = `balances::${method}`;
} else if (localAssetType === LocalTxType.Assets) {
tx =
method === 'transferKeepAlive'
? assets.transferKeepAlive(api, addr, assetId, amount)
: assets.transfer(api, addr, assetId, amount);
if (method === 'transferKeepAlive') {
tx = assets.transferKeepAlive(api, addr, assetId, amount);
} else if (method === 'transferAll') {
tx = assets.transferAll(api, assetId, addr, transferKeepAlive);
} else {
tx = assets.transfer(api, addr, assetId, amount);
}
palletMethod = `assets::${method}`;
} else if (localAssetType === LocalTxType.PoolAssets) {
tx =
method === 'transferKeepAlive'
? poolAssets.transferKeepAlive(api, addr, assetId, amount)
: poolAssets.transfer(api, addr, assetId, amount);
if (method === 'transferKeepAlive') {
tx = poolAssets.transferKeepAlive(api, addr, assetId, amount);
} else if (method === 'transferAll') {
tx = poolAssets.transferAll(api, assetId, addr, transferKeepAlive);
} else {
tx = poolAssets.transfer(api, addr, assetId, amount);
}
palletMethod = `poolAssets::${method}`;
} else if (localAssetType === LocalTxType.ForeignAssets) {
const location = parseLocationStrToLocation(assetId);
tx =
method === 'transferKeepAlive'
? foreignAssets.transferKeepAlive(api, addr, location, amount)
: foreignAssets.transfer(api, addr, location, amount);
const foreignAssetsXcmVersion = 4;
const location = resolveMultiLocation(JSON.parse(assetId) as AnyJson, foreignAssetsXcmVersion);

if (method === 'transferKeepAlive') {
tx = foreignAssets.transferKeepAlive(api, addr, location, amount);
} else if (method === 'transferAll') {
tx = foreignAssets.transferAll(api, location, addr, transferKeepAlive);
} else {
tx = foreignAssets.transfer(api, addr, location, amount);
}
palletMethod = `foreignAssets::${method}`;
} else {
throw new BaseError(
Expand All @@ -1062,19 +1087,25 @@ export class AssetTransferApi {
*/
if (localAssetType === LocalTxType.Balances) {
const palletMethod: LocalTransferTypes = `balances::${method}`;
const tx =
method === 'transferKeepAlive'
? balances.transferKeepAlive(api, addr, amount)
: balances.transfer(api, addr, amount);
if (method === 'transferKeepAlive') {
tx = balances.transferKeepAlive(api, addr, amount);
} else if (method === 'transferAll') {
tx = balances.transferAll(api, addr, transferKeepAlive);
} else {
tx = balances.transfer(api, addr, amount);
}
return this.constructFormat(tx, 'local', null, palletMethod, destChainId, specName, {
...opts,
});
} else if (localAssetType === LocalTxType.Tokens) {
const palletMethod: LocalTransferTypes = `tokens::${method}`;
const tx =
method === 'transferKeepAlive'
? tokens.transferKeepAlive(api, addr, assetIds[0], amount)
: tokens.transfer(api, addr, assetIds[0], amount);
if (method === 'transferKeepAlive') {
tx = tokens.transferKeepAlive(api, addr, assetIds[0], amount);
} else if (method === 'transferAll') {
tx = tokens.transferAll(api, assetIds[0], addr, transferKeepAlive);
} else {
tx = tokens.transfer(api, addr, assetIds[0], amount);
}
return this.constructFormat(tx, 'local', null, palletMethod, destChainId, specName, {
...opts,
});
Expand All @@ -1090,10 +1121,13 @@ export class AssetTransferApi {
* By default local transaction on a relay chain will always be from the balances pallet
*/
const palletMethod: LocalTransferTypes = `balances::${method}`;
const tx =
method === 'transferKeepAlive'
? balances.transferKeepAlive(api, addr, amount)
: balances.transfer(api, addr, amount);
if (method === 'transferKeepAlive') {
tx = balances.transferKeepAlive(api, addr, amount);
} else if (method === 'transferAll') {
tx = balances.transferAll(api, addr, transferKeepAlive);
} else {
tx = balances.transfer(api, addr, amount);
}
return this.constructFormat(tx, 'local', null, palletMethod, destChainId, specName, {
...opts,
});
Expand Down
5 changes: 5 additions & 0 deletions src/config/disabledOpts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export const disabledOpts: DisabledOptions = {
chains: [],
error: (opt: string, chain: string) => callError(opt, chain),
},
transferAll: {
disabled: false,
chains: [],
error: (opt: string, chain: string) => callError(opt, chain),
},
transferLiquidToken: {
disabled: false,
chains: [],
Expand Down
1 change: 1 addition & 0 deletions src/createCalls/assets/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

export { transfer } from './transfer';
export { transferAll } from './transferAll';
export { transferKeepAlive } from './transferKeepAlive';
2 changes: 1 addition & 1 deletion src/createCalls/assets/transfer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transfer } from './transfer';

describe('transfer', () => {
describe('assets::transfer', () => {
it('Should construct a valid transfer extrinsic', () => {
const res = transfer(
mockSystemApi,
Expand Down
16 changes: 16 additions & 0 deletions src/createCalls/assets/transferAll.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2024 Parity Technologies (UK) Ltd.

import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transferAll } from './transferAll';

describe('assets::transferAll', () => {
it('Should construct a valid assets pallet transferAll extrinsic', () => {
const res = transferAll(
mockSystemApi,
'1',
'0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b',
true,
);
expect(res.toHex()).toEqual('0x980432200400f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b01');
});
});
14 changes: 14 additions & 0 deletions src/createCalls/assets/transferAll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2024 Parity Technologies (UK) Ltd.

import type { ApiPromise } from '@polkadot/api';
import type { SubmittableExtrinsic } from '@polkadot/api/submittable/types';
import type { ISubmittableResult } from '@polkadot/types/types';

export const transferAll = (
api: ApiPromise,
assetId: string,
destAddr: string,
keepAlive: boolean,
): SubmittableExtrinsic<'promise', ISubmittableResult> => {
return api.tx.assets.transferAll(assetId, destAddr, keepAlive);
};
2 changes: 1 addition & 1 deletion src/createCalls/assets/transferKeepAlive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transferKeepAlive } from './transferKeepAlive';

describe('transfer', () => {
describe('assets::transfer', () => {
it('Should construct a valid transfer extrinsic', () => {
const res = transferKeepAlive(
mockSystemApi,
Expand Down
1 change: 1 addition & 0 deletions src/createCalls/balances/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

export { transfer } from './transfer';
export { transferAll } from './transferAll';
export { transferKeepAlive } from './transferKeepAlive';
2 changes: 1 addition & 1 deletion src/createCalls/balances/transfer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transfer } from './transfer';

describe('transfer', () => {
describe('balances::transfer', () => {
it('Should construct a valid transfer extrinsic', () => {
const res = transfer(mockSystemApi, '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', '10000');
expect(res.toHex()).toEqual('0x98040a0000f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b419c');
Expand Down
11 changes: 11 additions & 0 deletions src/createCalls/balances/transferAll.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2024 Parity Technologies (UK) Ltd.

import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transferAll } from './transferAll';

describe('balances::transferAll', () => {
it('Should construct a valid balances pallet transferAll extrinsic', () => {
const res = transferAll(mockSystemApi, '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', true);
expect(res.toHex()).toEqual('0x94040a0400f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b01');
});
});
13 changes: 13 additions & 0 deletions src/createCalls/balances/transferAll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2024 Parity Technologies (UK) Ltd.

import type { ApiPromise } from '@polkadot/api';
import type { SubmittableExtrinsic } from '@polkadot/api/submittable/types';
import type { ISubmittableResult } from '@polkadot/types/types';

export const transferAll = (
api: ApiPromise,
destAddr: string,
keepAlive: boolean,
): SubmittableExtrinsic<'promise', ISubmittableResult> => {
return api.tx.balances.transferAll(destAddr, keepAlive);
};
2 changes: 1 addition & 1 deletion src/createCalls/balances/transferKeepAlive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transferKeepAlive } from './transferKeepAlive';

describe('transfer', () => {
describe('balances::transfer', () => {
it('Should construct a valid transfer extrinsic', () => {
const res = transferKeepAlive(
mockSystemApi,
Expand Down
1 change: 1 addition & 0 deletions src/createCalls/foreignAssets/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

export { transfer } from './transfer';
export { transferAll } from './transferAll';
export { transferKeepAlive } from './transferKeepAlive';
2 changes: 1 addition & 1 deletion src/createCalls/foreignAssets/transfer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { UnionXcmMultiLocation } from '../../createXcmTypes/types';
import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transfer } from './transfer';

describe('transfer', () => {
describe('foreignAssets::transfer', () => {
it('Should construct a valid foreignAsset transfer extrinsic', () => {
const foreignAsset = {
parents: 1,
Expand Down
33 changes: 33 additions & 0 deletions src/createCalls/foreignAssets/transferAll.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2024 Parity Technologies (UK) Ltd.

import { UnionXcmMultiLocation } from '../../createXcmTypes/types';
import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transferAll } from './transferAll';

describe('foreignAssets::transferAll', () => {
it('Should construct a valid foreignAssets pallet transferAll extrinsic', () => {
const foreignAsset = {
parents: 1,
interior: {
X2: [
{
Parachain: '2125',
},
{
GeneralIndex: '0',
},
],
},
} as UnionXcmMultiLocation;

const res = transferAll(
mockSystemApi,
foreignAsset,
'0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b',
true,
);
expect(res.toHex()).toEqual(
'0xb00435200102003521050000f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b01',
);
});
});
15 changes: 15 additions & 0 deletions src/createCalls/foreignAssets/transferAll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2024 Parity Technologies (UK) Ltd.

import type { ApiPromise } from '@polkadot/api';
import type { SubmittableExtrinsic } from '@polkadot/api/submittable/types';
import type { ISubmittableResult } from '@polkadot/types/types';
import { UnionXcmMultiLocation } from 'src/createXcmTypes/types';

export const transferAll = (
api: ApiPromise,
assetId: UnionXcmMultiLocation,
destAddr: string,
keepAlive: boolean,
): SubmittableExtrinsic<'promise', ISubmittableResult> => {
return api.tx.foreignAssets.transferAll(assetId, destAddr, keepAlive);
};
2 changes: 1 addition & 1 deletion src/createCalls/foreignAssets/transferKeepAlive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { UnionXcmMultiLocation } from '../../createXcmTypes/types';
import { mockSystemApi } from '../../testHelpers/mockSystemApi';
import { transferKeepAlive } from './transferKeepAlive';

describe('transfer', () => {
describe('foreignAssets::transfer', () => {
it('Should construct a valid foreignAssets transferKeepAlive extrinsic', () => {
const foreignAssetMultiLocation = {
parents: 1,
Expand Down
1 change: 1 addition & 0 deletions src/createCalls/poolAssets/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

export { transfer } from './transfer';
export { transferAll } from './transferAll';
export { transferKeepAlive } from './transferKeepAlive';
Loading