Skip to content
Lattice

StarknetGatewayAdapterLib

Git Source

Title: StarknetGatewayAdapterLib

Authors: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80), Modified from Starknet (https://github.com/starkware-libs/cairo-lang)

Logic + ERC-7201 storage for the L1-side Starknet L1 <-> L2 connector. Outbound sendMessage parses the ERC-7930 felt252 recipient, felt-chunk encodes the payload, escrows msg.value as the non-refundable message fee via sendMessageToL2, and records the caller as the message's initiator; startCancellation/cancel re-derive the message hash from the original inputs and are gated to that initiator (the DIAMOND is the L1 sender on the core, so cancellation authority must be re-derived at the facet level — an ungated cancel would let anyone grief in-flight messages); inbound consumeMessage is a permissionless keeper-driven PULL against the core's per-message counter, allow-listed by trusted L2 sender.

TRUST MODEL: the Starknet core is the authenticator on BOTH directions — outbound it escrows the fee and mints the nonce, inbound consumeMessageFromL2 only consumes messages addressed to msg.sender (the diamond) and reverts when the counter is zero. The fee is ESCROWED AND NEVER REFUNDED, cancellation included. DELIBERATELY BESPOKE (not IERC7786GatewaySource): the inbound path is a pull-based consume against a counter (no message id to dedup), attributes cannot express the escrowed fee + cancellation lifecycle, and the wire payload is a felt array — see IStarknetGatewayAdapter. Emit-only inbound in v1; never routed into CrosschainLink / OpenBridge.

Constants

CHUNK_BOUND

One byte more than a felt chunk's 31-byte capacity — every valid chunk is &lt; 2**248.

uint256 private constant CHUNK_BOUND = 1 << 248;

Functions

starknetGatewayAdapterStorage

function starknetGatewayAdapterStorage() internal pure returns (StarknetGatewayAdapterStorage storage $);

Returns

NameTypeDescription
$StarknetGatewayAdapterStorage

__StarknetGatewayAdapter_init

Configures the Starknet core + expected chain reference and registers the IStarknetGatewayAdapter ERC-165 id.

Reverts StarknetZeroAddress on a zero core, StarknetEmptyChainReference on an empty chain reference (an unconfigured adapter must not exist). Called inside the diamond initializing window.

function __StarknetGatewayAdapter_init(address starknetCore_, bytes memory expectedChainReference_) internal;

Parameters

NameTypeDescription
starknetCore_address
expectedChainReference_bytes

registerInterface

Writes true to the ERC-165 map slot for IStarknetGatewayAdapter.

function registerInterface() internal;

starknetCore

function starknetCore() internal view returns (address);

Returns

NameTypeDescription
<none>address

expectedChainReference

function expectedChainReference() internal view returns (bytes memory);

Returns

NameTypeDescription
<none>bytes

l1HandlerSelector

function l1HandlerSelector(uint256 l2Target) internal view returns (uint256);

Parameters

NameTypeDescription
l2Targetuint256

Returns

NameTypeDescription
<none>uint256

isTrustedL2Sender

function isTrustedL2Sender(uint256 fromAddress) internal view returns (bool);

Parameters

NameTypeDescription
fromAddressuint256

Returns

NameTypeDescription
<none>bool

initiatorOf

function initiatorOf(bytes32 msgHash) internal view returns (address);

Parameters

NameTypeDescription
msgHashbytes32

Returns

NameTypeDescription
<none>address

starknetSelector

The Starknet selector (starknet_keccak) of an entry-point name: keccak256 masked to its low 250 bits. l1_handler selectors are starknet_keccak values of the handler's Cairo name.

function starknetSelector(string calldata name) internal pure returns (uint256);

Parameters

NameTypeDescription
namestring

Returns

NameTypeDescription
<none>uint256

registerL2Handler

Registers the l1_handler selector to invoke when sending to l2Target. Both must be non-zero felt252s (StarknetNotAFelt) — selectors are starknet_keccak values that CANNOT be derived from the ERC-7930 address, so each L2 target is registered with its handler by the admin. Admin only.

function registerL2Handler(uint256 l2Target, uint256 selector) internal;

Parameters

NameTypeDescription
l2Targetuint256
selectoruint256

setTrustedL2Sender

Sets the trusted flag of the L2 sender fromAddress (a non-zero felt252) for the inbound consume path. Admin only.

function setTrustedL2Sender(uint256 fromAddress, bool trusted) internal;

Parameters

NameTypeDescription
fromAddressuint256
trustedbool

sendMessage

Sends payload (felt-chunk encoded) to the ERC-7930 Starknet recipient's registered l1_handler, forwarding msg.value as the message fee, and records msg.sender as the message's initiator (the only party able to cancel it).

FEE WARNING: the fee is escrowed by the Starknet core and NEVER refunded — cancellation included. The adapter only requires it to be non-zero (StarknetZeroFee); the core bounds the max (getMaxL1MsgFee, bubbling MAX_L1_MSG_FEE_EXCEEDED). Fail-closed destination checks (mirroring the Across/ZetaChain cross-check precedent): the chainType must be the starknet key (StarknetWrongChainType) and the chain reference must equal the configured one (StarknetChainReferenceMismatch). nonReentrant with CEI — the only effect (the initiator record) depends on the core-returned msgHash, so it lands right after the call, inside the guard.

function sendMessage(bytes calldata recipient, bytes calldata payload)
        internal
        returns (bytes32 msgHash, uint256 nonce);

Parameters

NameTypeDescription
recipientbytes
payloadbytes

Returns

NameTypeDescription
msgHashbytes32
nonceuint256

startCancellation

Starts the two-step cancellation of a previously sent message. Initiator only.

The DIAMOND is the L1 sender on the core, so only the diamond can call the core's cancellation entrypoints — authority is re-derived here: toFelt/felts are recomputed deterministically from the ORIGINAL recipient/payload, the message hash is recomputed via core.l1ToL2MsgHash(address(this), ...), and the caller must be the recorded initiator (StarknetNotInitiator) BEFORE anything is forwarded to the core. The selector is the SEND-TIME selector, passed EXPLICITLY (it is emitted in StarknetMessageSent) and deliberately NOT re-read from the mutable _l1HandlerSelector registry — re-registering a target's handler while a message is in flight would otherwise change the re-derived hash and permanently lock the initiator out of cancelling (review finding). A wrong selector is harmless: it re-derives a different hash whose initiator record is empty. The core enforces the messageCancellationDelay() wait before cancel.

function startCancellation(bytes calldata recipient, uint256 selector, bytes calldata payload, uint256 nonce)
        internal
        returns (bytes32 msgHash);

Parameters

NameTypeDescription
recipientbytes
selectoruint256
payloadbytes
nonceuint256

Returns

NameTypeDescription
msgHashbytes32

cancel

Completes the cancellation started by startCancellation (same arguments), at least messageCancellationDelay() seconds later. Initiator only; clears the initiator record on success. The escrowed fee is NOT refunded. selector is the send-time selector (see startCancellation for why it is explicit rather than registry-derived).

function cancel(bytes calldata recipient, uint256 selector, bytes calldata payload, uint256 nonce)
        internal
        returns (bytes32 msgHash);

Parameters

NameTypeDescription
recipientbytes
selectoruint256
payloadbytes
nonceuint256

Returns

NameTypeDescription
msgHashbytes32

consumeMessage

Permissionless keeper-driven PULL: consumes an L2 -> L1 message from the trusted L2 sender fromAddress addressed to this diamond (StarknetUntrustedSender otherwise). Emit-only in v1.

The core is the real authenticator — consumeMessageFromL2 only consumes messages addressed to msg.sender (the diamond) and reverts when the per-message counter is zero. COUNTER SEMANTICS: duplicates are N DISTINCT consumes, NOT replays — each consume decrements a core-side counter, so consuming the same payload twice requires the L2 to have SENT it twice. NOT routed into CrosschainLink / OpenBridge (there is no message id to dedup or M-of-N confirm on).

function consumeMessage(uint256 fromAddress, bytes calldata payload) internal returns (bytes32 msgHash);

Parameters

NameTypeDescription
fromAddressuint256
payloadbytes

Returns

NameTypeDescription
msgHashbytes32

encodeFelts

Encodes payload bytes into felts per the LATTICE FELT-CHUNK CONVENTION v1 — the wire format the Cairo l1_handler must mirror: felts[0] = payload.length (byte count); felts[1..] = consecutive 31-byte big-endian chunks of the payload, the last chunk right-padded with zeros.

Each chunk is &lt; 2**248 &lt; FIELD_PRIME by construction. An empty payload encodes to [0]. Exact inverse of decodeFelts.

function encodeFelts(bytes memory payload) internal pure returns (uint256[] memory felts);

Parameters

NameTypeDescription
payloadbytes

Returns

NameTypeDescription
feltsuint256[]

decodeFelts

Decodes a LATTICE FELT-CHUNK CONVENTION v1 felt array back into the original bytes payload.

STRICT exact inverse of encodeFelts: reverts StarknetMalformedFeltPayload on a missing/wrong length prefix, a chunk >= 2**248, or non-zero padding bits in the final chunk — so decodeFelts(encodeFelts(x)) == x and every accepted felt array is canonical.

function decodeFelts(uint256[] memory felts) internal pure returns (bytes memory payload);

Parameters

NameTypeDescription
feltsuint256[]

Returns

NameTypeDescription
payloadbytes

_parseAndCheckRecipient

Parses the ERC-7930 recipient into a validated felt252 and fail-closed checks it targets THIS adapter's Starknet chain, then resolves the registered l1_handler selector (send path only — cancellation takes the send-time selector explicitly, see startCancellation).

Reverts StarknetTargetNotRegistered when no selector is registered for the target.

function _parseAndCheckRecipient(StarknetGatewayAdapterStorage storage $, bytes calldata recipient)
        private
        view
        returns (uint256 toFelt, uint256 selector);

Parameters

NameTypeDescription
$StarknetGatewayAdapterStorage
recipientbytes

Returns

NameTypeDescription
toFeltuint256
selectoruint256

_parseRecipientFelt

Parses the ERC-7930 recipient into a validated felt252 target on THIS adapter's Starknet chain.

Reverts StarknetWrongChainType on a non-starknet chainType, StarknetChainReferenceMismatch on a foreign chain reference. Felt range/zero checks live in NonEvmAddress.parseV1ToFelt252.

function _parseRecipientFelt(StarknetGatewayAdapterStorage storage $, bytes calldata recipient)
        private
        view
        returns (uint256 toFelt);

Parameters

NameTypeDescription
$StarknetGatewayAdapterStorage
recipientbytes

Returns

NameTypeDescription
toFeltuint256

_checkFelt

Requires value to be a valid non-zero felt252 (0 &lt; value &lt; FIELD_PRIME).

function _checkFelt(uint256 value) private pure;

Parameters

NameTypeDescription
valueuint256