Skip to content

Pallet Token Gateway

This is an ISMP module that allows standalone chains or parachains send and receive cross chain asset transfers.

Config

Let's look at the pallet specific components of the configuration trait:

  • Dispatcher: The should be an implementation of the IsmpDispatcher, it will be used by this pallet dispatch cross-chain requests.

  • Assets: This type should be configured with a component that implements the following interfaces, typically this will be pallet-assets
  • Currency: This type should be configured with a component that implements Currency interface, typically pallet-balances.

  • AssetAdmin: This pallet has some functionality for creating new assets. The account configured for this type would be the asset admin and also be responsible for paying the asset registration fees, therefore it should be funded before attempting to create any assets.

  • NativeAssetId: A constant value that represents the identifier of the native asset.

  • Decimals: A constant that represents the precision of the native currency.

  • EvmToSubstrate: A type that allows conversion of an EVM account to a substrate account.

Calls

  • teleport: This call is used to initialize a cross-chain asset transfer. Any provided assets are custodied by the pallet and a cross-chain request is dispatched to the destination chain.
  • set_token_gateway_addresses: This priviledged call is used to set the token gateway address for EVM chains.
  • create_erc6160_asset: This call dispatches a request to Hyperbridge to create multi chain native assets on token gateway deployments
  • update_erc6160_asset: This call dispatches a request to Hyperbridge to update multi chain native assets on token gateway deployments
  • update_asset_precision: This priviledged call is used to set or update the precision of an asset deployed on some remote chains.

Priviledged calls must be dispatched by CreateOrigin.

Integrating the pallet into the Runtime

The first step is to implement the pallet config for the runtime.

use frame_support::parameter_types;
use ismp::module::IsmpModule;
use ismp::router::IsmpRouter;
use pallet_token_gateway::types::NativeAssetLocation;
 
parameter_types! {
    // Set the correct decimals for the native currency
    pub const Decimals: u8 = 12;
}
 
/// A constant value that represents the native asset
/// `NativeAssetLocation::Local` indicates the native asset is custodied locally.
/// `NativeAssetLocation::Remote` indicates that the native asset is custodied on some remote chain.
pub struct NativeAssetId;
 
impl Get<NativeAssetLocation<u32>> for NativeAssetId {
	fn get() -> NativeAssetLocation<u32> {
		NativeAssetLocation::Local(0)
	}
}
 
/// Should provide an account that is funded and can be used to pay for asset creation
pub struct AssetAdmin;
 
impl Get<AccountId> for AssetAdmin {
	fn get() -> AccountId {
		Treasury::account_id()
	}
}
 
impl pallet_token_gateway::Config for Runtime {
    // configure the runtime event
    type RuntimeEvent = RuntimeEvent;
    // Configured as Pallet Ismp 
    type Dispatcher = Ismp;
    // Configured as Pallet Assets
	type Assets = Assets;
    // Configured as Pallet balances
	type Currency = Balances;
    // AssetAdmin account
    type AssetAdmin = AssetAdmin;
    // Origin allowed to create and update assets
    type CreateOrigin = frame_system::EnsureSigned<AccountId>
    // The Native asset Id
	type NativeAssetId = NativeAssetId;
    // The precision of the native asset
    type Decimals = Decimals;
    // An implementation that converts an evm account to a substrate account
    type EvmToSubstrate = ();
}
 
// Add the token gateway pallet to your ISMP router
#[derive(Default)]
struct Router;
impl IsmpRouter for Router {
    fn module_for_id(&self, id: Vec<u8>) -> Result<Box<dyn IsmpModule>, anyhow::Error> {
        let module = match id.as_slice() {
            id if TokenGateway::is_token_gateway(&id) => Box::new(TokenGateway::default()),
            _ => Err(Error::ModuleNotFound(id))?
        };
        Ok(module)
    }
}

Setting up Token Gateway

Setting up token gateway for use involves a few simple steps.

  1. Registering token gateway addresses on EVM chains
    The pallet needs to know the addresses of the token gateway contracts on EVM chains, so it can validate the source of incoming messages.

    This requires dispatching the set_token_gateway_addresses extrinsic from AdminOrigin configured in pallet-ismp.
    This call accepts a map of StateMachine to Vec<u8>, this is only neccessary for validating messages coming from EVM chains, all substrate chains use a static token gateway address. Find the required addresses here

  2. Registering assets on token gateway deployments
    For transfer of the native currency and other assets issued on your chain through token gateway, those assets need to be registered on the token gateway asset registry on hyperbridge The process of registering assets involves dispatching a request to create the asset on hyperbridge after which hyperbridge dispatches requests to deploy the asset on all chains specified in the initial request.

    The types involved in asset creation are described below:

