// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title AereIGP (Interchain Gas Paymaster) * @notice Receives AERE fees from outbound cross-chain messages. * Foundation/relayer claims these and uses them to fund the gas paid * on destination chains when delivering inbound messages from AERE. * * Hyperlane-compatible interface — apps using Hyperlane's `quoteDispatch` helpers * query us for the AERE fee required to relay a message to a given destination. * * Pricing model (configurable per destination): * fee = gasOverhead * gasPriceQuote * * gasOverhead = chain-specific gas estimate (e.g. Ethereum ~150k, BSC ~80k). * gasPriceQuote = AERE-denominated quote of destination chain's gas price. * Owner sets this; in v2 we'll pull from AereOracle once cross-chain price * feeds are wired. */ contract AereIGP is Ownable { struct DestinationConfig { uint256 gasOverhead; uint256 gasPriceQuoteAere; // wei AERE per unit of destination gas } mapping(uint32 => DestinationConfig) public destinations; address public payoutRecipient; event GasPaymentReceived(bytes32 indexed messageId, uint32 destDomain, uint256 amount); event DestinationConfigured(uint32 indexed domain, uint256 gasOverhead, uint256 gasPriceQuote); event PayoutRecipientChanged(address indexed recipient); constructor(address _payoutRecipient) { payoutRecipient = _payoutRecipient; } function configure(uint32 domain, uint256 gasOverhead, uint256 gasPriceQuoteAere) external onlyOwner { destinations[domain] = DestinationConfig(gasOverhead, gasPriceQuoteAere); emit DestinationConfigured(domain, gasOverhead, gasPriceQuoteAere); } function setPayoutRecipient(address _r) external onlyOwner { payoutRecipient = _r; emit PayoutRecipientChanged(_r); } /// Quote the AERE fee required to relay a message to `destDomain`. function quoteGasPayment(uint32 destDomain) external view returns (uint256) { DestinationConfig memory c = destinations[destDomain]; return c.gasOverhead * c.gasPriceQuoteAere; } /// Called by AereMessenger via postDispatch when a message includes an IGP fee. function postDispatch(bytes calldata, bytes calldata data) external payable { (bytes32 messageId, uint32 destDomain) = abi.decode(data, (bytes32, uint32)); emit GasPaymentReceived(messageId, destDomain, msg.value); } /// Sweep accumulated fees to the payout recipient (relayer's hot wallet). function claim() external { uint256 bal = address(this).balance; require(bal > 0, "AereIGP: nothing to claim"); (bool ok, ) = payoutRecipient.call{ value: bal }(""); require(ok, "AereIGP: payout failed"); } receive() external payable {} }