Sending Cross-Chain Messages between Parachains

The supported way to exchange cross-chain messages (XCM) between parachains is to use Horizontal Relay-routed Message Passing (HRMP) channels.

Each HRMP channel is unidirectional. In order to enable full connectivity between two parachains, two HRMP channels must be opened: one for sending outgoing XCM and the other for receiving incoming XCM.

Opening an HRMP Channel

Opening a channel between two parachains A and B takes 2 steps: 1. parachain A initiates a channel request 2. parachain B accepts the channel request

For step (1), parachain A calls hrmp > hrmpInitOpenChannel(recipient, proposedMaxCapacity, proposedMaxMessageSize) to create a channel request with the input configuration.

For step (2), parachain B calls hrmp > hrmpAcceptOpenChannel(sender) to accept the channel open request from the input sender.

In order to dispatch a call from its sovereign origin, a parachain may use governance to send the encoded call in a Transact instruction to the Relay Chain, but it may also execute this logic autonomously (e.g. on the notification that a channel was requested).

Connecting to Ecosystem Parachains

The examples in this section include steps to connect to existing parachains in the Polkadot ecosystem. Examples include the following Polkadot parachains: 1. AssetHub supports managing asset balances as well as the execution of cross-chain transfers. 2. Snowbridge supports sending ERC20 tokens between Polkadot parachains and Ethereum in both directions. 3. HydraDX supports DeFi functionality to provide liquidity to Polkadot.

For all examples: . paraId for the user’s parachain is set to 43211234. . proposedMaxCapacity and proposedMaxMessageSize are set to the values of Polkadot config.hrmpChannelMaxCapacity = 1000 and config.hrmpChannelMaxMessageSize = 102400, respectively.

AssetHub

AssetHub supports managing asset balances as well as the execution of cross-chain transfers.

AssetHub is a common-good, system parachain and is therefore controlled by relay chain governance. This means it is sufficient to propose opening both channels at once through the relay chain’s GeneralAdmin track. The proposal should set its call (proposed to be executed) to utility.batchAll with the input including 2 calls to hrmp.forceOpenHrmpChannel to open a channel in each direction between the user parachain and AssetHub.

The first call to hrmp.forceOpenHrmpChannel proposes opening a unidirectional channel to send XCM from the user parachain to AssetHub. If AssetHub’s paraId is set to 1000, here are the inputs:

hrmp.forceOpenChannel(
	sender = 43211234,
	recipient = 1000,
	max_capacity = 1000,
	max_message_size = 102400,
)

Here is the second call to open a unidirectional channel to send XCM from AssetHub to the user parachain:

hrmp.forceOpenChannel(
	sender = 1000,
	recipient = 43211234,
	max_capacity = 1000,
	max_message_size = 102400,
)

Here is a successful example of this proposal which passed to open 2 HRMP channels between Unique Network and AssetHub. Here is another example of a proposal executed to open HRMP Channels between AssetHub and Mythos.

Snowbridge

Snowbridge supports sending ERC20 tokens between Polkadot parachains and Ethereum in both directions.

Snowbridge leverages AssetHub to mint ERC20 tokens received from Ethereum and send them to parachains. This implies that a prerequisite step for receiving ERC20 tokens via Snowbridge is opening HRMP channels with AssetHub by following the previous section.

The standard way of interacting with Snowbridge is to make calls to the Gateway contract deployed on Ethereum at this address.

If an ERC20 token has already been bridged before, the user may send the following transaction to the Gateway to send ERC20 tokens to parachain destinationChain from Ethereum and deposit into account destinationAddress on that parachain.

function sendToken(address token, ParaID destinationChain, MultiAddress destinationAddress, uint128 destinationFee, uint128 amount)
    external
    payable;

If the ERC20 tokens has not been bridged before, there is a prerequisite step to register the ERC20 token on AssetHub via the ForeignAssets pallet. To register ERC20 tokens for the first time, the user may send the following transaction to the Gateway:

function registerToken(address token) external payable;

The above function sends a message to the AssetHub parachain to register a new fungible asset in the ForeignAssets pallet. To account for delivery costs, the above function charges a fee in Ether which may be retrieved beforehand by calling quoteRegisterFee.

For more information, see the Snowbridge Docs. For more information on the Snowbridge deployment to Polkadot, see the governance proposal which initialized Snowbridge on BridgeHub and AssetHub.

HydraDX

HydraDX supports DeFi functionality to provide liquidity to Polkadot.

The Opening an HRMP Channel section shows the general steps for opening two HRMP channels between any two parachains.

To propose opening a channel to send XCM to HydraDX, the sending parachain may call:

hrmp.hrmpInitOpenChannel(
	recipient = 2034,
	proposedMaxCapacity = 1000,
	proposedMaxMessageSize = 102400,
)

HydraDX may accept the channel open request from the sending parachain with paraID 43211234 by calling:

hrmpAcceptOpenChannel(
	sender = 43211234
)

HydraDX may call the following to propose opening a channel to send XCM from HydraDX to the parachain with paraID 43211234:

hrmp.hrmpInitOpenChannel(
	recipient = 43211234,
	proposedMaxCapacity = 1000,
	proposedMaxMessageSize = 102400,
)

Assuming the HydraDX has paraID 2034, the receiving parachain may accept the channel open request by calling:

hrmpAcceptOpenChannel(
	sender = 2034
)

Note that in order to dispatch a call from its sovereign origin, a parachain may use governance to send the encoded call in a Transact instruction to the Relay Chain, but it may also execute this logic autonomously (e.g. on the notification that a channel was requested). HRMP extrinsics often must be called from the parachain’s sovereign account as origin, often via a democracy proposal.

Here is an example of a proposal on Moonbeam to Open/Accept HRMP channels with HydraDX.

Bonus: HRMP Channel Notification Handlers

There are 3 handlers that may be configured as hooks to implement automated logic for when a HRMP notification is received: . HrmpChannelAcceptedHandler . HrmpChannelClosingHandler . HrmpNewChannelOpenRequestHandler

Each follows a similar interface:

pub trait HandleHrmpNewChannelOpenRequest {
	fn handle(sender: u32, max_message_size: u32, max_capacity: u32) -> XcmResult;
}

pub trait HandleHrmpChannelAccepted {
	fn handle(recipient: u32) -> XcmResult;
}

pub trait HandleHrmpChannelClosing {
	fn handle(initiator: u32, sender: u32, recipient: u32) -> XcmResult;
}

The default implementation () returns Ok(()) without executing any effects. Read more in the Polkadot documentation.