Skip to content

XC-20s and Cross-Chain Assets

Cross-Chain Assets Precompiled Contracts Banner


The Cross-Consensus Message (XCM) format defines how messages can be sent between interoperable blockchains. This format opens the door to transfer messages and assets (Substrate assets) between Moonbeam/Moonriver and the relay chain or other parachains in the Polkadot/Kusama ecosystems.

Substrate assets are natively interoperable. However, developers need to tap into the Substrate API to interact with them, making the developer experience unideal, especially for those from the Ethereum world. Consequently, to help developers tap into the native interoperability that Polkadot/Kusama offers, Moonbeam introduced the concept of XC-20s.

XC-20s are a unique asset class on Moonbeam. It combines the power of Substrate assets (native interoperability) but allows users and developers to interact with them through a familiar ERC-20 interface via a precompile contract (Ethereum API). Moreover, developers can integrate XC-20s with regular Ethereum development frameworks or dApps.

Moonbeam XC-20 XCM Integration With Polkadot

XC-20 assets will be differentiated by having xc prepended to their name. For example, Polkadot's DOT representation on Moonbeam is known as xcDOT and Kusama's KSM representation on Moonriver is xcKSM. Please note that XC-20 precompiles do not support cross-chain transfers within the contract itself, and this is intentionally done to stay as close as possible to the standard ERC-20 interface. Cross-chain transfer of XC-20s are done using the X-Tokens pallet.

XC-20s need to be registered and linked to another asset in the ecosystem before being used. This is done through a whitelisting process via a democracy proposal. If you are interested in testing XCM features in our TestNet, please contact us through our Discord Server. For more information on XCM, you can check out the XCM Overview page of our documentation.

Current XC-20 Assets

The current list of available XC-20 assets per network is the following:

Origin Symbol XC-20 Address
Polkadot xcDOT 0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080
Acala xcaUSD 0xfFfFFFFF52C56A9257bB97f4B2b6F7B2D624ecda
Acala xcACA 0xffffFFffa922Fef94566104a6e5A35a4fCDDAA9f

*You can check each Asset ID from the Assets tab on Polkadot.js Apps

Origin Symbol XC-20 Address
Relay Chain Alphanet xcUNIT 0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080
Basilisk Alphanet xcBSX 0xFFfFfFfF4d0Ff56d0097BBd14920eaC488540BFA
Bifrost Alphanet xcBNC 0xFffFFFfF1FAE104Dc4C134306bCA8e2E1990aCfd
Calamari Alphanet xcKMA 0xFFffFffFA083189f870640b141ae1E882c2b5bad
Crust/Shadow Alphanet xcCSM 0xffFfFFFf519811215E05eFA24830Eebe9c43aCD7
Karura Alphanet xcKAR 0xFfFFFFfF08220AD2E6e157f26eD8bD22A336A0A5
Karura Alphanet xckUSD 0xFfFffFFfa1B026a00FbAA67c86D5d1d5BF8D8228
Khala Alphanet xcPHA 0xffFfFFff8E6b63d9e447B6d4C45BDA8AF9dc9603
Kintsugi Alphanet xcKINT 0xFFFfffff27C019790DFBEE7cB70F5996671B2882
Kintsugi Alphanet xckBTC 0xFffFfFff5C2Ec77818D0863088929C1106635d26
Litentry Alphanet xcLIT 0xfffFFfFF31103d490325BB0a8E40eF62e2F614C0
Parallel Heiko Alphanet xcHKO 0xffffffFF394054BCDa1902B6A6436840435655a3
Statemine Alphanet xcMRMRK 0xFFffffFfd2aaD7f60626608Fa4a5d34768F7892d

*You can check each Asset ID from the Assets tab on Polkadot.js Apps

This guide will show you how to retrieve the available XC-20s and calculate their precompile addresses for the Moonbase Alpha TestNet using Polkadot.js Apps. In addition, you will learn how to interact with an XC-20 precompile using Remix.

XC-20 vs ERC-20

Although XC-20s and ERC-20s are very similar, some distinct differences are to be aware of.

First and foremost, XC-20s are Substrate-based assets, and as such, they are also subject to be directly impacted by Substrate features such as governance. In addition, XC-20s transactions done via the Substrate API won't be visible from EVM-based block explorers such as Moonscan. Only transactions done via the Ethereum API are visible through such explorers.

Nevertheless, XC-20s can be interacted with through an ERC-20 interface, so they have the additional benefit of being accessible from both the Substrate and Ethereum APIs. This ultimately provides greater flexibility for developers when working with these types of assets and allows seamless integrations with EVM-based smart contracts such as DEXs, lending platforms, among others.

Retrieve List of Cross-Chain Assets

To fetch a list of the XC-20s currently available on the Moonbase Alpha TestNet, head to Polkadot.js Apps and make sure you're connected to Moonbase Alpha. Then click on the Developer tab and select Chain State from the dropdown. To query the available XC-20s, you can follow these steps:

  1. From the selected state query dropdown, choose assets
  2. Select the asset extinsic
  3. Disable the include option slider
  4. Send the query by clicking on the + button

Fetch list of cross-chain assets

The result will display the asset ID along with some additional information for all of the registered XC-20s on Moonbase Alpha.

Retrieve Cross-Chain Assets Metadata

