UNPKG

@moonbeam-network/xcm-builder

Version:
2,015 lines (1,988 loc) 211 kB
// src/index.ts import "@polkadot/api-augment"; // src/extrinsic/ExtrinsicBuilder.utils.ts import { isEthAddress } from "@moonbeam-network/xcm-utils"; import { getTypeDef } from "@polkadot/types"; import { u8aToHex } from "@polkadot/util"; import { decodeAddress } from "@polkadot/util-crypto"; // src/extrinsic/ExtrinsicBuilder.interfaces.ts var XcmVersion = /* @__PURE__ */ ((XcmVersion2) => { XcmVersion2["v1"] = "V1"; XcmVersion2["v2"] = "V2"; XcmVersion2["v3"] = "V3"; XcmVersion2["v4"] = "V4"; XcmVersion2["v5"] = "V5"; return XcmVersion2; })(XcmVersion || {}); // src/extrinsic/ExtrinsicBuilder.utils.ts function getExtrinsicArgumentVersion(func, index = 0) { if (!func) return "V1" /* v1 */; const { type } = func.meta.args[index]; const instance = func.meta.registry.createType(type.toString()); const raw = getTypeDef(instance?.toRawType()); if (!raw.sub) { return "V1" /* v1 */; } const versions = Array.isArray(raw.sub) ? raw.sub.map((x) => x.name) : [raw.sub.name]; if (versions.includes("V5" /* v5 */)) { return "V5" /* v5 */; } if (versions.includes("V4" /* v4 */)) { return "V4" /* v4 */; } if (versions.includes("V3" /* v3 */)) { return "V3" /* v3 */; } if (versions.includes("V2" /* v2 */)) { return "V2" /* v2 */; } if (versions.includes("V1" /* v1 */)) { return "V1" /* v1 */; } throw new Error("Can't find XCM version"); } function getExtrinsicAccount(address) { return isEthAddress(address) ? { AccountKey20: { key: address } } : { AccountId32: { id: u8aToHex(decodeAddress(address)), network: null } }; } function isXcmV4(xcmVersion) { return xcmVersion >= "V4" /* v4 */; } function normalizeX1(xcmVersion, versionedObject) { if (!isXcmV4(xcmVersion)) return versionedObject; const normalizedObject = { ...versionedObject }; const interior = normalizedObject.interior; if ("X1" in interior && interior?.X1 && !Array.isArray(interior.X1)) { interior.X1 = [interior.X1]; } else if ("x1" in interior && interior?.x1 && !Array.isArray(interior.x1)) { interior.x1 = [interior.x1]; } return normalizedObject; } function normalizeConcrete(xcmVersion, versionedObject) { return isXcmV4(xcmVersion) ? versionedObject : applyConcreteWrapper(versionedObject); } function applyConcreteWrapper(versionedObject) { return { Concrete: { ...versionedObject } }; } // src/types/BaseConfig.ts var BaseConfig = class { module; func; constructor({ module: module5, func }) { this.module = module5; this.func = func; } }; // src/types/substrate/SubstrateQueryConfig.ts var SubstrateQueryConfig = class _SubstrateQueryConfig extends BaseConfig { args; queryType; transform; static is(obj) { return obj instanceof _SubstrateQueryConfig; } constructor({ args = [], transform, queryType = "query", ...other }) { super({ ...other }); this.args = args; this.queryType = queryType; this.transform = transform; } }; // src/asset-min/AssetMinBuilder.ts function AssetMinBuilder() { return { assetRegistry, assets, foreignAssets }; } function assetRegistry() { const pallet9 = "assetRegistry"; return { assetMetadatas: () => ({ build: ({ asset }) => new SubstrateQueryConfig({ module: pallet9, func: "assetMetadatas", args: [asset], // biome-ignore lint/suspicious/noExplicitAny: not sure how to fix this transform: async (response) => response.unwrapOrDefault().minimalBalance.toBigInt() }) }), currencyMetadatas: () => ({ build: ({ asset }) => new SubstrateQueryConfig({ module: pallet9, func: "currencyMetadatas", args: [asset], // biome-ignore lint/suspicious/noExplicitAny: not sure how to fix this transform: async (response) => response.unwrapOrDefault().minimalBalance.toBigInt() }) }), metadata: () => ({ build: ({ asset }) => new SubstrateQueryConfig({ module: pallet9, func: "metadata", args: [asset], // biome-ignore lint/suspicious/noExplicitAny: not sure how to fix this transform: async (response) => response.unwrapOrDefault().existentialDeposit.toBigInt() }) }) }; } function assets() { return { asset: () => ({ build: ({ asset }) => new SubstrateQueryConfig({ module: "assets", func: "asset", args: [asset], transform: async (response) => response.unwrapOrDefault().minBalance.toBigInt() }) }) }; } function foreignAssets() { return { asset: () => ({ build: ({ address }) => { if (!address) { throw new Error( "Asset address is missing for foreignAssets.asset min calculation" ); } const multilocation = { parents: 2, interior: { X2: [ { globalconsensus: { ethereum: { chainId: 1 } } }, getExtrinsicAccount(address) ] } }; return new SubstrateQueryConfig({ module: "foreignAssets", func: "asset", args: [multilocation], transform: async (response) => response.unwrapOrDefault().minBalance.toBigInt() }); } }) }; } // src/balance/BalanceBuilder.ts import { evmToAddress as evmToAddress2 } from "@polkadot/util-crypto"; // src/types/evm/ContractConfig.ts import { encodeFunctionData } from "viem"; var ContractConfig = class _ContractConfig extends BaseConfig { address; abi; args; value; static is(obj) { return obj instanceof _ContractConfig; } constructor({ address, abi, args, value, ...other }) { super({ ...other }); this.address = address; this.abi = abi; this.args = args; this.value = value; } encodeFunctionData() { return encodeFunctionData({ abi: this.abi, functionName: this.func, args: this.args }); } }; // src/contract/contracts/XcmPrecompile/XcmPrecompile.ts import { u8aToHex as u8aToHex3 } from "@polkadot/util"; import { decodeAddress as decodeAddress3 } from "@polkadot/util-crypto"; // src/types/evm/EvmQueryConfig.ts var EvmQueryConfig = class _EvmQueryConfig { args; func; static is(obj) { return obj instanceof _EvmQueryConfig; } constructor({ args, func }) { this.args = args; this.func = func; } }; // src/types/substrate/ExtrinsicConfig.ts var ExtrinsicConfig = class _ExtrinsicConfig extends BaseConfig { // biome-ignore lint/suspicious/noExplicitAny: not sure how to fix this getArgs; static is(obj) { return obj instanceof _ExtrinsicConfig; } constructor({ getArgs, ...other }) { super({ ...other }); this.getArgs = getArgs; } }; // src/types/substrate/SubstrateCallConfig.ts var SubstrateCallConfig = class _SubstrateCallConfig { api; // biome-ignore lint/suspicious/noExplicitAny: not sure how to fix this call; static is(obj) { return obj instanceof _SubstrateCallConfig; } constructor({ api, call }) { this.api = api; this.call = call; } }; // src/contract/ContractBuilder.interfaces.ts var TransferType = /* @__PURE__ */ ((TransferType2) => { TransferType2[TransferType2["Teleport"] = 0] = "Teleport"; TransferType2[TransferType2["LocalReserve"] = 1] = "LocalReserve"; TransferType2[TransferType2["DestinationReserve"] = 2] = "DestinationReserve"; return TransferType2; })(TransferType || {}); // src/contract/ContractBuilder.utils.ts import { EvmParachain } from "@moonbeam-network/xcm-types"; import { u8aToHex as u8aToHex2 } from "@polkadot/util"; import { decodeAddress as decodeAddress2 } from "@polkadot/util-crypto"; // src/extrinsic/pallets/polkadotXcm/polkadotXcm.util.ts import { Ecosystem } from "@moonbeam-network/xcm-types"; function getPolkadotXcmExtrinsicArgs({ assets: assets3, destinationAddress, destination, func, parents = 1, feeIndex = 0 }) { const version = getExtrinsicArgumentVersion(func); return [ // dest { [version]: normalizeX1(version, { parents, interior: { X1: { Parachain: destination.parachainId } } }) }, // beneficiary { [version]: normalizeX1(version, { parents: 0, interior: { X1: getExtrinsicAccount(destinationAddress) } }) }, // assets { [version]: assets3 }, // feeAssetItem feeIndex, // weightLimit "Unlimited" ]; } function isKusamaDestination(destination) { return destination.ecosystem === Ecosystem.Kusama || destination.ecosystem === Ecosystem.MoonsamaRelay; } function isPolkadotDestination(destination) { return destination.ecosystem === Ecosystem.Polkadot || destination.ecosystem === Ecosystem.MoonlamaRelay; } function getGlobalConsensus(destination) { if (isKusamaDestination(destination)) { return "Kusama"; } if (isPolkadotDestination(destination)) { return "Polkadot"; } return { ByGenesis: destination.relayGenesisHash }; } function getEcosystemTransferExtrinsicArgs({ assets: assets3, destinationAddress, destination, func, feeIndex = 0 }) { const version = getExtrinsicArgumentVersion(func); const globalConsensus = getGlobalConsensus(destination); return [ // dest { [version]: normalizeX1(version, { parents: 2, interior: { X2: [ { GlobalConsensus: globalConsensus }, { Parachain: destination.parachainId } ] } }) }, // beneficiary { [version]: normalizeX1(version, { parents: 0, interior: { X1: getExtrinsicAccount(destinationAddress) } }) }, // assets { [version]: assets3 }, // feeAssetItem feeIndex, // weightLimit "Unlimited" ]; } function shouldFeeAssetPrecedeAsset({ asset, fee }) { const assetIdNumber = Number(asset.getAssetId()); const feeAssetIdNumber = Number(fee.getAssetId()); if (Number.isNaN(assetIdNumber) || Number.isNaN(feeAssetIdNumber)) { return false; } return assetIdNumber > feeAssetIdNumber; } // src/contract/ContractBuilder.utils.ts function getPrecompileDestinationInterior(destination, address) { if (!address) { return [encodeParachain(destination.parachainId)]; } const acc = encodeAddress(destination, address); return destination.parachainId ? [encodeParachain(destination.parachainId), acc] : [acc]; } function getBeneficiaryMultilocation(address, destination) { return [0, [encodeAddress(destination, address)]]; } function getDestinationMultilocation(address, destination) { const interior = getPrecompileDestinationInterior(destination, address); return [1, interior]; } function getDestinationParachainMultilocation(destination) { if (destination.isRelay) { return [1, []]; } return [1, [encodeParachain(destination.parachainId)]]; } function getGlobalConsensusDestination(sourceApi, destination) { return [ 2, [ encodeGlobalConsensus(sourceApi, destination), encodeParachain(destination.parachainId) ] ]; } function getPalletInstanceMultilocation(sourceApi, asset) { return [ [0, [encodePalletInstance(sourceApi, asset.getAssetPalletInstance())]], asset.amount ]; } function getAssetAddressMultilocation(sourceApi, asset, destination) { if (!asset.address) { throw new Error(`Asset address is required for ${asset.key}`); } return [ [ 0, [ encodePalletInstance(sourceApi, asset.getAssetPalletInstance()), encodeAddress(destination, asset.address) ] ], asset.amount ]; } function getGlobalConsensusAssetMultilocation(sourceApi, asset, destination) { const assetInDestination = destination.getChainAsset(asset); return [ [ 2, [ encodeGlobalConsensus(sourceApi, destination), encodeParachain(destination.parachainId), encodePalletInstance( sourceApi, assetInDestination.getAssetPalletInstance() ) ] ], asset.amount ]; } function getAddressGlobalConsensusAssetMultilocation(sourceApi, asset, destination) { const assetInDestination = destination.getChainAsset(asset); if (!assetInDestination.address) { throw new Error(`Asset address is required for ${assetInDestination.key}`); } return [ [ 2, [ encodeGlobalConsensus(sourceApi, destination), encodeParachain(destination.parachainId), encodePalletInstance( sourceApi, assetInDestination.getAssetPalletInstance() ), encodeAddress(destination, assetInDestination.address) ] ], asset.amount ]; } function encodeXcmMessageToBytes(xcmMessage, api) { if (!api) { throw new Error("API is required to encode XCM message"); } try { const versionedXcm = api.createType("XcmVersionedXcm", xcmMessage); return versionedXcm.toHex(); } catch (error) { console.error("Failed to encode XCM message:", error); throw error; } } function encodeParachain(paraId) { return `0x00${paraId.toString(16).padStart(8, "0")}`; } function encodeGlobalConsensus(api, destination) { const globalConsensus = getGlobalConsensus(destination); return encodeXcmJunction(api, { GlobalConsensus: globalConsensus }); } function encodePalletInstance(api, palletInstance) { return encodeXcmJunction(api, { PalletInstance: palletInstance }); } function encodeXcmJunction(api, junction) { if (!api) { throw new Error("API is required to encode XCM junction"); } const junctionType = api.createType("XcmV3Junction", junction); return junctionType.toHex(); } function encodeAddress(destination, address) { const accountType = EvmParachain.is(destination) ? "03" : "01"; return `0x${accountType}${u8aToHex2( decodeAddress2(address), -1, false )}00`; } // src/contract/contracts/XcmPrecompile/XcmPrecompileAbi.ts var XCM_ABI = [ { inputs: [ { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "dest", type: "tuple" }, { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "beneficiary", type: "tuple" }, { components: [ { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "location", type: "tuple" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct XCM.AssetLocationInfo[]", name: "assets", type: "tuple[]" }, { internalType: "uint32", name: "feeAssetItem", type: "uint32" } ], name: "transferAssetsLocation", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "uint32", name: "paraId", type: "uint32" }, { internalType: "address", name: "beneficiary", type: "address" }, { components: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct XCM.AssetAddressInfo[]", name: "assets", type: "tuple[]" }, { internalType: "uint32", name: "feeAssetItem", type: "uint32" } ], name: "transferAssetsToPara20", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "uint32", name: "paraId", type: "uint32" }, { internalType: "bytes32", name: "beneficiary", type: "bytes32" }, { components: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct XCM.AssetAddressInfo[]", name: "assets", type: "tuple[]" }, { internalType: "uint32", name: "feeAssetItem", type: "uint32" } ], name: "transferAssetsToPara32", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "bytes32", name: "beneficiary", type: "bytes32" }, { components: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct XCM.AssetAddressInfo[]", name: "assets", type: "tuple[]" }, { internalType: "uint32", name: "feeAssetItem", type: "uint32" } ], name: "transferAssetsToRelay", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "dest", type: "tuple" }, { components: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct XCM.AssetAddressInfo[]", name: "assets", type: "tuple[]" }, { internalType: "enum XCM.TransferType", name: "assetsTransferType", type: "uint8" }, { internalType: "uint8", name: "remoteFeesIdIndex", type: "uint8" }, { internalType: "enum XCM.TransferType", name: "feesTransferType", type: "uint8" }, { internalType: "bytes", name: "customXcmOnDest", type: "bytes" } ], name: "transferAssetsUsingTypeAndThenAddress", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "dest", type: "tuple" }, { components: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct XCM.AssetAddressInfo[]", name: "assets", type: "tuple[]" }, { internalType: "uint8", name: "remoteFeesIdIndex", type: "uint8" }, { internalType: "bytes", name: "customXcmOnDest", type: "bytes" }, { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "remoteReserve", type: "tuple" } ], name: "transferAssetsUsingTypeAndThenAddress", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "dest", type: "tuple" }, { components: [ { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "location", type: "tuple" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct XCM.AssetLocationInfo[]", name: "assets", type: "tuple[]" }, { internalType: "enum XCM.TransferType", name: "assetsTransferType", type: "uint8" }, { internalType: "uint8", name: "remoteFeesIdIndex", type: "uint8" }, { internalType: "enum XCM.TransferType", name: "feesTransferType", type: "uint8" }, { internalType: "bytes", name: "customXcmOnDest", type: "bytes" } ], name: "transferAssetsUsingTypeAndThenLocation", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "dest", type: "tuple" }, { components: [ { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "location", type: "tuple" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct XCM.AssetLocationInfo[]", name: "assets", type: "tuple[]" }, { internalType: "uint8", name: "remoteFeesIdIndex", type: "uint8" }, { internalType: "bytes", name: "customXcmOnDest", type: "bytes" }, { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct XCM.Location", name: "remoteReserve", type: "tuple" } ], name: "transferAssetsUsingTypeAndThenLocation", outputs: [], stateMutability: "nonpayable", type: "function" } ]; // src/contract/contracts/XcmPrecompile/XcmPrecompile.ts var XCM_PRECOMPILE_ADDRESS = "0x000000000000000000000000000000000000081A"; function XcmPrecompile() { return { transferAssetsToPara20: (shouldTransferAssetPrecedeFeeAsset = true) => ({ build: ({ destinationAddress, asset, destination, fee }) => { const assets3 = getAssets( asset, fee, shouldTransferAssetPrecedeFeeAsset ); return new ContractConfig({ address: XCM_PRECOMPILE_ADDRESS, abi: XCM_ABI, args: [destination.parachainId, destinationAddress, assets3, 0], func: "transferAssetsToPara20", module: "Xcm" }); } }), transferAssetsToPara32: (shouldTransferAssetPrecedeFeeAsset = false) => ({ build: ({ destinationAddress, asset, destination, fee }) => { const assets3 = getAssets( asset, fee, shouldTransferAssetPrecedeFeeAsset ); return new ContractConfig({ address: XCM_PRECOMPILE_ADDRESS, abi: XCM_ABI, args: [ destination.parachainId, u8aToHex3(decodeAddress3(destinationAddress)), assets3, 0 ], func: "transferAssetsToPara32", module: "Xcm" }); } }), transferAssetsToRelay: () => ({ build: ({ destinationAddress, asset }) => { return new ContractConfig({ address: XCM_PRECOMPILE_ADDRESS, abi: XCM_ABI, args: [ u8aToHex3(decodeAddress3(destinationAddress)), [[asset.address, asset.amount]], 0 ], func: "transferAssetsToRelay", module: "Xcm" }); } }), transferAssetsLocation: () => ({ nativeAsset: () => ({ build: (params) => { return buildTransferAssetsLocation({ ...params, assetsMultilocations: [ getPalletInstanceMultilocation(params.sourceApi, params.asset) ] }); } }), localErc20: () => ({ build: (params) => { return buildTransferAssetsLocation({ ...params, assetsMultilocations: [ getAssetAddressMultilocation( params.sourceApi, params.asset, params.destination ) ] }); } }), foreignAsset: () => ({ build: (params) => { return buildTransferAssetsLocation({ ...params, assetsMultilocations: [ getGlobalConsensusAssetMultilocation( params.sourceApi, params.asset, params.destination ) ] }); } }), foreignErc20: () => ({ build: (params) => { return buildTransferAssetsLocation({ ...params, assetsMultilocations: [ // fee asset getGlobalConsensusAssetMultilocation( params.sourceApi, params.fee, params.destination ), // transfer asset getAddressGlobalConsensusAssetMultilocation( params.sourceApi, params.asset, params.destination ) ] }); } }) }), transferAssetsUsingTypeAndThenAddress: (shouldTransferAssetPrecedeFeeAsset = false) => ({ build: ({ destinationAddress, asset, fee, destination, destinationApi, sourceApi }) => { const assets3 = getAssets( asset, fee, shouldTransferAssetPrecedeFeeAsset ); const xcmMessage = buildXcmMessage( assets3, destinationAddress, sourceApi ); const customXcmOnDest = encodeXcmMessageToBytes( xcmMessage, destinationApi ); const feeIndex = shouldTransferAssetPrecedeFeeAsset ? 1 : 0; return new ContractConfig({ address: XCM_PRECOMPILE_ADDRESS, abi: XCM_ABI, args: [ getDestinationParachainMultilocation(destination), assets3, 2 /* DestinationReserve */, feeIndex, 2 /* DestinationReserve */, customXcmOnDest ], func: "transferAssetsUsingTypeAndThenAddress", module: "Xcm" }); } }) }; } function getAssets(asset, feeAsset, shouldTransferAssetPrecedeFeeAsset) { if (feeAsset.isSame(asset)) { return [[asset.address, asset.amount]]; } return shouldTransferAssetPrecedeFeeAsset ? [ [asset.address, asset.amount], [feeAsset.address, feeAsset.amount] ] : [ [feeAsset.address, feeAsset.amount], [asset.address, asset.amount] ]; } function buildXcmMessage(assets3, destinationAddress, sourceApi) { const xcmVersion = getExtrinsicArgumentVersion( sourceApi?.tx.polkadotXcm?.send || sourceApi?.tx.xcmPallet?.send ); const instruction = { DepositAsset: { assets: { Wild: { AllCounted: assets3.length } }, beneficiary: { parents: 0, interior: { X1: [ { AccountId32: { id: u8aToHex3(decodeAddress3(destinationAddress)), network: null } } ] } } } }; return { [xcmVersion]: [instruction] }; } function buildTransferAssetsLocation({ assetsMultilocations, feeAssetItem = 0, destinationAddress, destination, sourceApi }) { return new ContractConfig({ address: XCM_PRECOMPILE_ADDRESS, abi: XCM_ABI, args: [ getGlobalConsensusDestination(sourceApi, destination), getBeneficiaryMultilocation(destinationAddress, destination), assetsMultilocations, feeAssetItem ], func: "transferAssetsLocation", module: "Xcm" }); } // src/contract/contracts/Xtokens/Xtokens.ts import { formatAssetIdToERC20 } from "@moonbeam-network/xcm-utils"; import { u8aToHex as u8aToHex4 } from "@polkadot/util"; import { decodeAddress as decodeAddress4, evmToAddress } from "@polkadot/util-crypto"; // src/contract/contracts/Xtokens/XtokensABI.ts var XTOKENS_ABI = [ { inputs: [ { internalType: "address", name: "currencyAddress", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" }, { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct Xtokens.Multilocation", name: "destination", type: "tuple" }, { internalType: "uint64", name: "weight", type: "uint64" } ], name: "transfer", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { components: [ { internalType: "address", name: "currencyAddress", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" } ], internalType: "struct Xtokens.Currency[]", name: "currencies", type: "tuple[]" }, { internalType: "uint32", name: "feeItem", type: "uint32" }, { components: [ { internalType: "uint8", name: "parents", type: "uint8" }, { internalType: "bytes[]", name: "interior", type: "bytes[]" } ], internalType: "struct Xtokens.Multilocation", name: "destination", type: "tuple" }, { internalType: "uint64", name: "weight", type: "uint64" } ], name: "transferMultiCurrencies", outputs: [], stateMutability: "nonpayable", type: "function" } ]; // src/contract/contracts/Xtokens/Xtokens.ts var U_64_MAX = 18446744073709551615n; var XTOKENS_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000000804"; function Xtokens() { return { transfer: (weight = U_64_MAX) => ({ build: ({ destinationAddress, asset, destination }) => new ContractConfig({ address: XTOKENS_CONTRACT_ADDRESS, abi: XTOKENS_ABI, args: [ asset.address ? formatAssetIdToERC20(asset.address) : asset.getAssetId(), asset.amount, getDestinationMultilocation(destinationAddress, destination), weight ], func: "transfer", module: "Xtokens" }) }), transferMultiCurrencies: (shouldTransferAssetPrecedeFeeAsset = true, weight = U_64_MAX) => ({ build: ({ asset, destination, destinationAddress, fee }) => { const transferAsset = [ asset.address ? formatAssetIdToERC20(asset.address) : asset.getAssetId(), asset.amount ]; const feeAsset = [ fee.address ? formatAssetIdToERC20(fee.address) : fee.getAssetId(), fee.amount ]; const assets3 = shouldTransferAssetPrecedeFeeAsset ? [transferAsset, feeAsset] : [feeAsset, transferAsset]; const feeAssetIndex = shouldTransferAssetPrecedeFeeAsset ? 1 : 0; return new ContractConfig({ address: XTOKENS_CONTRACT_ADDRESS, abi: XTOKENS_ABI, args: [ assets3, feeAssetIndex, getDestinationMultilocation(destinationAddress, destination), weight ], func: "transferMultiCurrencies", module: "Xtokens" }); } }), transferWithEvmTo32: (weight = U_64_MAX) => ({ build: ({ destinationAddress, asset, destination }) => { const multilocation = getDestinationMultilocationForPrecompileDestination( destinationAddress, destination ); return new ContractConfig({ address: XTOKENS_CONTRACT_ADDRESS, abi: XTOKENS_ABI, args: [ asset.address ? formatAssetIdToERC20(asset.address) : asset.getAssetId(), asset.amount, multilocation, weight ], func: "transfer", module: "Xtokens" }); } }) }; } function getDestinationMultilocationForPrecompileDestination(address, destination) { const accountType = "01"; const substrateAddress = evmToAddress(address); const acc = `0x${accountType}${u8aToHex4( decodeAddress4(substrateAddress), -1, false )}00`; return [ 1, destination.parachainId ? [`0x0000000${destination.parachainId.toString(16)}`, acc] : [acc] ]; } // src/contract/ContractBuilder.ts function ContractBuilder() { return { Xtokens, XcmPrecompile }; } // src/balance/Erc20Abi.ts var ERC20_ABI = [ { constant: true, inputs: [], name: "name", outputs: [ { name: "", type: "string" } ], payable: false, stateMutability: "view", type: "function" }, { constant: false, inputs: [ { name: "_spender", type: "address" }, { name: "_value", type: "uint256" } ], name: "approve", outputs: [ { name: "", type: "bool" } ], payable: false, stateMutability: "nonpayable", type: "function" }, { constant: true, inputs: [], name: "totalSupply", outputs: [ { name: "", type: "uint256" } ], payable: false, stateMutability: "view", type: "function" }, { constant: false, inputs: [ { name: "_from", type: "address" }, { name: "_to", type: "address" }, { name: "_value", type: "uint256" } ], name: "transferFrom", outputs: [ { name: "", type: "bool" } ], payable: false, stateMutability: "nonpayable", type: "function" }, { constant: true, inputs: [], name: "decimals", outputs: [ { name: "", type: "uint8" } ], payable: false, stateMutability: "view", type: "function" }, { constant: true, inputs: [ { name: "_owner", type: "address" } ], name: "balanceOf", outputs: [ { name: "balance", type: "uint256" } ], payable: false, stateMutability: "view", type: "function" }, { constant: true, inputs: [], name: "symbol", outputs: [ { name: "", type: "string" } ], payable: false, stateMutability: "view", type: "function" }, { constant: false, inputs: [ { name: "_to", type: "address" }, { name: "_value", type: "uint256" } ], name: "transfer", outputs: [ { name: "", type: "bool" } ], payable: false, stateMutability: "nonpayable", type: "function" }, { constant: true, inputs: [ { name: "_owner", type: "address" }, { name: "_spender", type: "address" } ], name: "allowance", outputs: [ { name: "", type: "uint256" } ], payable: false, stateMutability: "view", type: "function" }, { payable: true, stateMutability: "payable", type: "fallback" }, { anonymous: false, inputs: [ { indexed: true, name: "owner", type: "address" }, { indexed: true, name: "spender", type: "address" }, { indexed: false, name: "value", type: "uint256" } ], name: "Approval", type: "event" }, { anonymous: false, inputs: [ { indexed: true, name: "from", type: "address" }, { indexed: true, name: "to", type: "address" }, { indexed: false, name: "value", type: "uint256" } ], name: "Transfer", type: "event" } ]; // src/balance/BalanceBuilder.ts function BalanceBuilder() { return { evm, substrate }; } function evm() { return { erc20, native }; } function erc20() { return { build: ({ asset, address }) => { if (!asset.address) { throw new Error(`Asset ${asset.key} has no address`); } return new ContractConfig({ address: asset.address, abi: ERC20_ABI, args: [address], func: "balanceOf", module: "Erc20" }); } }; } function native() { return { build: ({ address }) => { return new EvmQueryConfig({ func: "getBalance", args: [{ address }] }); } }; } function substrate() { return { assets: assets2, foreignAssets: foreignAssets2, system, tokens }; } function assets2() { return { account: () => ({ build: ({ address, asset }) => new SubstrateQueryConfig({ module: "assets", func: "account", args: [asset.getBalanceAssetId(), address], transform: async (response) => response.unwrapOrDefault().balance.toBigInt() }) }) }; } function foreignAssets2() { return { account: () => ({ globalConsensus: () => ({ build: ({ address, asset }) => { if (!asset.address) { throw new Error( "Asset address is needed to calculate balance with foreignAssets.account function" ); } const multilocation = { parents: 2, interior: { X2: [ { GlobalConsensus: { ethereum: { chainId: 1 } } }, getExtrinsicAccount(asset.address) ] } }; return new SubstrateQueryConfig({ module: "foreignAssets", func: "account", args: [multilocation, address], transform: async (response) => response.unwrapOrDefault().balance.toBigInt() }); } }), id: () => ({ build: ({ address, asset }) => { return new SubstrateQueryConfig({ module: "foreignAssets", func: "account", args: [asset.getBalanceAssetId(), address], transform: async (response) => response.unwrapOrDefault().balance.toBigInt() }); } }) }) }; } function system() { return { account: () => ({ build: ({ address }) => new SubstrateQueryConfig({ module: "system", func: "account", args: [address], transform: async (response) => calculateSystemAccountBalance(response) }) }), accountEquilibrium: () => ({ build: ({ address, asset }) => new SubstrateQueryConfig({ module: "system", func: "account", args: [address], transform: async (response) => { if (response.data.isEmpty) { return 0n; } const res = response.data.toJSON(); let balances; if (Array.isArray(res)) { balances = res; } if (Array.isArray(res?.v0?.balance)) { balances = res.v0.balance; } if (!balances) { throw new Error("Can't get balance from Equilibrium chain"); } const balance = balances.find( ([assetId]) => assetId === asset.getBalanceAssetId() ); if (!balance) { return 0n; } return BigInt(balance[1].positive); } }) }), accountEvmTo32: () => ({ build: ({ address }) => { const substrateAddress = evmToAddress2(address); return new SubstrateQueryConfig({ module: "system", func: "account", args: [substrateAddress], transform: async (response) => calculateSystemAccountBalance(response) }); } }) }; } function tokens() { return { accounts: () => ({ build: ({ address, asset }) => new SubstrateQueryConfig({ module: "tokens", func: "accounts", args: [address, asset.getBalanceAssetId()], transform: async ({ free, frozen }) => BigInt(free.sub(frozen).toString()) }) }) }; } async function calculateSystemAccountBalance(response) { const balance = response.data; const free = BigInt(balance.free.toString()); const frozen = balance.miscFrozen ?? balance.frozen; const frozenMinusReserved = BigInt(frozen.sub(balance.reserved).toString()); const locked = frozenMinusReserved < 0n ? 0n : frozenMinusReserved; return free - locked; } // src/extrinsic/pallets/eqBalances/eqBalances.ts var pallet = "eqBalances"; function eqBalances() { return { xcmTransfer: () => ({ build: ({ destinationAddress, asset, destination }) => new ExtrinsicConfig({ module: pallet, func: "xcmTransfer", getArgs: () => [ asset.getAssetId(), asset.amount, { parents: 1, interior: { X2: [ { Parachain: destination.parachainId }, getExtrinsicAccount(destinationAddress) ] } }, "ThisAccWillPay" /* ThisAccWillPay */ ] }) }), transferXcm: () => ({ build: ({ destinationAddress: address, asset, destination, fee }) => new ExtrinsicConfig({ module: pallet, func: "transferXcm", getArgs: () => { const amountWithoutFee = asset.amount - fee.amount > 0n ? asset.amount - fee.amount : 0n; return [ [ asset.getAssetId(), asset.isSame(fee) ? amountWithoutFee : asset.amount ], [fee.getAssetId(), fee.amount], { parents: 1, interior: { X2: [ { Parachain: destination.parachainId }, getExtrinsicAccount(address) ] } } ]; } }) }) }; } // src/extrinsic/pallets/polkadotXcm/polkadotXcm.ts var pallet2 = "polkadotXcm"; function polkadotXcm() { return { limitedReserveTransferAssets: () => { const func = "limitedReserveTransferAssets"; return { here: () => ({ build: (params) => new ExtrinsicConfig({ module: pallet2, func, getArgs: (extrinsicFunction) => { const version = getExtrinsicArgumentVersion(extrinsicFunction); return getPolkadotXcmExtrinsicArgs({ ...params, func: extrinsicFunction, assets: [ { id: normalizeConcrete(version, { parents: 0, interior: "Here" }), fun: { Fungible: params.asset.amount } } ] }); } }) }), X1: () => ({ build: (params) => new ExtrinsicConfig({ module: pallet2, func, getArgs: (extrinsicFunction) => { const version = getExtrinsicArgumentVersion(extrinsicFunction); return getPolkadotXcmExtrinsicArgs({ ...params, func: extrinsicFunction, assets: [ { id: normalizeConcrete( version, normalizeX1(version, { parents: 0, interior: { X1: { PalletInstance: params.asset.getAssetPalletInstance() } } }) ), fun: { Fungible: params.asset.amount } } ] }); } }) }), X2: () => ({ build: (params) => new ExtrinsicConfig({ module: pallet2, func, getArgs: (extrinsicFunction) => { const isAssetDifferent = !params.asset.isSame(params.fee); const version = getExtrinsicArgumentVersion(extrinsicFunction); const assets3 = [ { id: normalizeConcrete(version, { parents: 0, interior: { X2: [ { PalletInstance: params.asset.getAssetPalletInstance() }, { GeneralIndex: params.asset.getAssetId() } ] } }), fun: { Fungible: params.asset.amount } } ]; const shouldFeeAssetPrecede = shouldFeeAssetPrecedeAsset(params); if (isAssetDifferent) { const feeAsset = { id: normalizeConcrete(version, { parents: 0, interior: { X2: [ { PalletInstance: params.fee.getAssetPalletInstance() }, { GeneralIndex: params.fee.getAssetId() } ] } }), fun: { Fungible: params.fee.amount } }; if (shouldFeeAssetPrecede) { assets3.unshift(feeAsset); } else { assets3.push(feeAsset); } } return getPolkadotXcmExtrinsicArgs({ ...params, func: extrinsicFunction, assets: assets3, feeIndex: isAssetDifferent && !shouldFeeAssetPrecede ? 1 : 0 }); } }) }), X2PalletInstance: () => ({ build: (params) => new ExtrinsicConfig({ module: pallet2, func, getArgs: (extrinsicFunction) => { const version = getExtrinsicArgumentVersion(extrinsicFunction); const assetInDestination = params.destination.getChainAsset(