Skip to content
On this page

Send XCM messages across Paraverse 🪐

You can use our SDK in all three scenarios:

  • Relay chain to Parachain XCM transfer
  • Parachain to Relay chain XCM transfer
  • Parachain to Parachain XCM transfer

Video guide for this section:

xcmPallet

Relay chain to Parachain

Only the to parameter is provided, thus the Relay chain to Parachain scenario will be used.

Builder pattern

js
  await Builder(api)        //Api parameter is optional and can also be ws_url_string
      .to('Basilisk')       // Destination Parachain //You can now add custom ParachainID eg. .to('Basilisk', 2024) or use custom Multilocation
      .amount(amount)       // Token amount
      .address(address)     // AccountId32 or AccountKey20 address or custom Multilocation
      /*.xcmVersion(Version.V1/V2/V3/V4)  //Optional parameter for manual override of XCM Version used in call*/
      .build()              // Function called to build call

Function pattern

js
await paraspell.xcmPallet.transferRelayToPara(
  {
    api?,                 //Api parameter (Optional) + can also be ws_url_string
    destination,          // Destination Parachain or custom Multilocation
    amount,               // Token amount
    to                    // AccountId32 or AccountKey20 address or custom Multilocation
    paraIdTo?,            //Custom destination parachain ID (Optional)
    destApiForKeepAlive?  //Api parameter for keep alive check (Optional)
  }
)

AccountId32 and AccountKey20 addresses can be directly copied from PolkadotJS as our SDK has a handler to convert it into the desired hex string automatically.

Eg. use standard public key 141NGS2jjZca5Ss2Nysth2stJ6rimcnufCNHnh5ExSsftn7U Instead of 0x84fc49ce30071ea611731838cc7736113c1ec68fbc47119be8a0805066df9b2b

To find out more about custom multilocations reffer to the following PR.

Parachain to Relay chain

Only the from parameter is provided, thus the Parachain to Relay chain scenario will be used.

Builder pattern

js
  await Builder(api)            //Api parameter is optional and can also be ws_url_string
      .from('Acala')            // Origin Parachain
      .amount(amount)           // Token amount
      .address(address)         // AccountId32 address or custom Multilocation
      /*.xcmVersion(Version.V1/V2/V3/V4)  //Optional parameter for manual override of XCM Version used in call*/
      .build()                  // Function called to build call

Function pattern

js
await paraspell.xcmPallet.send(
  {
    api?,                 //Api parameter (Optional) + can also be ws_url_string
    origin,               // Origin Parachain
    amount,               // Token amount
    to                    // AccountId32 or AccountKey20 address or custom Multilocation
    paraIdTo?,            //Custom destination parachain ID (Optional)
    destApiForKeepAlive?  //Api parameter for keep alive check (Optional)
  }
)

AccountId32 and AccountKey20 addresses can be directly copied from PolkadotJS as our SDK has a handler to convert it into the desired hex string automatically.

Eg. use standard public key 141NGS2jjZca5Ss2Nysth2stJ6rimcnufCNHnh5ExSsftn7U Instead of 0x84fc49ce30071ea611731838cc7736113c1ec68fbc47119be8a0805066df9b2b

To find out more about custom multilocations reffer to the following PR.

Parachain to Parachain

Both from and to parameters are provided, thus the Parachain to Parachain scenario will be used.

NOTE If you wish to transfer from Parachain that uses long IDs for example Moonbeam you have to add character 'n' the end of currencyID. Eg: .currency(42259045809535163221576417993425387648n) will mean you transfer xcDOT.

Builder pattern

