Fees
Hyperbridge charges a non-refundable protocol fee per byte of the request/response body. This fee is withdrawn from the contract account calling the IDispatcher
. Fees can be paid in either the native token, or the IDispatcher
's configured feeToken
. Modules may also opt to pay a delivery fee for their messages to incentivize 3rd party relayers to deliver and execute their requests on the destination chain.
Native Token Payments
The IDispatcher
contract can be called with some msg.value
to be used to pay for both the Hyperbridge protocol fees and relayer fees. Lets understand how this value is calculated. First we must understand that Hyperbridge ultimately collects it's fees in stablecoins. The configured stablecoin for fees is accessible by:
address feeToken = IDispatcher(host()).feeToken();
So when you use the native token to pay for fees, The IDispatcher
actually swaps this native token for the feeToken
using UniswapV2 underneath.
So to quote the amount that's required for fees, you will need to call the following methods offchain:
uint256 perByteFee = IDispatcher(host()).perByteFee(request.dest);
uint256 protocolFees = request.body.length * perByteFee;
uint256 fees = protocolFees + request.fee;
address[] memory path = new address[](2);
path[0] = IUniswapV2Router02(_uniswap).WETH();
path[1] = address(IDispatcher(host()).feeToken());
uint256 nativeCost = IUniswapV2Router02(_uniswap).getAmountsIn(fees, path)[0];
Alternatively If your module extends the BaseIsmpModule
, then you already have access to a convenient method quoteNative
for this.
uint256 nativeCost = YourModule(moduleAddress).quoteNative(request);
Now you can simply dispatch the request while paying with the native token
IDispatcher(host()).dispatch{value: nativeCost}(request);
Stable Coin Payments
Alternatively, you might want to make payments directly in the configured feeToken
. This would save on a considerable amount of gas, and as a result save your application users some money. To do this, ensure that your application has approved the host contract to spend infinitely, not to worry here. The host contract is immutable, ie not upgradeable, so you don't have to worry about getting drained.
In your contract constructor you should execute the following:
constructor() {
// approve the host infintely
address feeToken = IDispatcher(host()).feeToken();
IERC20(feeToken).approve(host(), type(uint256).max);
}
Now that your contract has approved the host
. You can now call dispatch without the msg.value
. The host will withdraw the exact amount for it's protocol fees and any relayer fees from your contract's balance in the feeToken
.
You can estimate the cost of a request through
uint256 perByteFee = IDispatcher(host()).perByteFee(request.dest);
uint256 protocolFees = request.body.length * perByteFee;
uint256 fees = protocolFees + request.fee;
Alternatively If your module extends the BaseIsmpModule
, then a convenient method quote
is provided for this.
uint256 feeTokenCost = YourModule(moduleAddress).quote(request);
Now you can simply dispatch the request without the msg.value
. If this request is the side-effect of a user transaction, then you should withdraw the feeToken
from the user's account before dispatching.
using SafeERC20 for IERC20;
address feeToken = IDispatcher(host()).feeToken();
IERC20(feeToken).safeTransferFrom(msg.sender, address(this), feeTokenCost);
IDispatcher(host()).dispatch(request);
Relayer Fees
The relayer fee is an optional incentive provided by applications initiating cross-chain transactions. It compensates Hyperbridge's decentralized relayers for delivering messages to the destination chain.
Components
The fee consists of three parts:
-
Proof verification cost: For a cross-chain message to be delivered and executed, it must first be authenticated through state proofs. The expected cost for state proof verification is 150k gas. Contracts should account for this cost when setting the relayer fee.
-
Message execution gas cost: After proof verification, the receiving module is handed the request to be executed. This will consume some gas which should also be accounted for
-
Relayer service fee: This additional amount rewards relayers for their services. Relayers are profit-Driven mediators and they will prioritize messages with fees that ensure profitability.
Calculating the relayer fee can be expressed as follows:
destination_gas_cost = 150_000 + receiving_module_gas_cost
relayer_fee = gas_price_to_usd(destination_gas_price * destination_gas_cost) + relayer_tip_usd
Mitigating Gas Cost Spikes
During periods of high gas prices on the destination chain, you can increase the relayer fee for in-flight requests and responses to incentivize their delivery.
The IDispatcher
interface provides the fundRequest
and fundResponse
methods for this purpose. Simply call the appropriate function with the request/response commitment and the desired fee increase amount.