Skip to content
Lattice

HyperlaneGatewayAdapterLib

Git Source

Title: HyperlaneGatewayAdapterLib

Authors: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80), Modified from Hyperlane (https://github.com/hyperlane-xyz/hyperlane-monorepo)

Logic + ERC-7201 storage for the Hyperlane ERC-7786 gateway adapter. sendMessage wraps the ERC-7930 envelope with a source-minted nonce and dispatches it via the Mailbox's 4-arg dispatch (default hook + synthesized StandardHookMetadata); handle is the Mailbox-gated delivery callback that validates the origin domain + trusted remote, de-duplicates per (chainId, sendId), and delivers to the ERC-7930 recipient. EVM chains only. Hyperlane routes by uint32 domain — usually the EVM chainId but NOT guaranteed, so the map is admin-registered, never inferred.

Wire message = abi.encode(senderInteropAddr, recipientInteropAddr, innerPayload, nonce); the Mailbox recipientAddress targets the trusted 32-byte remote, which forwards to the final recipient. The hook metadata is Hyperlane StandardHookMetadata variant 1 synthesized INLINE (no hook-lib dependency): variant(uint16=1) || msgValue(uint256=0) || gasLimit(uint256) || refundAddress(address = the sending user) — overpaid IGP fees refund to the user, never to the Diamond. v1 uses the Mailbox DEFAULT ISM (no ISpecifiesInterchainSecurityModule); pinning a Lattice-custom ISM by adding interchainSecurityModule() to the facet later is a compatible additive follow-up.

Constants

METADATA_VARIANT

StandardHookMetadata variant 1 (the only variant the default hook stack understands).

uint16 private constant METADATA_VARIANT = 1;

DEFAULT_DESTINATION_GAS

handle gas limit used when a destination has no admin-configured gas limit. Sized for envelope decode + auth + dedup + a typical receiveMessage (Hyperlane's own bare default of 50k is too thin for the recipient hop).

uint256 internal constant DEFAULT_DESTINATION_GAS = 200_000;

Functions

hyperlaneGatewayAdapterStorage

function hyperlaneGatewayAdapterStorage() internal pure returns (HyperlaneGatewayAdapterStorage storage $);

Returns

NameTypeDescription
$HyperlaneGatewayAdapterStorage

__HyperlaneGatewayAdapter_init

Stores the Mailbox (zero reverts HyperlaneZeroMailbox — an unconfigured adapter must not exist) and registers the gateway-source + adapter ERC-165 ids.

function __HyperlaneGatewayAdapter_init(address mailbox_) internal;

Parameters

NameTypeDescription
mailbox_address

registerInterfaces

Writes true to the SHARED IERC7786GatewaySource ERC-165 map slot (0x11967553 → ...; same slot the CCIP/LayerZero/Wormhole/Axelar adapters register — a Diamond mounts at most one gateway) and to the adapter's own IHyperlaneGatewayAdapter map slot.

function registerInterfaces() internal;

mailbox

function mailbox() internal view returns (address);

Returns

NameTypeDescription
<none>address

domainOf

function domainOf(uint256 chainId) internal view returns (uint32);

Parameters

NameTypeDescription
chainIduint256

Returns

NameTypeDescription
<none>uint32

chainIdOf

function chainIdOf(uint32 domain) internal view returns (uint256);

Parameters

NameTypeDescription
domainuint32

Returns

NameTypeDescription
<none>uint256

trustedRemoteOf

function trustedRemoteOf(uint256 chainId) internal view returns (bytes32);

Parameters

NameTypeDescription
chainIduint256

Returns

NameTypeDescription
<none>bytes32

destGasLimitOf

function destGasLimitOf(uint256 chainId) internal view returns (uint256);

Parameters

NameTypeDescription
chainIduint256

Returns

NameTypeDescription
<none>uint256

supportsAttribute

No sendMessage attributes are supported by this adapter (per-dest gas is admin-configured).

function supportsAttribute(bytes4) internal pure returns (bool);

Parameters

NameTypeDescription
_bytes4

Returns

NameTypeDescription
<none>bool

quoteFee

Quotes the Mailbox fee to send payload to recipient (ERC-7930), synthesizing the same wire envelope (with the NEXT nonce) + StandardHookMetadata a real send would dispatch.

function quoteFee(bytes calldata recipient, bytes calldata payload) internal view returns (uint256);

Parameters

NameTypeDescription
recipientbytes
payloadbytes

Returns

NameTypeDescription
<none>uint256

registerDomain

Registers an EVM chainId ↔ Hyperlane domain equivalence (both directions). FAIL-LOUD identity admin: an already-mapped chainId OR domain reverts — identities are never remapped. Admin only.

function registerDomain(uint256 chainId, uint32 domain) internal;

Parameters

NameTypeDescription
chainIduint256
domainuint32

registerRemote

Sets the trusted 32-byte remote (counterpart adapter) for a chain. TUNABLE (updatable), but never zero — clearing a corridor is not supported in v1. Admin only.

function registerRemote(uint256 chainId, bytes32 remote) internal;

Parameters

NameTypeDescription
chainIduint256
remotebytes32

configureDestination

Configures a destination's handle gas limit. TUNABLE; 0 = fall back to DEFAULT_DESTINATION_GAS at send time. Admin only.

function configureDestination(uint256 chainId, uint256 gasLimit) internal;

Parameters

NameTypeDescription
chainIduint256
gasLimituint256

sendMessage

ERC-7786 source. Quotes the Mailbox fee (typed HyperlaneInsufficientFee on shortfall), then dispatches immediately via the 4-arg dispatch, forwarding msg.value WHOLE — the default hook refunds any surplus to the metadata refundAddress (msg.sender, the sending user), so the Diamond never holds or spends fee funds.

No attributes are supported — any attribute reverts UnsupportedAttribute. The envelope carries a source-owned monotonic nonce (++$._lastNonce) so the delivery id is globally unique per (source, nonce) rather than envelope content — otherwise two byte-identical-but-distinct messages would collide on delivery (one permanently dropped). Returns the suite-level sendId = keccak256(abi.encode(block.chainid, nonce)); the Hyperlane messageId is surfaced via HyperlaneMessageDispatched.

function sendMessage(bytes calldata recipient, bytes calldata payload, bytes[] calldata attributes)
        internal
        returns (bytes32);

Parameters

NameTypeDescription
recipientbytes
payloadbytes
attributesbytes[]

Returns

NameTypeDescription
<none>bytes32

handle

Mailbox delivery callback: DUAL AUTH — msg.sender must be the Mailbox AND the 32-byte sender must equal the trusted remote registered for the origin domain's chain (fail-closed on an unregistered domain, i.e. chainId 0, or an unset remote). De-dups per (chainId, sendId), marking BEFORE the external delivery (checks-effects-interactions), then delivers to the ERC-7930 recipient encoded in the envelope.

The Mailbox's own delivered map is the PROTOCOL-level replay guard (process reverts on redelivery); the per-(chainId, sendId) guard here is defense-in-depth at the suite convention level, keyed by the source-minted nonce id every sibling adapter uses. handle is forced payable by IMessageRecipient, but the adapter never expects value-bearing messages in v1 — stray value reverts HyperlaneUnexpectedValue instead of being trapped in the Diamond.

function handle(uint32 origin, bytes32 sender, bytes calldata message) internal;

Parameters

NameTypeDescription
originuint32
senderbytes32
messagebytes

_resolveDestination

Resolves recipient's chain into (domain, trusted remote, effective gas limit).

Reverts HyperlaneUnknownDestinationChain if the domain or remote is unset. An unset gas limit falls back to DEFAULT_DESTINATION_GAS (mirroring the per-dest admin gas of the LZ sibling, with a default instead of a hard revert — Hyperlane's default hook prices gas from the metadata).

function _resolveDestination(HyperlaneGatewayAdapterStorage storage $, bytes calldata recipient)
        private
        view
        returns (uint32 domain, bytes32 remote, uint256 gasLimit);

Parameters

NameTypeDescription
$HyperlaneGatewayAdapterStorage
recipientbytes

Returns

NameTypeDescription
domainuint32
remotebytes32
gasLimituint256

_buildMetadata

Synthesizes Hyperlane StandardHookMetadata variant 1 INLINE (no hook-lib dependency): variant(uint16=1) || msgValue(uint256=0) || gasLimit(uint256) || refundAddress(address). msgValue is always 0 (plain messages only in v1); refundAddress is the sending user, so overpaid IGP fees refund there.

function _buildMetadata(uint256 gasLimit, address refundAddress) private pure returns (bytes memory);

Parameters

NameTypeDescription
gasLimituint256
refundAddressaddress

Returns

NameTypeDescription
<none>bytes