js
  await Builder(api)            //Api parameter is optional and can also be ws_url_string
      .from('Karura')           // Origin Parachain
      .to('Basilisk')           // Destination Parachain //You can now add custom ParachainID eg. .to('Basilisk', 2024) or use custom Multilocation
      .currency({symbol: 'KSM'}) //{id: currencyID} | {symbol: currencySymbol} | {symbol: Native('currencySymbol')} | {symbol: Foreign('currencySymbol')} | {symbol: ForeignAbstract('currencySymbol')} | {multilocation: AssetMultilocationString | AssetMultilocationJson} | {multilocation: Override('Custom Multilocation')} | {multiasset: multilocationJsonArray}
      /*.feeAsset(feeAsset) - Parameter required when using MultilocationArray*/
      .amount(amount)           // Token amount
      .address(address)         // AccountId32 or AccountKey20 address or custom Multilocation
      /*.xcmVersion(Version.V1/V2/V3/V4)  //Optional parameter for manual override of XCM Version used in call*/
      .build()                  // Function called to build call

Function pattern

js
await paraspell.xcmPallet.send(
  {
    api?,                 //Api parameter (Optional) + can also be ws_url_string
    origin,               // Origin Parachain
    currency,             // {id: currencyID} | {symbol: currencySymbol} | {symbol: Native('currencySymbol')} | {symbol: Foreign('currencySymbol')} | {symbol: ForeignAbstract('currencySymbol')} | {multilocation: AssetMultilocationString | AssetMultilocationJson} | {multilocation: Override('Custom Multilocation')} | {multiasset: multilocationJsonArray}
    feeAsset?             // Fee asset select id,
    amount,               // Token amount
    to,                   // AccountId32 or AccountKey20 address or custom Multilocation
    destination,          // Destination Parachain or custom Multilocation
    paraIdTo?,            //Custom destination parachain ID (Optional)
    destApiForKeepAlive?  //Api parameter for keep alive check (Optional)
  }
)

AccountId32 and AccountKey20 addresses can be directly copied from PolkadotJS as our SDK has a handler to convert it into the desired hex string automatically.

Eg. use standard public key 141NGS2jjZca5Ss2Nysth2stJ6rimcnufCNHnh5ExSsftn7U Instead of 0x84fc49ce30071ea611731838cc7736113c1ec68fbc47119be8a0805066df9b2b

To find out more about custom multilocations reffer to the following PR.

Ecosystem Bridges

This section sums up currently available and implemented ecosystem bridges that are offered in the XCM SDK. Implementing cross-ecosystem asset transfers was never this easy!

Polkadot <> Kusama bridge

Latest SDK versions support Polkadot <> Kusama bridge in very native and intuitive way. You just construct the Polkadot <> Kusama transfer as standard Parachain to Parachain scenario transfer.

js
  await Builder(api)            //Api parameter is optional and can also be ws_url_string
        .from('AssetHubPolkadot')  //Either AHP or AHK
        .to('AssetHubKusama')     //Either AHP or AHK
        .currency({symbol: 'DOT'})        // Either KSM or DOT 
        .amount(amount)
        .address(address)
        .build()

Polkadot <> Ethereum bridge (Snowbridge)

Just like Polkadot <> Kusama bridge the Snowbridge is implemented in as intuitive and native form as possible. The implementations for Polkadot -> Ethereum and Ethereum -> Polkadot differ due to different architecure so we will mention both scenarios.

Polkadot -> Ethereum transfer

js
await Builder(api)
          .from('AssetHubPolkadot')
          .to('Ethereum')           
          .currency({symbol: 'WETH'})   //Any supported asset by bridge eg. WETH, WBTC, SHIB and more - {symbol: currencySymbol} | {id: currencyID}
          .amount(amount)
          .address(eth_address)  //AccountKey20 recipient address
          .build()

Ethereum -> Polkadot transfer

js
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

await EvmBuilder(provider)      //Ethereum provider
  .to('AssetHubPolkadot')
  .amount(amount)
  .currency({symbol: 'WETH'})    //Any supported asset by bridge eg. WETH, WBTC, SHIB and more - {symbol: currencySymbol} | {id: currencyID}
  .address(address)   //AccountID32 recipient address
  .signer(signer)     //Ethereum signer address
  .build();

Batch calls