pub struct GatewayAssetRegistration {
	/// The asset name
	pub name: BoundedVec<u8, ConstU32<20>>,
	/// The asset symbol
	pub symbol: BoundedVec<u8, ConstU32<20>>,
	/// The list of chains to create the asset on
	pub chains: Vec<StateMachine>,
	/// Minimum balance for the asset, for substrate chains,
	pub minimum_balance: Option<u128>,
}
 
pub struct AssetRegistration<AssetId> {
	/// The asset must exist locally
	pub local_id: AssetId,
	/// Asset metadata
	pub reg: GatewayAssetRegistration,
    /// A Flag that asserts if this asset is custodied locally
    /// This should be false for all imported assets
	pub native: bool,
	/// Precision of asset on supported chains
	pub precision: BTreeMap<StateMachine, u8>,
}

To register assets on token gateway you need to dispatch the create_erc6160_asset extrinsic with an AssetRegistration<AssetId> object.
The former contains the local asset id and the asset metadata.

GatewayAssetRegistration

This is the core struct that holds the metadata of the asset you need to register.

name: This should be the full name of the asset, it's limited to 20 characters.

symbol: This is the ticker for the asset, also limited to 20 characters. The asset Id will be derived from the keccak256 hash of this value.

chains: This is a vector of the chains you want this asset deployed. It should be only chains supported by hyperbridge network.

minimum_balance: This value is only necessary when you intend to deploy this asset on a substrate chain. It represents the minimum amount of the asset that an account can hold on susbtrate based chains.

A local asset id must be provided for the asset so it ca be mapped to the token gateway asset id.

To register the native currency(pallet-balances) on token gateway you need to set the local_id field in the AssetRegistration<AssetId> to the same constant that was specified as the NativeAssetId in the pallet config along with the required metadata.
Assets can also be updated in the same way by dispatching update_erc6160_asset.

Asset Ids

Token gateway protocol represents assets ids with a 32 byte hash which is derived from the keccak256 hash of the asset's symbol.

Asset Precision

Asset precision is used to note the precision an asset is configured with on a remote chain, this allows the pallet to handle balance conversions correctly. To update or set the precision of an asset on dispatch update_asset_precision with the values that need to be set. The precision for ERC6160 assets deployed on EVM should always be set to 18.

Teleporting assets

To make a crosschain transfer the teleport extrinsic should must be dispatched with the correct parameters.

pub struct TeleportParams<AssetId, Balance> {
	/// Asset Id registered on Hyperbridge
	pub asset_id: AssetId,
	/// Destination state machine
	pub destination: StateMachine,
	/// Receiving account on destination
	pub recepient: H256,
	/// Amount to be sent
	pub amount: Balance,
	/// Request timeout
	pub timeout: u64,
	/// Token gateway address
	pub token_gateway: Vec<u8>,
	/// Relayer fee
	pub relayer_fee: Balance,
    /// Optional call data
    pub call_data: Option<Vec<u8>>,
	/// Redeem erc20 tokens
	pub redeem: bool
}

Let's explore what each parameter holds:

  • asset_id: The local asset id for the asset that should be transferred
  • destination: The destination chain that should receive the funds
  • recepient: The beneficiary account for the funds on the destination. (For EVM chains, the address should be left padded with zeros to fit into the required 32 bytes.)
  • amount: The amount that should be transferred.
  • timeout: The request timeout, this is the time after which the request cannot be delivered to the destination. It should represent the cumulative time for finalization on the source chain and hyperbridge with some additional buffer.
  • token_gateway: The address of the token gateway module on the destination chain.
  • relayer_fee: The amount to be paid to relayers for delivering the request, a value of zero means the dispatcher is responsible for relaying the request.
  • redeem: This value should be set to true if bridging an asset that has an ERC20 deployment on the destination, it should be false in any other case.

Funds from undelivered requests can be recovered by submitting a timeout message for the request through pallet-ismp.