Developing Cross-Chain Applications
A xApp (pronounced "zap") is a cross-chain application built on top of the Nomad Protocol.
Nomad sends messages from one chain to another in the form of raw bytes. A cross-chain application that wishes to use Nomad will need to define the rules for sending and receiving messages for its use case.
Each cross-chain application must implement its own messaging protocol. By convention, we call the contracts that implement this protocol the application's Router contracts. These Router contracts must:
- maintain a permissioned set of the contract(s) on remote chains from which it will accept messages via Nomad — this could be a single owner of the application on one chain; it could be a registry of other applications implementing the same rules on various chains
- maintain a permissioned registry of connections via the
XappConnectionManagercontract (see "Connection Management").
- encode messages in a standardized format, so they can be decoded by the Router contract on the destination chain
- handle messages from remote Router contracts
- dispatch messages to remote Router contracts
By implementing these pieces of functionality within a Router contract and deploying it across multiple chains, we create a working cross-chain application using a common language and set of rules. Applications of this kind may use Nomad as the cross-chain courier for sending and receiving messages to each other.
This repository has several examples one can use to build understanding around Cross-Chain Applications.
Important! The template supported Solidity version is <0.8!
This is a template provided by the Nomad team that shows the high-level components of an xApp, ready for one to fill in their own application logic and utilize an Nomad channel for cross-chain communication.
To implement a xApp, define the actions you would like to execute across chains. For each type of action,
- in the xApp Router
- implement a function like doTypeA to initiate the action from one domain to another (add your own parameters and logic)
- implement a corresponding _handle function to receive, parse, and execute this type of message on the remote domain
- add logic to the handle function to route incoming messages to the appropriate _handle function
- in the Message library,
- implement functions to format the message to send to the other chain (encodes all necessary information for the action)
- implement functions to parse the message once it is received on the other chain (decode all necessary information for the action)
The XCM is the primary permissioning point for channels. It provides functions by which
- xApp administrators can enroll or unenroll
Replicacontracts for inbound messages
- xApp administrators can enroll or unenroll a
Homecontract for outbound messages
- xApp administrators can permission or de-permission watchers
- watchers can unenroll
When deploying a xApp
Router, the xApp administrators must select an existing XCM, or deploy their own. The address of the XCM must be passed to the router's initialization method.
Ping Pong xApp
Important! The Ping Pong xApp is for reference only. Please do not deploy!
The PingPong xApp is capable of initiating PingPong "matches" between two chains. A match consists of "volleys" sent back-and-forth between the two chains via Nomad.
The first volley in a match is always a Ping volley.
- When a Router receives a Ping volley, it returns a Pong.
- When a Router receives a Pong volley, it returns a Ping.
The Routers keep track of the number of volleys in a given match, and emit events for each Sent and Received volley so that spectators can watch.
Token Bridge xApp
See the full-length Token Bridge Documentation for in-depth details on Token Bridge operation and construction.
Cross-Chain Governance xApp
See the full-length Nomad Governance Documentation for in-depth details on Governance xApp operation and construction.
Glossary of Terms
- Local vs Remote: in the context of discussing a particular contract, it is deployed on a particular chain. Contracts and assets on that chain are "local." Contracts and assets on another chain are "remote"
- e.g. Uniswap is deployed on Ethereum. Ethereum is the local chain. Celo is a remote chain
- e.g. there is a token deployed on Celo. There is a local
Homecontract (on Celo), and a local
Replicacontract (on Celo) receiving messages from Ethereum. There is a remote
Home(on Ethereum) sending messages. There is a remote
Replica(on Ethereum) receiving messages from Celo. There is a remote
Router(on Ethereum) communicating with the local
- "Locally originating" vs "Remotely Originating": in the context of a token or asset in a specific contract, these terms denote whether the original canonical contract is deployed on the local chain, or on a remote chain
- e.g. cUSD originates on Celo. in the context of the Celo blockchain, cUSD is "of local origin" or "locally originating"; in the context of the Ethereum blockchain, cUSD is "of remote origin"
- e.g. Ether and WETH originate on Ethereum. When used in a Celo contract, they are "remotely originating" or "of remote origin"
- e.g. a
Routerreceives a Transfer message for a remotely-originating asset. It finds the local contract that represents that asset. When it receives a message for a locally originating asset, it knows that it can find the original asset contract locally
- Router Contract: a contract that implements a cross-chain application by specifying the:
- message format - the bytes-encoded format of messages for the application
- registry of remote Router contracts that implement the same application on remote chains
- rules & behavior for handling messages sent via Nomad by a registered Router contract on a remote chain
- rules & behavior for dispatching messages via Nomad to a registered Router contract on a remote chain
- Message: bytes transferred via Nomad that encode some application-specific instructions via a standardized set of rules
- Instructions: set of application-specific actions (e.g. "send 5 token X to 0x123...456 on chain Z" in the case of a Token Bridge); calls to functions on the Router contract
- Handling Messages from Nomad Channels:
- receive bytes-encoded message from Nomad (sent from a remote chain)
- enact or dispatch the instructions on the local chain
- local handler decodes the message into application-specific instructions
- Dispatching Message to Nomad Channels:
- receive instructions on the local chain (via local users and contracts calling functions on the contract)
- encode the instructions into bytes using standardized message format
- dispatch the bytes-encoded message to Nomad (to be sent to a remote chain)