XCM SDK is a tool that provides an interface to send XCM messages for Substrate based blockchains. This library is written in Typescript so it can be imported in a whole new set of applications or dApps that use Javascript/Typescript engines such as Node.js.
npm i xcm-sdk
// JavaScript
const { Provider } = require("xcm-sdk")
// TypeScript
import { Provider } from "xcm-sdk"
const provider = new Provider(rpc, sender)
Param | Description |
---|---|
rpc | rpc endpoint |
sender | signer of the transaction |
If you want to sign with Alice in a local node:
import { Keyring } from '@polkadot/keyring'
import { cryptoWaitReady } from '@polkadot/util-crypto'
const rpc = "ws://127.0.0.1:37345" // local node ws
await cryptoWaitReady();
const keyring = new Keyring({ type: "sr25519" });
const sender = keyring.addFromUri("//Alice");
const provider = new Provider(rpc, sender);
If you want to sign with mnemonic
import { Keyring } from '@polkadot/keyring'
const sender = keyring.addFromMnemonic(
"<your mnemonic seed here>"
);
If you want to sign with polkadotjs extension
import { web3FromAddress, web3Accounts, web3Enable } from "@polkadot/extension-dapp";
const extensions = await web3Enable("<your app name>");
const accounts = await web3Accounts();
const accountId = accounts[0].address;
const injector = await web3FromAddress(accountId);
const provider = new Provider(rpc, accountId);
provider.setInjectorSigner(injector.signer);
Reserve Asset Transfer with reserveTransferAsset and LimitedReserveTransferAsset methods and Asset teleportation with teleportAsset and LimitedTeleportAsset methods.
provider.limitedReserveTransferAssets(params)
provider.reserveTransferAssets(params)
provider.limitedReserveTransferAssets(params)
provider.reserveTransferAssets(params)
Param | Description |
---|---|
destination | The destination to transfer the asset. If you want to transfer asset from relaychain to a parachain set 'Parachain'. Default 'Here'. |
destinationParents | 0 is default, 1 when you want to transfer from parachain to relaychain or parachain to parachain |
destinationValue | The destination value, for example a parachain id |
beneficiary | beneficary target, an accountId32 |
beneficiaryParents | 0 is default |
beneficiaryValue | The beneficiary value, account address to send the asset |
amount | token amount to transfer |
assetId | AssetId to transfer from parachain, make sure the parchain support the asset and the sender account have enough asset to transfer |
weightLimit | Optional, only for limited methods. Set the maximum weight for the extrinsic |
Depends on the parachain or relay chain configuration you have to use Asset teleportation or reserve asset transfer. Make sure you know what method use before execute any transfer. You can search in any scan to know, for example rococo scan
If you want to tests in Testnet, you have Rococo.
Get some assets: Rococo faucet
The examples are in ./examples/rococo/, you can put your configuration in ./examples/rococo/rococo-examples-util.ts. Then you can run a command for each example. If you want to run them manually, you must create your own script (.js or ts) and import the dependencies.
export const rococoExampleUtils = {
rococoRpc: 'wss://rococo-rpc.polkadot.io',
rockMineRpc: 'wss://rococo-rockmine-rpc.polkadot.io',
rockMineParachainId: 1000,
mangataParachainId: 2110,
daliParachainId: 2087,
senderMnemonic: '<your account mnemonic>',
rockmineDestinationAccount: '<rockmine address account>',
daliDestinationAccount: '<dali destination account>',
rococoDestinationAccount: '<rococo address account>',
mangataDestinationAccount: '<mangata address account>',
rocAmount: <amount to transfer>,
}
command:
npx ts-node src/examples/rococo/rococo-to-rockmine.ts
manually:
const destination = "Parachain"
const destinationValue = 2000 // Rockmine parchain id
const beneficiary = "AccountId32"
const beneficiaryValue = "<rockmine account address>" // account address
const amount = 1000000000000000
const res = await provider.limitedTeleportAssets({
destination,
destinationValue,
beneficiary,
beneficiaryValue,
amount,
});
or
command:
npx ts-node src/examples/rococo/rococo-to-rockmine-no-limited.ts
manually:
const destination = "Parachain"
const destinationValue = 2000 // Rockmine parchain id
const beneficiary = "AccountId32"
const beneficiaryValue = "<rockmine account address>" // account address
const amount = 1000000000000000
const res = await provider.teleportAssets({
destination,
destinationValue,
beneficiary,
beneficiaryValue,
amount,
});
command:
npx ts-node src/examples/rococo/rockmine-to-rococo.ts
manually:
const destinationParents = 1; // Destination to Rococo
const beneficiary = "AccountId32"
const beneficiaryValue = "<rococo account address>" // account address
const amount = 1000000000000000
const res = await provider.limitedTeleportAssets({
destination,
destinationValue,
beneficiary,
beneficiaryValue,
amount,
});
The ROC asset in Mangata is the asset with id 4. You can check here, in "SELECTED STATE QUERY" select tokens, then in u128 input put 4.
command:
npx ts-node src/examples/rococo/rococo-to-mangata-no-limited.ts
manually:
const destination = "Parachain"
const destinationValue = 2110 // Mangata parchain id
const beneficiary = "AccountId32"
const beneficiaryValue = "<mangata account address>" // account address
const amount = 1000000000000000
const res = await provider.reserveTransferAssets({
destination,
destinationValue,
beneficiary,
beneficiaryValue,
amount,
});
The sdk also has a method to make custom extrinsics defined by the user. You can call any pallet and method and passing a custom body to that method on your own.
provider.customExtrinsic(params)
Param | Description |
---|---|
asSudo | pass true if you want to execute the extrinsic as sudo, default is false |
pallet | The pallet to call, for example: "polkadotXcm", "xcmPallet" |
method | The method to call in the pallet, for example: "reserveTransferAssets" |
body | The arguments for the method, can be an array or an object |
From Rococo to Rockmine using body as an object:
command:
npx ts-node src/examples/custom-extrinsic/teleport-relaychain-to-parachain.ts
manually:
const pallet = "xcmPallet"
const method = "limitedTeleportAssets"
const body = {
dest: {
V1: {
parents: 0,
interior: {
X1: {
Parachain: 1000,
},
},
},
},
beneficiary: {
V1: {
parents: 0,
interior: {
X1: {
AccountId32: {
network: 'Any',
id: u8aToHex(decodeAddress("<rockmine address account>")),
},
},
},
},
},
assets: {
V1: [
{
id: {
Concrete: {
parents: 0,
interior: 'Here',
},
},
fun: {
Fungible: 100000000000,
},
},
],
},
feeAssetItem: 0,
weightLimit: 'Unlimited',
}
const res = await provider.customExtrinsic({
pallet,
method,
body,
})
From Rococo to Rockmine using body as an array:
const pallet = "xcmPallet"
const method = "limitedTeleportAssets"
const body = [
// dest
{
V1: {
parents: 0,
interior: {
X1: {
Parachain: 1000,
},
},
},
},
// beneficiary
{
V1: {
parents: 0,
interior: {
X1: {
AccountId32: {
network: 'Any',
id: u8aToHex(decodeAddress("<rockmine address account>")),
},
},
},
},
},
// assets
{
V1: [
{
id: {
Concrete: {
parents: 0,
interior: 'Here',
},
},
fun: {
Fungible: 100000000000,
},
},
],
},
// feeAssetItem
0,
// weigthLimit
'Unlimited',
]
const res = await provider.customExtrinsic({
pallet,
method,
body,
})
From Rockmine to Rococo:
command:
npx ts-node src/examples/custom-extrinsic/teleport-parachain-to-relay.ts
manually:
const pallet = 'xcmPallet'
const method = 'limitedTeleportAssets'
const body = {
dest: {
V1: {
parents: 1,
interior: 'Here',
},
},
beneficiary: {
V1: {
parents: 0,
interior: {
X1: {
AccountId32: {
network: 'Any',
id: u8aToHex(decodeAddress('<rococo address account>')),
},
},
},
},
},
assets: {
V1: [
{
id: {
Concrete: {
parents: 1,
interior: 'Here',
},
},
fun: {
Fungible: 100000000000,
},
},
],
},
feeAssetItem: 0,
weightLimit: 'Unlimited',
}
const res = await provider.customExtrinsic({
pallet,
method,
body,
})
From this local network example, to set an asset on trappist as multilocation:
command:
npx ts-node src/examples/custom-extrinsic/mark-asset-as-multilocation.ts
manually:
const pallet = "assetRegistry"
const method = "registerReserveAsset"
const body = {
assetId: 1, // local asset id
assetMultiLocation: {
parents: 1,
interior: {
X3: [
{
Parachain: 1000,
},
{
PalletInstance: 50,
},
{
GeneralIndex: 1,
},
],
},
},
}
const res = await provider.customExtrinsic({
asSudo: true,
pallet,
method,
body,
})
xcm sdk is also a command-line interface tool that helps you to transfer and teleport assets between chains.
install:
npm i -g xcm-sdk
There are 4 commands availables:
xcm-sdk limitedReserveTransferAssets [..args]
xcm-sdk reserveTransferAssets [..args]
xcm-sdk teleportAssets [...args]
xcm-sdk limitedTeleportAssets [..args]
commands:
args:
Arg | Meaning | Description |
---|---|---|
--dest | destination | The destination to transfer the asset. If you want to transfer asset from relaychain to a parachain set 'Parachain'. Default 'Here'. |
--destP | Destination Parents | 0 is default, 1 when you want to transfer from parachain to relaychain |
--destV | Destination Value | The destination value, for example a parachain id |
--ben | Beneficiary | beneficary target, an accountId32 |
--benV | Beneficiary Value | The beneficiary value, account address to send the asset |
--a | Amount | token amount to transfer |
--assetId | Asset Id | AssetId to transfer from parachain, make sure the parchain support the asset and the sender account have enough asset to transfer |
--wl | Weight Limit | Optional, only for limited methods. Set the maximum weight for the extrinsic |
Running the unit tests.
npm run test
Running the test coverage.
npm run test:cov
See Changelog for more information.
Contributions welcome! See Contributing.
Licensed under the MIT - see the LICENSE file for details.