Skip to content
Lattice

AcrossBridgeAdapterLib

Git Source

Title: AcrossBridgeAdapterLib

Authors: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80), Modified from Across (https://github.com/across-protocol/contracts)

Logic + ERC-7201 storage for the Across v3 intent/optimistic token-bridge adapter. Outbound deposit pulls exactly inputAmount from the caller, force-approves the SpokePool for exactly that amount, escrows the deposit intent, then resets the allowance to 0. Inbound handleV3AcrossMessage is authenticated to the SpokePool and ONLY emits (no handler registry in v1 — the diamond itself is the recipient and other facets manage the received funds).

TRUST MODEL: Across is an INTENT bridge — a relayer fronts outputToken on the destination at fill time and is later reimbursed via UMA optimistic settlement. There is NO guaranteed fill: an unfilled deposit past fillDeadline is refunded ON THIS CHAIN to the deposit's depositor, which is why the adapter ALWAYS passes depositor = msg.sender (see deposit). Inbound fills are relayer-pushed and optimistic — NOT yet UMA-finalized when handleV3AcrossMessage runs — so received funds/messages are reversible-until-finalized and are never routed into OpenBridge (no M-of-N, no receiveId). Across chain ids are passed raw by callers (no domain table, no admin surface). Reuses the shared safe-transfer / force-approve helpers (BridgeFungibleLib.pullExact, AdapterBaseLib.forceApprove); no bespoke ERC-20 plumbing.

Functions

acrossBridgeAdapterStorage

function acrossBridgeAdapterStorage() internal pure returns (AcrossBridgeAdapterStorage storage $);

Returns

NameTypeDescription
$AcrossBridgeAdapterStorage

__AcrossBridgeAdapter_init

Configures the local SpokePool and registers the IAcrossBridgeAdapter ERC-165 id.

Reverts AcrossZeroAddress if spokePool_ is zero (so an unconfigured module cannot exist). Called inside the diamond initializing window.

function __AcrossBridgeAdapter_init(address spokePool_) internal;

Parameters

NameTypeDescription
spokePool_address

registerInterface

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

function registerInterface() internal;

spokePool

function spokePool() internal view returns (address);

Returns

NameTypeDescription
<none>address

deposit

Escrows params.inputAmount of params.inputToken (pulled from msg.sender) as an Across v3 deposit intent toward the ERC-7930 params.recipient on params.destinationChainId. Strict CEI under the reentrancy guard: checks, pull exactly inputAmount, approve the SpokePool for exactly inputAmount, deposit, then reset the allowance to 0.

REFUND SAFETY (fund-safety critical): the SpokePool depositor field is who Across refunds ON THIS CHAIN when the deposit expires unfilled, and the only party able to speed up the deposit. It is ALWAYS msg.sender (the calling user) and NEVER address(this) — depositor = address(this) would strand every expired-deposit refund in the diamond. DESTINATION CROSS-CHECK (fail-closed, mirrors the ZetaChain SourceChainMismatch precedent): when the ERC-7930 recipient's chainType is eip-155 (0x0000), its chain reference must equal params.destinationChainId, else AcrossDestinationMismatch. For non-EVM chainTypes no cross-check is possible (Across uses its own numeric ids for non-EVM chains) — the recipient bytes32 and destinationChainId are then trusted as caller-supplied. NOT payable: ERC-20 inputs only in v1 (msg.value == 0 is implicit — the SpokePool reverts on non-zero value for non-wrapped-native inputs; native ETH users wrap first). No leftover sweep is needed: the SpokePool pulls exactly inputAmount and the approve reset guarantees zero standing allowance.

function deposit(IAcrossBridgeAdapter.DepositParams calldata params) internal;

Parameters

NameTypeDescription
paramsIAcrossBridgeAdapter.DepositParams

_parseAndCheckRecipient

Parses the ERC-7930 recipient (canonical widths enforced; reverts on empty/oversized/short-EVM address fields) and fail-closed cross-checks a declared eip-155 chain reference against the raw Across destinationChainId (reverts AcrossDestinationMismatch — see the deposit dev notes).

function _parseAndCheckRecipient(bytes calldata recipientBytes, uint256 destinationChainId)
        private
        pure
        returns (bytes32 recipient);

Parameters

NameTypeDescription
recipientBytesbytes
destinationChainIduint256

Returns

NameTypeDescription
recipientbytes32

_callSpokePool

The 12-arg SpokePool call lives in its own frame with the pool re-read from storage and message copied to memory (a 1-slot value instead of a 2-slot calldata slice) — both are stack-too-deep relief for deposit under the legacy codegen pipeline.

function _callSpokePool(bytes32 recipient, IAcrossBridgeAdapter.DepositParams calldata params) private;

Parameters

NameTypeDescription
recipientbytes32
paramsIAcrossBridgeAdapter.DepositParams

handleV3AcrossMessage

SpokePool-authenticated fill-time delivery hook: reverts AcrossNotSpokePool unless msg.sender is the configured SpokePool, then emits AcrossMessageReceived. NOTHING else in v1 — no handler registry (the diamond itself is the recipient; other facets manage received funds).

OPTIMISTIC: fills are relayer-pushed and NOT yet UMA-finalized when this is called — treat the received funds/message as reversible-until-finalized. Never routed into OpenBridge (no M-of-N confirmation, no receiveId).

function handleV3AcrossMessage(address tokenSent, uint256 amount, address relayer, bytes calldata message)
        internal;

Parameters

NameTypeDescription
tokenSentaddress
amountuint256
relayeraddress
messagebytes

_chainIdFromReference

Right-aligns an ERC-7930 chain-reference (<= 32 bytes, big-endian) into a uint256 chainId.

For eip-155 chains the reference IS the chainId. Reverts AcrossChainReferenceTooLong if > 32 bytes.

function _chainIdFromReference(bytes memory ref) private pure returns (uint256 chainId);

Parameters

NameTypeDescription
refbytes

Returns

NameTypeDescription
chainIduint256