@moonbeam-network/xcm-builder
Version:
Moonbeam XCM builder
2,015 lines (1,988 loc) • 211 kB
JavaScript
// 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(