Dispatching Requests & Responses
This section explores the means by which applications may leverage Hyperbridge to dispatch cross-chain messages. It's important to understand that in Hyperbridge protocol, there are 3 different kinds of cross-chain messages. We'll explore each of them Below.
POST Requests
A POST request is simply a cross-chain message to be executed by some IIsmpModule
on a destination. It's important to understand that EOAs should not dispatch POST requests directly, instead an application must be responsible for the dispatch. This way if the request delivery or execution fails on the destination, modules can "catch" this failure similar to the try/catch pattern and handle the failure case in the onPostRequestTimeout
callback.
A post request is created using the DispatchPost
struct
// An object for dispatching post requests
struct DispatchPost {
// Use the StateMachine library to create this
bytes dest;
// The destination module
bytes to;
// The request body
bytes body;
// timeout for this request in seconds
uint64 timeout;
// The amount put up to be paid to the relayer,
// this is in DAI and charged to msg.sender
uint256 fee;
// who pays for this request?
address payer;
}
Dispatch Parameters
dest
: Destination chain, for this you'll use theStateMachine
library egStateMachine.evm(1)
for Ethereum Mainnet.to
: Receiving module/contract address on the destination chain.body
: Serialized byte representation of the message (to be decoded by the receiving contract).timeout
: Time in seconds for message validity eg 3600 for a timeout of 1 hour, or 0 for no timeout. ie Messages will never expire. If the timeout is set to a non-zero value, messages that have exceeded this timeout will be rejected on the destination and require user action (timeout message) to revert changes.fee
: Optional relayer fees, this can also be set to zero if the application developers prefer to self-relay.payer
: The account that should receive a refund of the relayer fees if the request times out.
function send_message(
bytes memory message,
uint64 timeout,
address to,
uint256 relayerFee
) public payable returns (bytes32) {
DispatchPost memory post = DispatchPost({
body: message,
dest: StateMachine.evm(1),
timeout: timeout,
to: abi.encodePacked(to),
fee: relayerFee,
payer: tx.origin
});
return IDispatcher(_host).dispatch{value: msg.value}(post);
}
POST Responses
Dispatching a POST response, going by it's name is, well, a response to a previously received POST request. Dispatching a POST response requires that the contract has indeed received a post request from a counterparty chain in a previous transaction.
A post response dispatch has the following fields:
// An object for dispatching post responses
struct DispatchPostResponse {
// The request that initiated this response
PostRequest request;
// bytes for post response
bytes response;
// timeout for this response in seconds
uint64 timeout;
// The amount put up to be paid to the relayer,
// this is in DAI and charged to msg.sender
uint256 fee;
// who pays for this request?
address payer;
}
Dispatch Parameters
request
: The request object that was previously received.response
: Opaque byte representation of the response message (to be decoded by the receiving contract).timeout
: Time in seconds for message validity eg 3600 for a timeout of 1 hour, or 0 for no timeout. ie Messages will never expire. If the timeout is set to a non-zero value, messages that have exceeded this timeout will be rejected on the destination and require user action (timeout message) to revert changes.fee
: Optional relayer incentive (zero for self-relay).payer
: The account that should receive a refund of the relayer fees if the request times out.
// _host is variable that contains the EvmHost contract address
function sendResponse(
PostRequest memory request,
bytes memory response,
uint64 timeout,
uint256 relayerFee
) public payable returns (bytes32) {
DispatchPostResponse memory postResponse = DispatchPostResponse({
request: request,
response: response,
timeout: timeout,
fee: relayerFee,
payer: msg.sender
});
return IDispatcher(_host).dispatch{value: msg.value}(postResponse);
}
GET Requests
GET requests allow contracts to perform asynchronous reads of a counterparty blockchain's state. This can be used to read either the Account
object, which is stored in the world state, or even storage slots in a contract storage. Eg reading the price of a Uniswap pair on a remote chain.
When dispatching get requests, you specify the storage keys you need to read and the block height at which you need to read these storage entries.
// An object for dispatching get requests
struct DispatchGet {
// bytes representation of the destination state machine
bytes dest;
// height at which to read the state machine
uint64 height;
// Storage keys to read
bytes[] keys;
// timeout for this request in seconds
uint64 timeout;
// The initiator of this request
address sender;
// Hyperbridge protocol fees for processing this request.
uint256 fee;
}
Dispatch Parameters
dest
: The chain whose database should be read (e.g.,StateMachine.evm(1)
for Ethereum Mainnet).height
: Block height at which the values should be fetched.keys
: Storage keys whose values need to be fetched.timeout
: Time in seconds for message validity eg 3600 for a timeout of 1 hour, or 0 for no timeout. ie Messages will never expire. If the timeout is set to a non-zero value, messages that have exceeded this timeout will be rejected on the destination and require user action (timeout message) to revert changes.fee
: Hyperbridge protocol fees for processing the request.sender
: The account initiating this request.
// _host is variable that contains the EvmHost contract address
function readState(
bytes memory dest,
bytes[] memory keys,
uint64 timeout,
uint256 fee,
uint256 height
) public payable returns (bytes32) {
DispatchGet memory getRequest = DispatchGet({
dest: dest,
keys: keys
height: height
timeout: timeout,
fee: fee,
sender: tx.origin
});
return IDispatcher(_host).dispatch{value: msg.value}(getRequest);
}
In the next section we'll look at how Hyperbridge collects it's fees.