跨链 Gas 费用后置处理
在后置处理钩子中,如果支付的费用不足以覆盖中继器的预期成本,InterchainGasPaymaster
合约将会回滚。在 dispatch
时计算的 gas 报价必须与中继器的预期成本相符。
Gas 限制
gasLimit
是根据在目标链上调用给定消息的 handle
的成本来设置的。这可能会根据消息内容和处理程序的逻辑而有所不同。
用于计量 handle 调用的默认 gasLimit
是静态的 50_000
gas。这对于简单操作来说已经足够,但对于更复杂的 handle
函数可能不够。
如果你的 handle
函数执行复杂操作或需要更多 gas,你必须在元数据中覆盖默认的 gasLimit
以避免交易回滚。在单元测试中对你的 handle
实现进行基准测试,以确定合理的 gasLimit
。
元数据
此 hook 需要 打包编码 格式的 StandardHookMetadata
。查看 Mailbox dispatch 重载了解如何传递元数据覆盖。
- Solidity
struct StandardHookMetadata {
uint16 variant;
uint256 msgValue;
uint256 gasLimit;
address refundAddress;
}
StandardHookMetadata
结构定义了元数据编码所需的字段:
variant
: 指定元数据格式版本msgValue
: 随消息发送的原生代币数量gasLimit
: 目标链上handle
函数的 gas 限制。确保这与你的模拟结果相匹配refundAddress
: 退还未使用 gas 费用的地址
要编码此元数据,请使用 StandardHookMetadata.formatMetadata
库函数。Solidity 不支持使用 abi.encodePacked
直接编码结构体。
使用示例
- Solidity
// 示例: 使用 StandardHookMetadata 编码元数据
bytes memory metadata = StandardHookMetadata.formatMetadata(
0, // ETH 消息值
200000, // 自定义 gas 限制
address(this), // 退款地址
bytes("") // 可选的自定义元数据
);
确定和覆盖 Gas 限制
- 模拟和基准测试 Gas 使用量:
- 使用 Tenderly 或 Foundry 等工具模拟消息接收者的
handle
函数。确保from
地址设置为你 链上的 Mailbox 合约。 - 如果 gas 使用量超过
50,000
,计算适当的gasLimit
并更新你的元数据。 - 使用 Hyperlane Explorer 中的操作按钮 从消息详情模拟交易。
- 更新你的元数据:
- 根据模拟结果计算所需的
gasLimit
。 - 在元数据中传入更新后的
gasLimit
,确保中继器能够传递你的消息。
目标 Gas 配置
对于每个远程域,InterchainGasPaymaster 设置域 gas 配置。
struct DomainGasConfig {
IGasOracle gasOracle;
uint96 gasOverhead;
}
Gas 开销
gas 开销是目标 gas 配置的一部分。这对应于在目标链上处理消息的运营成本。
- 你应该确保
gasOverhead
足以覆盖目标链上的 ISM 范围。 - 由于你可以为不同的消息类型配置不同的 ISM,每个 ISM 的
verify
函数可能有不同的 gas 开销。
Gas 预言机
跨链 Gas 费用要求是使用预言机提供的 gas 价格和原目标链之间的汇率计算的。
IGP 合约可以配置 gas 预言机,负责跟踪远程代币 gas 价格和汇率。开发者应该使用 Mailbox 合约上的 quoteDispatch
函数来计算 gas 费用。quoteDispatch
考虑了系统级开销,确保整个 dispatch
过程的费用计算准确。
- 汇率和 gas 价格由中继器决定。可能会收取价差以应对价格波动和运营成本。
最终,中继器将能够自动更新其 gas 预言机,以确保其 IGP 始终为远程 gas 报出公平价格。
getExchangeRateAndGasPrice
- Solidity
function getExchangeRateAndGasPrice(
uint32 _destinationDomain
)
public
view
override
returns (uint128 tokenExchangeRate, uint128 gasPrice)
{
IGasOracle _gasOracle = destinationGasConfigs[_destinationDomain]
.gasOracle;
if (address(_gasOracle) == address(0)) {
revert(
string.concat(
"Configured IGP doesn't support domain ",
Strings.toString(_destinationDomain)
)
);
}
return _gasOracle.getExchangeRateAndGasPrice(_destinationDomain);
}
参数
destinationDomain
: 消息的目标域
返回值
tokenExchangeRate
: 原链和目标链 gas 代币之间的汇率gasPrice
: 目标链的 gas 价格
quoteGasPayment
quoteGasPayment
函数计算中继器预期成本的费用。
- Solidity
function quoteGasPayment(
uint32 _destinationDomain,
uint256 _gasLimit
) public view virtual override returns (uint256) {
// Get the gas data for the destination domain.
(
uint128 _tokenExchangeRate,
uint128 _gasPrice
) = getExchangeRateAndGasPrice(_destinationDomain);
// The total cost quoted in destination chain's native token.
uint256 _destinationGasCost = _gasLimit * uint256(_gasPrice);
// Convert to the local native token.
return
(_destinationGasCost * _tokenExchangeRate) /
TOKEN_EXCHANGE_RATE_SCALE;
}
参数
destinationDomain
: 消息的目标域gasLimit
: 用于计量handle
调用的 gas 限制
返回值
fee
:postDispatch
成功所需的支付金额
重试
如果 handle
调用消耗的 gas 超过报价,中继器将不会提交处理交易。这个问题通常是由于初始 dispatch
时 gas 支付不足导致的。
在这种情况下,可以使用 payForGas
函数支付额外的 gas。
- Solidity
function payForGas(
bytes32 _messageId,
uint32 _destinationDomain,
uint256 _gasAmount,
address _refundAddress
) external payable;
参数
messageId
: 从dispatch
调用返回的消息标识符destinationDomain
: 消息的目标域gasAmount
: 要支付的额外 gas 数量refundAddress
: 退还多余支付的地址
使用 Hyperlane Explorer 进行调试
Hyperlane Explorer 是一个强大的工具,可用于调试跨链消息问题,包括 gas 支付和中继器行为。
主要功能
- 消息状态: 查看消息的当前状态(例如,"重试: GasPaymentRequirementNotMet")。
- Gas 支付详情: 检查已支付的 gas 数量(原始 IGP gas 数量)和中继器要求的数量。
- 模拟调用数据: 使用"查看调用数据详情"选项在 Tenderly 等工具上模拟交易。
故障排除
本节介绍开发者在处理跨链 Gas 支付时遇到的常见问题及其可能的解决方案。
GasPaymentRequirementNotMet
警告
-
原因: 当
dispatch
期间提供的 gas 支付不满足中继器计算的要求时发生。 -
解决方案:
- 使用
quoteDispatch
计算使dispatch
调用成功所需的总费用。 - 验证元数据中的
msg.value
是否覆盖了中继器报价的费用。 - 在 Hyperlane Explorer 中检查消息状态。查找:
Retry(GasPaymentRequirementNotMet)
。
- 使用
回退路由和超额支付警告
-
原因:
msg.value
超过所需的 gas 支付,触发回退路由钩子。 -
解决方案:
- 验证你的报价逻辑(
quoteDispatch
)是否与中继器的预期费用匹配。 - 避免在未先对
handle
函数进行基准测试的情况下过度估计gasLimit
值。 - 模拟交易以确认适当的支付。
- 验证你的报价逻辑(
意外的高额 Gas 报价
-
原因: 设置了非常高的
gasLimit
,导致过高的 gas 报价。 -
解决方案:
- 仔细检查元数据中指定的
gasLimit
。 - 验证你的报价逻辑(
quoteDispatch
)是否与中继器的预期费用匹配。 - 调整
gasLimit
以匹配你的handle
函数的估计 gas 消耗。
- 仔细检查元数据中指定的