You can batch XCM calls and execute multiple XCM calls within one call. All three scenarios (Para->Para, Para->Relay, Relay->Para) can be used and combined.

js
await Builder(/*node api||ws_url_string - optional*/)
      .from(NODE) //Ensure, that origin node is the same in all batched XCM Calls.
      .to(NODE_2) //Any compatible Parachain
      .currency(currency) //Currency to transfer (If Para->Para), otherwise you do not need to specify .currency()
      .amount(amount) 
      .address(address | Multilocation object)
      .addToBatch()

      .from(NODE) //Ensure, that origin node is the same in all batched XCM Calls.
      .to(NODE_3) //Any compatible Parachain
      .currency(currency) //Currency to transfer (If Para->Para), otherwise you do not need to specify .currency()
      .amount(amount)
      .address(address | Multilocation object)
      .addToBatch()
      .buildBatch({ 
          // This settings object is optional and batch all is the default option
          mode: BatchMode.BATCH_ALL //or BatchMode.BATCH
      })

Query existential deposit

Latest SDK versions now offer ability to query existential deposit on implemented chains using simple call:

ts
//PJS
import { getExistentialDeposit } from "@paraspell/sdk";
//PAPI
import { getExistentialDeposit } from "@paraspell/sdk/papi";

const ed = getExistentialDeposit('Acala')

XCM Transfer info

You can now query all important information about your XCM call including information about fees (If your balance is sufficient to transfer XCM message) and more.

ts
//PJS
import { getTransferInfo, getBalanceForeign, getBalanceNative, getOriginFeeDetails } from "@paraspell/sdk";
//PAPI
import { getTransferInfo, getBalanceForeign, getBalanceNative, getOriginFeeDetails } from "@paraspell/sdk/papi";

//Get balance of foreign currency
await getBalanceForeign({address, node, currency /*- {id: currencyID} | {symbol: currencySymbol} | {symbol: Native('currencySymbol')} | {symbol: Foreign('currencySymbol')} | {symbol: ForeignAbstract('currencySymbol')} | {multilocation: AssetMultilocationString | AssetMultilocationJson}*/, api /* api/ws_url_string optional */})

//Get balance of native currency
await getBalanceNative({address, node, api /* api/ws_url_string optional */})

//Get fee information regarding XCM call
await getOriginFeeDetails({from, to, currency /*- {id: currencyID} | {symbol: currencySymbol} | {symbol: Native('currencySymbol')} | {symbol: Foreign('currencySymbol')} | {symbol: ForeignAbstract('currencySymbol')} | {multilocation: AssetMultilocationString | AssetMultilocationJson}*/, amount, originAddress, destinationAddress, api /* api/ws_url_string optional */, feeMargin /* 10% by default */})

//Retrieves the asset balance for a given account on a specified node.
await getAssetBalance({address, node, currency /*- {id: currencyID} | {symbol: currencySymbol} | {symbol: Native('currencySymbol')} | {symbol: Foreign('currencySymbol')} | {symbol: ForeignAbstract('currencySymbol')} | {multilocation: AssetMultilocationString | AssetMultilocationJson}*/, api /* api/ws_url_string optional */});

//Get all the information about XCM transfer
await getTransferInfo({from, to, address, destinationAddress, currency /*- {id: currencyID} | {symbol: currencySymbol} | {symbol: Native('currencySymbol')} | {symbol: Foreign('currencySymbol')} | {symbol: ForeignAbstract('currencySymbol')} | {multilocation: AssetMultilocationString | AssetMultilocationJson}*/, amount, api /* api/ws_url_string optional */})

Developer experience

Builder pattern experience

When developing with the Builder pattern, the developer is guided by the typescript and thus knows which parameter can be added next. This increases the developer experience and makes SDK easier to use. builder

Control messages into the console

Once the call is being constructed developer is warned about major details regarding the call into the console. This way they can ensure, that the call they wanted to create is being created. 212045110-c001fcb7-8cc2-421c-9cd0-6d8205b3b11f