Asset Transfer API¶
Introduction¶
Asset Transfer API, a tool developed and maintained by Parity, is a specialized library designed to streamline asset transfers for Substrate-based blockchains. This API provides a simplified set of methods for users to:
- Execute asset transfers to other parachains or locally within the same chain
- Facilitate transactions involving system parachains like Asset Hub (Polkadot and Kusama)
Using this API, developers can manage asset transfers more efficiently, reducing the complexity of cross-chain transactions and enabling smoother operations within the ecosystem.
For additional support and information, please reach out through GitHub Issues.
Prerequisites¶
Before you begin, ensure you have the following installed:
- Node.js (recommended version 21 or greater)
- Package manager - npm should be installed with Node.js by default. Alternatively, you can use other package managers like Yarn
Install Asset Transfer API¶
To use asset-transfer-api
, you need a TypeScript project. If you don't have one, you can create a new one:
-
Create a new directory for your project:
-
Initialize a new TypeScript project:
Once you have a project set up, you can install the asset-transfer-api
package:
Note
This documentation covers version 0.3.1
of Asset Transfer API.
Set Up Asset Transfer API¶
To initialize the Asset Transfer API, you need three key components:
- A Polkadot.js API instance
- The
specName
of the chain - The XCM version to use
Using Helper Function from Library¶
For a simpler setup process, you can leverage the constructApiPromise
helper function provided by the library. It not only constructs a Polkadot.js ApiPromise
but also automatically retrieves the chain's specName
and fetches a safe XCM version. By using this function, developers can significantly reduce boilerplate code and potential configuration errors, making the initial setup both quicker and more robust.
import {
AssetTransferApi,
constructApiPromise,
} from "@substrate/asset-transfer-api";
async function main() {
const { api, specName, safeXcmVersion } = await constructApiPromise(
"INSERT_WEBSOCKET_URL"
);
const assetsApi = new AssetTransferApi(api, specName, safeXcmVersion);
// Your code using assetsApi goes here
}
main();
Note
The code examples are enclosed in an async main function to provide the necessary asynchronous context. However, you can use the code directly if you're already working within an async environment. The key is to ensure you're in an async context when working with these asynchronous operations, regardless of your specific setup.
Asset Transfer API Reference¶
For detailed information on the Asset Transfer API, including available methods, data types, and functionalities, refer to the Asset Transfer API Reference section. This resource provides in-depth explanations and technical specifications to help you integrate and utilize the API effectively.
Examples¶
Relay to System Parachain Transfer¶
This example demonstrates how to initiate a cross-chain token transfer from a relay chain to a system parachain. Specifically, 1 WND will be transferred from a Westend (relay chain) account to a Westmint (system parachain) account.
import {
AssetTransferApi,
constructApiPromise,
} from "@substrate/asset-transfer-api";
async function main() {
const { api, specName, safeXcmVersion } = await constructApiPromise(
"wss://westend-rpc.polkadot.io"
);
const assetApi = new AssetTransferApi(api, specName, safeXcmVersion);
let callInfo;
try {
callInfo = await assetApi.createTransferTransaction(
"1000",
"5EWNeodpcQ6iYibJ3jmWVe85nsok1EDG8Kk3aFg8ZzpfY1qX",
["WND"],
["1000000000000"],
{
format: "call",
xcmVersion: safeXcmVersion,
}
);
console.log(`Call data:\n${JSON.stringify(callInfo, null, 4)}`);
} catch (e) {
console.error(e);
throw Error(e as string);
}
const decoded = assetApi.decodeExtrinsic(callInfo.tx, "call");
console.log(`\nDecoded tx:\n${JSON.stringify(JSON.parse(decoded), null, 4)}`);
}
main()
.catch((err) => console.error(err))
.finally(() => process.exit());
After running the script, you'll see the following output in the terminal, which shows the call data for the cross-chain transfer and its decoded extrinsic details:
Call data: { "origin": "westend", "dest": "westmint", "direction": "RelayToSystem", "xcmVersion": 3, "method": "transferAssets", "format": "call", "tx": "0x630b03000100a10f03000101006c0c32faf970eacb2d4d8e538ac0dab3642492561a1be6f241c645876c056c1d030400000000070010a5d4e80000000000" } Decoded tx: { "args": { "dest": { "V3": { "parents": "0", "interior": { "X1": { "Parachain": "1,000" } } } }, "beneficiary": { "V3": { "parents": "0", "interior": { "X1": { "AccountId32": { "network": null, "id": "0x6c0c32faf970eacb2d4d8e538ac0dab3642492561a1be6f241c645876c056c1d" } } } } }, "assets": { "V3": [ { "id": { "Concrete": { "parents": "0", "interior": "Here" } }, "fun": { "Fungible": "1,000,000,000,000" } } ] }, "fee_asset_item": "0", "weight_limit": "Unlimited" }, "method": "transferAssets", "section": "xcmPallet" }
Local Parachain Transfer¶
The following example demonstrates a local GLMR transfer within Moonbeam, using the balances
pallet. It transfers 1 GLMR token from one account to another account, where both the sender and recipient accounts are located on the same parachain.
import {
AssetTransferApi,
constructApiPromise,
} from "@substrate/asset-transfer-api";
async function main() {
const { api, specName, safeXcmVersion } = await constructApiPromise(
"wss://wss.api.moonbeam.network"
);
const assetApi = new AssetTransferApi(api, specName, safeXcmVersion);
let callInfo;
try {
callInfo = await assetApi.createTransferTransaction(
"2004",
"0xF977814e90dA44bFA03b6295A0616a897441aceC",
[],
["1000000000000000000"],
{
format: "call",
keepAlive: true,
}
);
console.log(`Call data:\n${JSON.stringify(callInfo, null, 4)}`);
} catch (e) {
console.error(e);
throw Error(e as string);
}
const decoded = assetApi.decodeExtrinsic(callInfo.tx, "call");
console.log(`\nDecoded tx:\n${JSON.stringify(JSON.parse(decoded), null, 4)}`);
}
main()
.catch((err) => console.error(err))
.finally(() => process.exit());
Upon executing this script, the terminal will display the following output, illustrating the encoded extrinsic for the cross-chain message and its corresponding decoded format:
Call data: { "origin": "moonbeam", "dest": "moonbeam", "direction": "local", "xcmVersion": null, "method": "balances::transferKeepAlive", "format": "call", "tx": "0x0a03f977814e90da44bfa03b6295a0616a897441acec821a0600" } Decoded tx: { "args": { "dest": "0xF977814e90dA44bFA03b6295A0616a897441aceC", "value": "1,000,000,000,000,000,000" }, "method": "transferKeepAlive", "section": "balances" }
Parachain to Parachain Transfer¶
This example demonstrates creating a cross-chain asset transfer between two parachains. It shows how to send vMOVR and vBNC from a Moonriver account to a Bifrost Kusama account using the safe XCM version. It connects to Moonriver, initializes the API, and uses the createTransferTransaction
method to prepare a transaction.
import {
AssetTransferApi,
constructApiPromise,
} from "@substrate/asset-transfer-api";
async function main() {
const { api, specName, safeXcmVersion } = await constructApiPromise(
"wss://moonriver.public.blastapi.io"
);
const assetApi = new AssetTransferApi(api, specName, safeXcmVersion);
let callInfo;
try {
callInfo = await assetApi.createTransferTransaction(
"2001",
"0xc4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a063",
["vMOVR", "72145018963825376852137222787619937732"],
["1000000", "10000000000"],
{
format: "call",
xcmVersion: safeXcmVersion,
}
);
console.log(`Call data:\n${JSON.stringify(callInfo, null, 4)}`);
} catch (e) {
console.error(e);
throw Error(e as string);
}
const decoded = assetApi.decodeExtrinsic(callInfo.tx, "call");
console.log(`\nDecoded tx:\n${JSON.stringify(JSON.parse(decoded), null, 4)}`);
}
main()
.catch((err) => console.error(err))
.finally(() => process.exit());
After running this script, you'll see the following output in your terminal. This output presents the encoded extrinsic for the cross-chain message, along with its decoded format, providing a clear view of the transaction details.
Call data: { "origin": "moonriver", "dest": "bifrost", "direction": "ParaToPara", "xcmVersion": 2, "method": "transferMultiassets", "format": "call", "tx": "0x6a05010800010200451f06080101000700e40b540200010200451f0608010a0002093d000000000001010200451f0100c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300" } Decoded tx: { "args": { "assets": { "V2": [ { "id": { "Concrete": { "parents": "1", "interior": { "X2": [ { "Parachain": "2,001" }, { "GeneralKey": "0x0101" } ] } } }, "fun": { "Fungible": "10,000,000,000" } }, { "id": { "Concrete": { "parents": "1", "interior": { "X2": [ { "Parachain": "2,001" }, { "GeneralKey": "0x010a" } ] } } }, "fun": { "Fungible": "1,000,000" } } ] }, "fee_item": "0", "dest": { "V2": { "parents": "1", "interior": { "X2": [ { "Parachain": "2,001" }, { "AccountId32": { "network": "Any", "id": "0xc4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a063" } } ] } } }, "dest_weight_limit": "Unlimited" }, "method": "transferMultiassets", "section": "xTokens" }