Sui Interchain Token Service (ITS)

Axelar’s Interchain Token Service (ITS) is live on the Sui blockchain to allow for the integration of coins on Sui to be sent to/from other ecosystems connected to the Axelar Network. ITS allows teams to deploy fresh new fungible Interchain Tokens as well as integrate custom tokens that want to go cross-chain. ITS is already live on many different EVM and non-evm chains, so that you can send your Sui coins to and from those chains.

The official Sui integration codebase can be found here

💡

The simplest way to use ITS is through Axelar’s ITS Portal, which also supports Sui. The ITS Testnet Portal.

Coins that are integrated with ITS can either be done through registering canonically with ITS on Sui and then using deploy remote interchain token to deploy wrapped version of the canonically registered coin. Or by leveraging ITS’s link coin functionality to connect custom deployed tokens between two different chains.

A tokenId is a unique identifier for an ITS integration. Since ITS is a permissionless service, anyone can, in theory, integrate a deployed coin. The tokenId differentiates between the potentially many different integrations of a coin with ITS.

The module for the Sui coin id can be found here.

A TokenId is a wrapper of a coin’s address.

public struct TokenId has a copy, drop, store {
id: address,
}

The coin info defines the CoinInfo type, which stores information about a coin:

The following fields are available for CoinInfo

  1. name: The name of the coin.
  2. symbol: The symbol of the coin.
  3. decimals: The amount of decimals the coin can hold.
  4. metadata: The metadata for the coin.

💡

Since coins are u64, some conversion might need to happen when receiving coins, as decimals of 18 are too large for Sui to handle.

public struct CoinInfo<phantom T> has store {
name: String,
symbol: ascii::String,
decimals: u8,
metadata: Option<CoinMetadata<T>>,
}

You can run one of two factory functionalities to create a new Coin Info module for your coin.

This will create new Coin Info based on the given name, symbol, and decimals. The selection alongside the coin type will result in a unique TokenId.

public fun from_info<T>(name: String, symbol: ascii::String, decimals: u8): CoinInfo<T> {
CoinInfo {
name,
symbol,
decimals,
metadata: option::none(),
}
}

An example of how to register a new Coin Info with from_info() can be found here

This will create a new coin info from the given CoinMetadata object. This can only be done once per coin since there is only one CoinMetadata per Coin.

public fun from_metadata<T>(metadata: CoinMetadata<T>): CoinInfo<T> {
CoinInfo {
name: metadata.get_name(),
symbol: metadata.get_symbol(),
decimals: metadata.get_decimals(),
metadata: option::some(metadata),
}
}

The Interchain Transfer Ticket contains a unique type to be sent for each transfer, holding all the info required for an interchain transfer. It includes the following fields:

  1. token_id: The id of the coin being sent cross-chain.
  2. balance: A wrapped balance object representing the coin amount to be transferred, which is later converted to a numeric amount.
  3. source_address: The address initiating the transfer.
  4. destination_chain: The name of the blockchain where the coin is being sent to.
  5. destination_address: The receive address on the destination chain.
  6. metadata: Additional executable data to be sent with the coin.
  7. version: The version of ITS that is being used for this transfer.
public struct InterchainTransferTicket<phantom T> {
token_id: TokenId,
balance: Balance<T>,
source_address: address,
destination_chain: String,
destination_address: vector<u8>,
metadata: vector<u8>,
version: u64,
}

To create an InterchainTransferTicket, you can trigger the prepare_interchain_transfer_ticket() on ITS.

public fun prepare_interchain_transfer<T>(
token_id: TokenId,
coin: Coin<T>,
destination_chain: String,
destination_address: vector<u8>,
metadata: vector<u8>,
source_channel: &Channel,
): InterchainTransferTicket<T> {
interchain_transfer_ticket::new<T>(
token_id,
coin.into_balance(),
source_channel.to_address(),
destination_chain,
destination_address,
metadata,
VERSION,
)
}

A treasury cap reclaimer is a capability that allows the reclaimer to reclaim the treasury cap of a coin. It is used to reclaim the treasury cap of a coin when the coin is unregistered from ITS.

The Phantom type T is the type of the token that the TreasuryCap is being reclaimed for.

public struct TreasuryCapReclaimer<phantom T> has key, store {
id: UID,
/// The `token_id` for which the `TreasuryCap` is being stored by ITS
token_id: TokenId,
}

Edit on GitHub