Skip to content

XCM SDK is a tool that provides an interface to send XCM messages for Substrate based blockchains.

License

Notifications You must be signed in to change notification settings

blockcoders/xcm-sdk

Repository files navigation

XCM SDK

CircleCI Coverage Status CodeQL npm npm vulnerabilities License

About

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.

Get Started

Install

npm i xcm-sdk

Usage

// JavaScript
const { Provider } = require("xcm-sdk")

// TypeScript
import { Provider } from "xcm-sdk"

Provider

const provider = new Provider(rpc, sender)
Param Description
rpc rpc endpoint
sender signer of the transaction

Examples

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);

Supported Methods

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)

Methods parameters

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

Disclaimer

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

Rococo examples

If you want to tests in Testnet, you have Rococo.
Get some assets: Rococo faucet

Config

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>,
}

Send Asset from Rococo to Rockmine

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,
  });

Send Asset from RockMine to Rococo

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,
  });

Send Asset from Rococo to Mangata

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,
  });

see token transfered:

Other examples

local network examples

Support for other pallets and methods

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

Examples

Teleport asset

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,
  })

Asset Multilocation

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,
})

CLI Usage

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

CLI examples

See cli examples

Testing

Running the unit tests.

npm run test

Running the test coverage.

npm run test:cov

Change Log

See Changelog for more information.

Contributing

Contributions welcome! See Contributing.

Collaborators

License

Licensed under the MIT - see the LICENSE file for details.