Module 9 - Building an OmniChain Token on Router Chain
Cloning the Repository
For Understanding how to Write a Smart Contract for an OmniChain Token on Router Chain, Clone the Below Repository in your Local Machine -
This will Clone the OmniChain Token Contract in the Current Directory.
File Structure
The cosmwasm Directory consists of Smart Contracts written on Router Chain and evm Directory consists of the Smart Contracts written on Polygon and Arbitrum.
Directorycosmwasm/
Directory.cargo/
config
Directorycontracts/
Directoryxerc20/
Directoryexamples/
schema.rs
Directorysrc/
contract.rs
execution.rs
handle_sudo_execution.rs
lib.rs
modifiers.rs
query.rs
state.rs
tests.rs
Cargo.toml
Directorypackages/
Directorynew-crosstalk-sample/
Directorysrc/
lib.rs
xerc20.rs
Cargo.toml
Directoryscripts/
build.sh
.editorconfig
.gitignore
Cargo.lock
Cargo.toml
rustfmt.toml
Directoryevm/
Directorycontracts/
XERC20.sol
Directorydeployment/
Directoryconfig/
xerc20.json
deployments.json
Directorytasks/
Directorydeploy/
DeployOnEachChain.ts
XERC20.ts
enroll_added_chain.ts
enroll_on_chain.ts
index.ts
storeDeployments.ts
Directorytest/
PingPong.ts
Directoryutils/
chain.ts
onEachChain.ts
types.ts
utils.ts
.gitignore
hardhat.config.ts
package-lock.json
package.json
README.md
tsconfig.json
yarn.lock
CosmWasm-side
Contract.rs
Imports and Constants
This Section Imports necessary Modules and Defines Constants for the Contract Name and Version. It uses various CosmWasm Libraries and Router Protocol Bindings.
Instantiate Function
The instantiate function is called when the Contract is first Deployed. It -
Saves the Contract Owner.
Sets the Contract Version.
Creates a new CW20 Token as the underlying Token for Omnichain functionality.
Response::new() - Creates a new empty Response.
add_submessage() - Adds a Submessage to be executed after the Current Transaction.
SubMsg - A Submessage that can Trigger a reply.
WasmMsg::Instantiate - Message Type for Creating a new Contract Instance.
The CW20 Token is Created via a Submessage, allowing for Asynchronous handling of the Token Creation. The Contract will receive a Reply when this Succeeds (via INSTANTIATE_REPLY_ID).
Reply Function
The Reply function handles Responses from Submessages. It has Two Main Cases -
INSTANTIATE_REPLY_ID - Handles the Reply from CW20 Token Creation, Saving the new Token’s Address.
CREATE_I_SEND_REQUEST - Handles Replies from cross-chain Send Requests, including Logging Debug Information.
Key features -
It’s an entry point for handling submessage replies.
Includes Extensive Debug Logging for Development Purposes.
Uses the Router’s Specific Query and Message Types (RouterQuery, RouterMsg).
Has a TODO Note about nonce handling which suggests Incomplete cross-chain functionality.
Returns error for Unknown Reply IDs to prevent Unhandled Cases.
Execute Function
This is a Standard CosmWasm execute entry Point. It acts as a Thin Wrapper around handle_execute. It uses Router-specific Types (RouterQuery, RouterMsg) Suggesting cross-chain functionality.
This function is the entry point for all Execute Messages sent to the Contract. It Delegates the actual handling to a Separate handle_execute function.
Sudo Function
The Sudo function handles Privileged Operations, typically Called by the Router Protocol Infrastructure. It has Two Main Operations -
HandleIReceive - Processes Incoming cross-chain Requests. It processes - who sent the Request, Which Chain it came from, A Unique Identifier, and the actual Payload Data.
HandleIAck - Handles Acknowledgments for Outgoing cross-chain Requests. It processes - The Request being Acknowledged, whether it succeeded, any returned Data, and any refund Amount.
It Delegates actual handling to Two Separate functions -
handle_sudo_request for incoming requests.
handle_sudo_ack for acknowledgments.
Migrate Function
This function allows for Upgrading the Contract. It Checks that the Migration is from the Correct Contract Type and Logs Information about the Migration.
Query Function
This function handles all Query Requests to the Contract, Delegating to a Separate handle_query function.
This Contract serves as the Main Entry Point for the Omnichain Token, Coordinating between Local Token Operations (via the CW20 token) and cross-chain Operations (via Router Protocol).
execution.rs
This is an Implementation of a cross-chain (omnichain) Token System that allows Tokens to move Between different Blockchains. Think of it as a Bridge that Connects Multiple Blockchain Networks, allowing Users to send Tokens from one Chain to another.
Imports and Constants
These are the Core Types and functions from cosmwasm_std, which is the main Library for Building CosmWasm Smart Contracts.
These Imports come from the Router Protocol Bindings and the new_crosstalk_sample Contract.
Execute Handler (Main Entry Point)
This is like a Traffic Controller that directs different Operations to their Specific Handlers. It Supports -
System Configuration (chain IDs, contract addresses)
This function Matches the Message (msg) with the different Operations, like setting chain types, updating ownership, minting, etc.
Each ExecuteMsg variant Calls a Different function to handle the Logic for that particular Operation.
Administrative Functions
The Contract allows Changing the Owner, but Only the Current Owner can perform this Operation -
Controls who has Administrative Rights
Only Current Owner can Transfer Ownership
Critical for Security Governance
Ownership check - The Modifier is_owner_modifier ensures only the Current Owner can change Ownership.
Update state - The new Owner’s Address is saved in the Contract’s State.
The function set_white_listed_contracts is responsible for Setting a list of Whitelisted Contracts on different Chains in the Contract’s State. Only the Contract Owner is allowed to execute this Operation, which is enforced by the is_owner_modifier function.
This Line Calls the is_owner_modifier function to ensure that only the Contract Owner can execute this function.
add_attribute(“action”, “SetCustodyContracts”) - Adds a Custom Attribute to the Response, indicating that the action performed was the Setting of Custody Contracts (whitelisting contracts). This is Useful for Debugging, Logging, and Transaction Tracking.
When the SetChainTypes Message is received, the Contract Sets the Mapping for different Chain Types -
Purpose - Stores Information about Different Chain Types in the CHAIN_TYPE_MAPPING.
Ownership check - The function begins by Verifying that the Caller is the Contract Owner with is_owner_modifier.
Chain Type Mapping - It Loops over chain_type_info to Store each Chain ID and Type Pair.
Event - Emits an event SetChainTypeInfo for Tracking.
The function set_xerc20_addr is responsible for Setting the Address of a cross-chain Token (referred to as XERC20 in this case) in the Contract’s Storage. This function ensures that only the Contract Owner can Execute this Operation, which is enforced by the is_owner_modifier. Let’s go through each part of the function -
This Line Calls the is_owner_modifier function to ensure that the Sender (identified in info.sender) is the Owner of the Contract.
CROSS_CHAIN_TOKEN - This is a Constant or State Variable that refers to the Storage Location where the address of the cross-chain Token is saved. The save function Stores the provided value (the new XERC20 token address, addr) into the Contract’s Storage.
Event - CosmWasm Contracts can emit events that are useful for External Systems (like blockchain explorers) to Track specific actions Within the Contract. This Line Creates an event called SetXERC20Addr.
CHAIN_ID is a variable in the Contract’s State that represents the ID of the Blockchain Network the Contract is working with.
save(deps.storage, &id) - This stores the new CHAIN_ID in the Contract’s Persistent Storage. deps.storage is used to access the Contract’s Storage, and id is the new Chain ID value Passed to the function.
Token Operations
This function Mints new Tokens to a specific Recipient Address -
OwnershipCheck - Only the Contract Owner can Mint new Tokens.
Recipient Validation - The Recipient’s address is Validated to ensure it’s a Valid address on-chain.
Mint Message - The Contract creates a Mint Message using the cw20_base standard for Minting Tokens.
Execute the Mint - It sends a Message to the Contract responsible for handling cross-chain Tokens (xerc20_token) to Mint Tokens for the Recipient.
Cross-Chain Transfer (Most Complex Operation)
This is the Core of the Omnichain functionality, enabling Token Transfers across Chains. Here’s what happens -
Burn Token Locally - Before Transferring Tokens cross-chain, the Amount is Burned from the Sender’s Balance using the BurnFrom message.
Prepare cross-chain Message - The Payload contains the Recipient Address and amount to Transfer, which is encoded.
Oracle Gas Prices - Fetches Gas Prices from an Oracle for the Source and Destination Chains.
Whitelisted Contract Check - Ensures that the Destination Chain’s Contract is Whitelisted before Proceeding with the Transfer.
RouterMsg - The RouterMsg::CrosschainCall sends the Transfer Request through Router Protocol to the Destination Chain, with Metadata like Gas Limits and acknowledgment Types.
handle_sudo_execution.rs
The handle_sudo_execution.rs file handles two Important Parts of the Omnichain Token Project -
handle_sudo_request
handle_sudo_ack
This function is responsible for handling Incoming Sudo Requests, Verifying them, and Minting Tokens on the Target Chain for the Recipient.
This function handles the Acknowledgment of a Sudo Request Execution, Logging whether the Execution was Successful.
lib.rs
Imports and Constants
The above Code Implementation is a Standard lib.rs file of a CosmWasm Project.
modifiers.rs
The file modifiers.rs Defines Two functions (is_owner_modifier and is_white_listed_modifier) that act as “modifiers” or Checks Before allowing Certain Operations to proceed. These functions help ensure that Only Authorized Users (the owner or whitelisted contracts) can Perform Specific Actions on the Contract.
Imports and Constants
In the above Code, We’re Implementing router_wasm_bindings package along with cosmwasm_std package.
The is_owner_modifier function Checks if the Person trying to execute a Specific function is the Contract Owner. If they are not, it returns an error, preventing further Execution of the function.
The Contract State has a Key called OWNER, which Stores the address of the Contract’s Owner.
This function Checks if a Contract (from a specific chain) is Whitelisted, meaning it’s allowed to interact with this Contract.
is_owner_modifier - Ensures Only the Owner can Execute Certain functions. This is essential for Administrative Actions.
is_white_listed_modifier - Ensures only approved (whitelisted) Contracts on other Chains can Interact with this Contract. This is Crucial for cross-chain Interactions to Maintain Security and prevent Unauthorized access.
query.rs
The query.rs file in the Omnichain Token Project Defines various Query handlers and helper functions to fetch Specific information from the Contract’s State and External Sources. These functions respond to Queries sent to the Contract and provide Information related to the Contract’s Configuration, such as Whitelisted Contracts, Chain Types, Owner Details, and Prices for Gas and Tokens from an External Oracle (via Router Protocol).
Imports and Constants
The above Code Comprises of Importing cosmwasm_std, cw2, router_wasm_bindings, etc. packages.
Handling Query Function
Helper Functions
It Checks if the given Contract on a Specified Chain is Whitelisted.
Loads the Whitelisted Contract for a given chain_id from Storage.
Compares the Loaded Contract Address with the provided contract_addr.
It fetches the Whitelisted Contract Address for a given Chain.
Loads the Whitelisted Contract for the provided chain_id from Storage.
It fetches the Owner of the Contract.
Loads the Owner from Storage and returns the Owner’s Address as a String.
It fetches all Whitelisted Contracts Stored in the Contract.
Retrieves all entries in WHITELISTED_CONTRACT_MAPPING, which Stores Chain IDs and Contract Addresses.
The range() method is used to fetch all Records, which are then returned as a Cector of Tuples (chain_id, contract_address).
It fetches the type of the chain corresponding to a given chain_id.
Loads the Chain Type (as a u64) for the provided chain_id from Storage and returns it.
It fetches the Gas Price for a given Chain by querying the Router Protocol Oracle.
Uses RouterQuerier to Query the Router Protocol Oracle for the Gas Price of a specified Chain.
The returned Gas Price is multiplied by 120% to adjust it Before returning the Result.
It fetches the Price of a given Token Symbol by Querying the Router Protocol Oracle.
Queries the Router Protocol Oracle for the Price of a Specific Token Symbol using RouterQuerier.
It fetches the Address of the cross-chain XERC20 Token.
Loads the XERC20 Token Address from Storage and returns it as a String.
It fetches the Chain ID of the Current Contract.
Loads the Chain ID from storage and returns it as a String.
state.rs
The state.rs file Defines the Contract’s State Variables and how they are stored in the Blockchain using CosmWasm’s Storage Utilities like Item and Map. These Variables are used throughout the Contract to Maintain important pieces of Data, such as the Contract Owner, Whitelisted Contracts, Chain Types, Chain IDs, and the cross-chain Token Information.
tests.rs
It Contains the Test Scripts for the Smart Contract which we Wite.
EVM-side
XERC20.sol
Imports and Inheritance
The Contract Imports necessary Libraries and Interfaces for cross-chain functionality and Basic ERC-20 Token Implementation.
IDapp and IGateway are Router Protocol Interfaces enabling cross-chain Communication.
ERC20 is OpenZeppelin’s Implementation of the ERC-20 Standard for Tokens.
State Variables
Define contract-level Variables to store Owner, Gateway Details, Chain Mappings, etc.
owner Stores the Address of the Contract Owner.
name Maps Chain names (like “mumbai”) to Chain IDs for cross-chain Reference.
ourContractOnChains Maps the Chain ID to the Contract Address on the Destination Chain.
Constructor
The Constructor Initializes the Contract on Deployment. It sSets the Chain and Gateway Addresses based on the given Chain Name and Mints an Initial Token Supply.
Set Cross-Chain Contract Addresses
Define functions to Set Metadata, Gateways, and Contract Addresses on Destination Chains, controlled by the Owner.
setDappMetadata sets the Fee Payer Address.
setGateway updates the Gateway Contract Address.
setContractOnChain stores the Destination Chain’s Contract Address.
mint is ued for Minting of the Token.
Cross-Chain Transfer Function
This function enables the actual cross-chain Token Transfer by Burning Tokens on the Source Chain and initiating a cross-chain Message.
_burn removes Tokens from the Sender’s Balance on the Source Chain.
packet encodes the Sender’s Address and Amount, while requestPacket includes the Destination Contract Address.
iSend initiates the cross-chain Message.
Receiving Cross-Chain Tokens on the Destination Chain
The iReceive function mints tokens on the destination chain for the recipient specified in the cross-chain message.
iReceive Checks that the Call comes from the Gateway and that the Source Contract matches.
It Decodes the Packet to retrieve recipient and amount, then Mints Tokens to the Recipient’s Address.
Acknowledgement Handling
The iAck function can Optionally Track whether a cross-chain Transaction succeeded or failed on the Destination Chain.
requestIdentifier is an Event nonce to Track the request.
execFlag Indicates success or failure.
execData Contains any Data returned from the Destination Chain’s handling function.
Utility Functions
Helper functions for Address Conversion and Metadata packing.
getRequestMetadata prepares Metadata required by Router’s cross-chain functions.
toAddress Converts a Byte array to an Address, often used for Unpacking cross-chain Data.