To quickly get more information about a specific XC-20 such as the name, symbol and multi location of the asset, you can use the metadata extrinsic to return metadata:

  1. From the selected state query dropdown, choose assets
  2. Select the metadata extinsic
  3. Enable the include option slider
  4. Enter in the asset ID that was returned from calling the asset extrinsic. Please note that if you copy and paste the asset ID with the commas, the commas will automatically be removed and the number might be cut off. Make sure it's the exact same number as the ID. For this example, you can use asset ID 42259045809535163221576417993425387648
  5. Send the query by clicking on the + button

Get asset metadata

With the results from the metadata, you can see that the asset ID corresponds to the VUNIT XC-20.

Calculate Precompile Addresses

Now that you have retrieved a list of the available XC-20s, before you can interact with them via the precompile, you need to derive the precompile address from the asset ID.

The XC-20 precompile address is calculated using the following:

address = "0xFFF..." + DecimalToHex(AssetId)

Given the above calculation, the first step is to take the u128 representation of the asset ID and convert it to a hex value. You can use your search engine of choice to look up a simple tool for converting decimals to hex values. For asset ID 42259045809535163221576417993425387648, the hex value is 1FCACBD218EDC0EBA20FC2308C778080.

Since Ethereum addresses are 40 characters long, you will need to prepend Fs to the hex value until the address has 40 characters.

The hex value that was already calculated is 32 characters long, so prepending 8 Fs to the hex value will give you the 40 character address you need to interact with the XC-20 precompile. For this example, the full address is 0xFFFFFFFF1FCACBD218EDC0EBA20FC2308C778080.

Asset precompiles can only fall between 0xFFFFFFFF00000000000000000000000000000000 and 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.

Now that you've calculated the XC-20 precompile address, you can use the address to interact with the XC-20 like you would with any other ERC-20 in Remix.

The ERC-20 Interface

The ERC20.sol interface on Moonbeam follows the EIP-20 Token Standard which is the standard API interface for tokens within smart contracts. The standard defines the required functions and events that a token contract must implement to be interoperable with different applications.

The interface includes the following functions:

  • name() — read-only function that returns the name of the token
  • symbol() — read-only function that returns the symbol of the token
  • decimals() — read-only function that returns the decimals of the token
  • totalSupply() — read-only function that returns the total number of tokens in existence
  • balanceOf(address who) — read-only function that returns the balance of the specified address
  • allowance(address owner, address spender) — read-only function that checks and returns the amount of tokens that an owner is allowed to a spender
  • transfer(address to, uint256 value) — transfers a given amount of tokens to a specified address and returns true if the transfer was successful
  • approve(address spender, uint256 value) — approves the provided address to spend a specified amount of tokens on behalf of msg.sender. Returns true if successful
  • transferFrom(address from, address to, uint256 value) — transfers tokens from one given address to another given address and returns true if successful


The ERC-20 standard does not specify the implications of multiple calls to approve. Changing an allowance with this function numerous times enables a possible attack vector. To avoid incorrect or unintended transaction ordering, you can first reduce the spender allowance to 0 and then set the desired allowance afterward. For more details on the attack vector, you can check out the ERC-20 API: An Attack Vector on Approve/TransferFrom Methods overview

The interface also includes the following required events:

  • Transfer(address indexed from, address indexed to, uint256 value) - emitted when a transfer has been performed
  • Approval(address indexed owner, address indexed spender, uint256 value) - emitted when an approval has been registered

Checking Prerequisites

To approve a spend or transfer XC-20s via the XC-20 precompile, you will need:

Interact with the Precompile Using Remix

You can interact with the XC-20 precompile using Remix. First, you will need to add the ERC-20 interface to Remix:

  1. Get a copy of ERC20.sol
  2. Paste the file contents into a Remix file named IERC20.sol

Load the interface in Remix

Compile the Contract

Once you have the ERC-20 interface loaded in Remix, you will need to compile it:

  1. Click on the Compile tab, second from top
  2. Compile the IER20.sol file

Compiling IERC20.sol

If the interface was compiled successfully, you will see a green checkmark next to the Compile tab.

Access the Contract

Instead of deploying the ERC-20 precompile, you will access the interface given the address of the XC-20 precompile:

  1. Click on the Deploy and Run tab directly below the Compile tab in Remix. Please note the precompiled contract is already deployed
  2. Make sure Injected Web3 is selected in the Environment dropdown. Once you select Injected Web3, you might be prompted by MetaMask to connect your account to Remix
  3. Make sure the correct account is displayed under Account
  4. Ensure IERC20 - IERC20.sol is selected in the Contract dropdown. Since this is a precompiled contract, there is no need to deploy any code. Instead we are going to provide the address of the precompile in the At Address Field
  5. Provide the address of the XC-20 precompile calculated in the Calculate Precompile Address section, 0xFFFFFFFF1FCACBD218EDC0EBA20FC2308C778080, and click At Address

Access the address


Optionally, you can checksum the XC-20 precompile address by going to your search engine of choice and searching for a tool to checksum the address. Once the address has been checksummed, you can use it in the At Address field instead.

The IERC20 precompile for the XC-20 will appear in the list of Deployed Contracts. Now you can feel free to call any of the standard ERC-20 functions to get information about the XC-20 or transfer the XC-20.

Interact with the precompile functions

To learn how to interact with each of the functions, you can check out the ERC-20 Precompile guide and modify it for interacting with the XC-20 precompile.