StargateBridgeAdapterLib
Title: StargateBridgeAdapterLib
Authors: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80), Modified from Stargate (https://github.com/stargate-protocol/stargate-v2)
Logic + ERC-7201 storage for the Stargate v2 POOLED-LIQUIDITY token-bridge adapter (the third
token rail: neither burn/mint CCTP nor intent Across). Outbound sendToken pulls exactly
amountLD from the caller, force-approves the token's registered pool for exactly that amount,
dispatches a TAXI-mode OFT send, resets the allowance to 0, then sweeps any un-debited dust back
to the caller (pools truncate amountLD to shared decimals, so amountSentLD may be LESS than
what was pulled — the diamond nets zero).
TRUST MODEL / SCOPE: Stargate pools hold unified liquidity; the DESTINATION pool is the LayerZero
receiver that credits the recipient — no Lattice inbound surface exists (outbound-only for plain
transfers; never routed through OpenBridge, no receiveId). DEFERRED (documented, deliberate):
composeMsg/lzCompose destination hooks and BUS mode (non-empty oftCmd; issue #77 Q11) — every
send here is a taxi (empty oftCmd); native-ETH pools (StargatePoolNative), whose msg.value
mixes fee + amount — v1 is ERC-20 ONLY and msg.value is the LayerZero fee EXCLUSIVELY. REFUND
SAFETY: refundAddress is ALWAYS msg.sender — excess LayerZero fee refunds go to the USER, never
the diamond (same stranding class as the Across depositor). Reuses the shared safe-transfer /
force-approve helpers (BridgeFungibleLib.pullExact, AdapterBaseLib.forceApprove); no bespoke
ERC-20 plumbing.
Functions
stargateBridgeAdapterStorage
function stargateBridgeAdapterStorage() internal pure returns (StargateBridgeAdapterStorage storage $);Returns
| Name | Type | Description |
|---|---|---|
| $ | StargateBridgeAdapterStorage |
__StargateBridgeAdapter_init
Registers the IStargateBridgeAdapter ERC-165 id. Called inside the diamond initializing window. No protocol wiring happens at init — Stargate is per-token pools, all admin-registered (registerStargateEid/registerPool) after deploy.
function __StargateBridgeAdapter_init() internal;registerInterface
Writes true to the ERC-165 map slot for IStargateBridgeAdapter.
function registerInterface() internal;stargateEidOf
function stargateEidOf(uint256 chainId) internal view returns (uint32);Parameters
| Name | Type | Description |
|---|---|---|
| chainId | uint256 |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint32 |
stargateChainIdOf
function stargateChainIdOf(uint32 eid) internal view returns (uint256);Parameters
| Name | Type | Description |
|---|---|---|
| eid | uint32 |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
poolOf
function poolOf(address token) internal view returns (address);Parameters
| Name | Type | Description |
|---|---|---|
| token | address |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
quoteSendFee
Quotes the LayerZero native fee of p, building the IDENTICAL SendParam (same
_buildSendParam builder, same checks) the sendToken path dispatches — quote and send can
never drift.
function quoteSendFee(IStargateBridgeAdapter.SendTokenParams calldata p) internal view returns (uint256 nativeFee);Parameters
| Name | Type | Description |
|---|---|---|
| p | IStargateBridgeAdapter.SendTokenParams |
Returns
| Name | Type | Description |
|---|---|---|
| nativeFee | uint256 |
registerStargateEid
Registers an EVM chainId ↔ LayerZero eid equivalence (both directions). FAIL-LOUD identity
admin: an already-mapped chainId OR eid reverts — identities are never remapped. chainId 0
and eid 0 are rejected (0 is each map's unset sentinel). Admin only.
Stargate rides LayerZero, so this eid equals the LayerZero adapter's eid for the same chain — recorded in this adapter's OWN map (adapters never share hot-path storage).
function registerStargateEid(uint256 chainId, uint32 eid) internal;Parameters
| Name | Type | Description |
|---|---|---|
| chainId | uint256 | |
| eid | uint32 |
registerPool
Registers token's LOCAL Stargate pool. FAIL-LOUD identity per token
(StargateTokenAlreadyRegistered — a pool upgrade cannot re-point a token entry in v1;
deliberate YAGNI) and FAIL-CLOSED: IStargate(pool).token() must equal token
(StargatePoolTokenMismatch) — a mis-registered pool would burn user approvals against the
wrong asset. Admin only.
ERC-20 pools ONLY in v1: a StargatePoolNative reports token() == address(0) and is rejected
by the cross-check against the non-zero token argument.
function registerPool(address token, address pool) internal;Parameters
| Name | Type | Description |
|---|---|---|
| token | address | |
| pool | address |
sendToken
Sends p.amountLD of p.token (pulled from msg.sender) through its registered Stargate
pool toward the ERC-7930 p.recipient on p.destinationChainId. Strict CEI under the
reentrancy guard: checks + fee precheck, pull exactly amountLD, approve the pool for exactly
amountLD, send (taxi), reset the allowance to 0, sweep un-debited dust back to the caller.
FEE PRECHECK (mirrors the Hyperlane sibling): quoteSend(...).nativeFee is compared against
msg.value BEFORE any funds move — a shortfall reverts StargateInsufficientFee instead of
bubbling an opaque pool revert after the pull. msg.value is then forwarded WHOLE as the
MessagingFee.nativeFee with refundAddress = msg.sender, so any excess refunds to the USER,
never the diamond (REFUND-CRITICAL — same stranding class as the Across depositor).
DUST-CRITICAL: the pool debits oftReceipt.amountSentLD, which may be LESS than amountLD
(shared-decimal truncation) — the exact leftover (balance now - snapshot) is transferred back
to msg.sender, so the diamond nets zero even for dusty amounts.
function sendToken(IStargateBridgeAdapter.SendTokenParams calldata p)
internal
returns (bytes32 guid, uint256 amountSentLD, uint256 amountReceivedLD);Parameters
| Name | Type | Description |
|---|---|---|
| p | IStargateBridgeAdapter.SendTokenParams |
Returns
| Name | Type | Description |
|---|---|---|
| guid | bytes32 | |
| amountSentLD | uint256 | |
| amountReceivedLD | uint256 |
_buildSendParam
Validates p and builds the TAXI-mode SendParam both the quote and send paths use: empty
extraOptions (pool default executor options), empty composeMsg (no lzCompose hook) and
empty oftCmd (taxi — bus mode deferred, issue #77 Q11).
Check order: pool registered, non-zero amount, non-zero minAmount (mandatory slippage floor — pools charge fees so output < input; 0 would mean unlimited slippage), not same-chain, eid registered, then the ERC-7930 recipient parse + fail-closed eip-155 cross-check.
function _buildSendParam(IStargateBridgeAdapter.SendTokenParams calldata p)
private
view
returns (address pool, SendParam memory sendParam);Parameters
| Name | Type | Description |
|---|---|---|
| p | IStargateBridgeAdapter.SendTokenParams |
Returns
| Name | Type | Description |
|---|---|---|
| pool | address | |
| sendParam | SendParam |
_callPool
The pool call lives in its own frame (returns collapse the two receipts into scalars) — stack
relief for sendToken under the legacy codegen pipeline. Forwards msg.value WHOLE as the
native fee; refundAddress = msg.sender (REFUND-CRITICAL, see sendToken). The returned bus
Ticket is ignored: taxi-mode sends always return an empty one.
function _callPool(address pool, SendParam memory sendParam)
private
returns (bytes32 guid, uint256 amountSentLD, uint256 amountReceivedLD);Parameters
| Name | Type | Description |
|---|---|---|
| pool | address | |
| sendParam | SendParam |
Returns
| Name | Type | Description |
|---|---|---|
| guid | bytes32 | |
| amountSentLD | uint256 | |
| amountReceivedLD | uint256 |
_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
destinationChainId (reverts StargateDestinationMismatch — mirrors the Across sibling).
For non-EVM chainTypes no cross-check is possible; the bytes32 is trusted as caller-supplied.
function _parseAndCheckRecipient(bytes calldata recipientBytes, uint256 destinationChainId)
private
pure
returns (bytes32 recipient);Parameters
| Name | Type | Description |
|---|---|---|
| recipientBytes | bytes | |
| destinationChainId | uint256 |
Returns
| Name | Type | Description |
|---|---|---|
| recipient | bytes32 |
_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 StargateChainReferenceTooLong if > 32 bytes.
function _chainIdFromReference(bytes memory ref) private pure returns (uint256 chainId);Parameters
| Name | Type | Description |
|---|---|---|
| ref | bytes |
Returns
| Name | Type | Description |
|---|---|---|
| chainId | uint256 |