diff --git a/src/renderer/entities/network/lib/common/constants.ts b/src/renderer/entities/network/lib/common/constants.ts index cf3ec45348..00d88f1147 100644 --- a/src/renderer/entities/network/lib/common/constants.ts +++ b/src/renderer/entities/network/lib/common/constants.ts @@ -1,5 +1,3 @@ -import { BN, BN_THOUSAND, BN_TWO } from '@polkadot/util'; - export const enum Chains { POLKADOT = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', KUSAMA = '0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe', @@ -19,10 +17,4 @@ export const AUTO_BALANCE_TIMEOUT = 1000; export const MAX_ATTEMPTS = 3; export const PROGRESSION_BASE = 2; -// Some chains incorrectly use these, i.e. it is set to values such as 0 or even 2 -// Use a low minimum validity threshold to check these against -export const THRESHOLD = BN_THOUSAND.div(BN_TWO); -export const DEFAULT_TIME = new BN(6_000); -export const ONE_DAY = new BN(24 * 60 * 60 * 1000); - export const GET_METADATA_METHOD = 'state_getMetadata'; diff --git a/src/renderer/shared/api/xcm/lib/types.ts b/src/renderer/shared/api/xcm/lib/types.ts index 8658935adc..0683081c28 100644 --- a/src/renderer/shared/api/xcm/lib/types.ts +++ b/src/renderer/shared/api/xcm/lib/types.ts @@ -67,7 +67,11 @@ export type XcmTransfer = { }; }; -export type XcmTransferType = 'xtokens' | 'xcmpallet' | 'xcmpallet-teleport'; +export const enum XcmTransferType { + XTOKENS = 'xtokens', + XCMPALLET = 'xcmpallet', + XCMPALLET_TELEPORT = 'xcmpallet-teleport', +} export type PathType = 'absolute' | 'relative' | 'concrete'; export const enum Action { diff --git a/src/renderer/shared/api/xcm/xcmService.ts b/src/renderer/shared/api/xcm/xcmService.ts index d2c7044837..6b1f364e98 100644 --- a/src/renderer/shared/api/xcm/xcmService.ts +++ b/src/renderer/shared/api/xcm/xcmService.ts @@ -3,7 +3,7 @@ import { ApiPromise } from '@polkadot/api'; import get from 'lodash/get'; import { XCM_URL, XCM_KEY } from './lib/constants'; -import { getTypeVersion, toLocalChainId, getAssetId, TEST_ACCOUNT_ID } from '@shared/lib/utils'; +import { getTypeVersion, toLocalChainId, getTypeName, getAssetId, TEST_ACCOUNT_ID } from '@shared/lib/utils'; import { XcmPalletTransferArgs, XTokenPalletTransferArgs } from '@entities/transaction'; import { chainsService } from '@entities/network'; import { toRawString } from './lib/utils'; @@ -20,6 +20,7 @@ import { XcmTransfer, PathType, Action, + XcmTransferType, } from './lib/types'; export const fetchXcmConfig = async (): Promise => { @@ -199,6 +200,7 @@ export const createJunctionFromObject = (data: {}) => { export const getAssetLocation = ( api: ApiPromise, + transferType: XcmTransferType, asset: AssetXCM, assets: Record, amount: BN, @@ -212,7 +214,8 @@ export const getAssetLocation = ( const location = PathMap[asset.assetLocationPath.type](); - const assetVersionType = getTypeVersion(api, isArray ? 'XcmVersionedMultiAssets' : 'XcmVersionedMultiAsset'); + const type = getTypeName(api, transferType, isArray ? 'assets' : 'asset'); + const assetVersionType = getTypeVersion(api, type || ''); const assetObject = { id: { Concrete: location, @@ -260,12 +263,14 @@ const getConcreteAssetLocation = (assetLocation?: LocalMultiLocation): Object | export const getVersionedDestinationLocation = ( api: ApiPromise, + transferType: XcmTransferType, originChain: Pick, destinationParaId?: number, accountId?: AccountId, ): Object | undefined => { const location = getDestinationLocation(originChain, destinationParaId, accountId); - const version = getTypeVersion(api, 'XcmVersionedMultiLocation'); + const type = getTypeName(api, transferType, 'dest'); + const version = getTypeVersion(api, type || ''); if (!version) return location; @@ -294,9 +299,14 @@ export const getDestinationLocation = ( return undefined; }; -export const getVersionedAccountLocation = (api: ApiPromise, accountId?: AccountId): Object | undefined => { +export const getVersionedAccountLocation = ( + api: ApiPromise, + transferType: XcmTransferType, + accountId?: AccountId, +): Object | undefined => { const location = getAccountLocation(accountId); - const version = getTypeVersion(api, 'XcmVersionedMultiLocation'); + const type = getTypeName(api, transferType, 'dest'); + const version = getTypeVersion(api, type || ''); if (!version) return location; diff --git a/src/renderer/shared/core/index.ts b/src/renderer/shared/core/index.ts index dba6d20fae..570bf0d00a 100644 --- a/src/renderer/shared/core/index.ts +++ b/src/renderer/shared/core/index.ts @@ -52,3 +52,4 @@ export { RewardsDestination } from './types/stake'; export type { Stake, Unlocking } from './types/stake'; export type { ProxyAccount, PartialProxyAccount, ProxyType } from './types/proxy'; +export { XcmPallets } from './types/substrate'; diff --git a/src/renderer/shared/core/types/substrate.ts b/src/renderer/shared/core/types/substrate.ts new file mode 100644 index 0000000000..8d42fdc80a --- /dev/null +++ b/src/renderer/shared/core/types/substrate.ts @@ -0,0 +1,5 @@ +export const enum XcmPallets { + XTOKENS = 'xTokens', + XCM_PALLET = 'xcmPallet', + POLKADOT_XCM = 'polkadotXcm', +} diff --git a/src/renderer/shared/lib/utils/__tests__/substrate.test.ts b/src/renderer/shared/lib/utils/__tests__/substrate.test.ts index 1eabf90497..d2dca839cc 100644 --- a/src/renderer/shared/lib/utils/__tests__/substrate.test.ts +++ b/src/renderer/shared/lib/utils/__tests__/substrate.test.ts @@ -2,7 +2,7 @@ import { ApiPromise } from '@polkadot/api'; import { BN, BN_TWO } from '@polkadot/util'; import { getExpectedBlockTime } from '../substrate'; -import { DEFAULT_TIME, THRESHOLD } from '@entities/network/lib/common/constants'; +import { DEFAULT_TIME, THRESHOLD } from '../constants'; describe('shared/lib/utils/substrate', () => { const blockTime = new BN(10_000); diff --git a/src/renderer/shared/lib/utils/constants.ts b/src/renderer/shared/lib/utils/constants.ts index f480c17ca5..b44322b9b2 100644 --- a/src/renderer/shared/lib/utils/constants.ts +++ b/src/renderer/shared/lib/utils/constants.ts @@ -1,3 +1,5 @@ +import { BN, BN_THOUSAND, BN_TWO } from '@polkadot/util'; + export const ZERO_BALANCE = '0'; export const DEFAULT_TRANSITION = 300; @@ -30,3 +32,9 @@ export const RootExplorers = [ { name: 'Subscan', account: 'https://subscan.io/account/{address}' }, { name: 'Sub.ID', account: 'https://sub.id/{address}' }, ]; + +// Some chains incorrectly use these, i.e. it is set to values such as 0 or even 2 +// Use a low minimum validity threshold to check these against +export const THRESHOLD = BN_THOUSAND.div(BN_TWO); +export const DEFAULT_TIME = new BN(6_000); +export const ONE_DAY = new BN(24 * 60 * 60 * 1000); diff --git a/src/renderer/shared/lib/utils/substrate.ts b/src/renderer/shared/lib/utils/substrate.ts index 090be2c734..b455b8fc26 100644 --- a/src/renderer/shared/lib/utils/substrate.ts +++ b/src/renderer/shared/lib/utils/substrate.ts @@ -4,8 +4,9 @@ import { isHex, hexToU8a, bnMin, BN_TWO, BN } from '@polkadot/util'; import { blake2AsHex } from '@polkadot/util-crypto'; import { u32 } from '@polkadot/types'; -import { Address, CallData, CallHash } from '@shared/core'; -import { DEFAULT_TIME, ONE_DAY, THRESHOLD } from '@entities/network'; +import { Address, CallData, CallHash, XcmPallets } from '@shared/core'; +import { XcmTransferType } from '../../api/xcm'; +import { DEFAULT_TIME, ONE_DAY, THRESHOLD } from './constants'; const V3_LABEL = 'V3'; const UNUSED_LABEL = 'unused'; @@ -135,3 +136,35 @@ export const getTypeVersions = (api: ApiPromise, typeName: string): string[] => // @ts-ignore return api.createType(typeName).defKeys; }; + +export const getTypeName = (api: ApiPromise, transferType: XcmTransferType, paramName: string): string | undefined => { + const { pallet, call } = getPalletAndCallByXcmTransferType(api, transferType); + + const param = api.tx[pallet][call].meta.args.find((n) => n.name.toString() === paramName); + + if (param) { + return param.type.toString(); + } +}; + +export const getPalletAndCallByXcmTransferType = ( + api: ApiPromise, + transferType: XcmTransferType, +): { pallet: XcmPallets; call: string } => { + if (transferType === XcmTransferType.XTOKENS) { + return { pallet: XcmPallets.XTOKENS, call: 'transferMultiasset' }; + } + + const pallet = api.tx.xcmPallet ? XcmPallets.XCM_PALLET : XcmPallets.POLKADOT_XCM; + + if (transferType === XcmTransferType.XCMPALLET) { + return { pallet, call: 'limitedReserveTransferAssets' }; + } + + if (transferType === XcmTransferType.XCMPALLET_TELEPORT) { + return { pallet, call: 'limitedTeleportAssets' }; + } + + // Should never be reached as all transferType cases are covered + throw new Error('Invalid transferType'); +}; diff --git a/src/renderer/widgets/SendAssetModal/model/send-asset.ts b/src/renderer/widgets/SendAssetModal/model/send-asset.ts index 77ee83e38e..869b9272b4 100644 --- a/src/renderer/widgets/SendAssetModal/model/send-asset.ts +++ b/src/renderer/widgets/SendAssetModal/model/send-asset.ts @@ -12,6 +12,7 @@ import { getVersionedAccountLocation, estimateRequiredDestWeight, getVersionedDestinationLocation, + XcmTransferType, } from '@shared/api/xcm'; import { xcmModel } from '@entities/xcm'; import { toLocalChainId, getParachainId } from '@shared/lib/utils'; @@ -147,9 +148,9 @@ sample({ if (!xcmTransfer || !chain || !api) return null; return ( - (xcmTransfer.type === 'xtokens' && accountId - ? getVersionedDestinationLocation(api, chain, paraId || undefined, accountId) - : getVersionedDestinationLocation(api, chain, paraId || undefined)) || null + (xcmTransfer.type === XcmTransferType.XTOKENS && accountId + ? getVersionedDestinationLocation(api, xcmTransfer.type, chain, paraId || undefined, accountId) + : getVersionedDestinationLocation(api, xcmTransfer.type, chain, paraId || undefined)) || null ); }, target: $txDest, @@ -161,11 +162,11 @@ sample({ }); sample({ - source: { accountId: $accountId, props: $xcmProps }, - fn: ({ accountId, props: { api } }) => { - if (!accountId || accountId === '0x00' || !api) return null; + source: { accountId: $accountId, props: $xcmProps, xcmTransfer: $xcmTransfer }, + fn: ({ xcmTransfer, accountId, props: { api } }) => { + if (!accountId || accountId === '0x00' || !api || !xcmTransfer) return null; - return getVersionedAccountLocation(api, accountId) || null; + return getVersionedAccountLocation(api, xcmTransfer.type, accountId) || null; }, target: $txBeneficiary, }); @@ -208,9 +209,9 @@ sample({ if (!api || !xcmAsset || !config || !xcmTransfer) return null; const resultAmount = new BN(amount || 0).add(new BN(xcmFee || 0)); - const isArray = xcmTransfer.type !== 'xtokens'; + const isArray = xcmTransfer.type !== XcmTransferType.XTOKENS; - return getAssetLocation(api, xcmAsset, config.assetsLocation, resultAmount, isArray) || null; + return getAssetLocation(api, xcmTransfer.type, xcmAsset, config.assetsLocation, resultAmount, isArray) || null; }, target: $txAsset, }); diff --git a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx index 45d65e082e..49455dd65b 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx @@ -190,11 +190,11 @@ export const InitOperation = ({ }; const getXcmTransferType = (type: XcmTransferType): TransactionType => { - if (type === 'xtokens') { + if (type === XcmTransferType.XTOKENS) { return TransactionType.XTOKENS_TRANSFER_MULTIASSET; } - if (type === 'xcmpallet-teleport') { + if (type === XcmTransferType.XCMPALLET_TELEPORT) { return api.tx.xcmPallet ? TransactionType.XCM_TELEPORT : TransactionType.POLKADOT_XCM_TELEPORT; }