SkyBridgeEntryPoint
SkyBridgeEntryPoint is the user-facing contract on every chain. It is deployed as an ERC-1967 / UUPS upgradeable proxy. Per-chain addresses are listed in Official Addresses.
Version: 6.3.0 (constant version on the implementation). 6.3.0 added Rail 5 (Arbitrum Canonical); it is only active on the L1 EntryPoints (Ethereum, Sepolia) - the arbitrumInboxes registry simply stays empty elsewhere.
Roles
The EntryPoint has three roles:
| Role | bytes32 identifier | Purpose |
|---|---|---|
DEFAULT_ADMIN_ROLE | bytes32(0) | Grant/revoke all roles; set fee, diamond, token messenger, fee collector; add/remove chains; upgrade |
PAUSER_ROLE | keccak256("aviator.pauser_role") | Call setPaused(true/false) |
OPERATOR_ROLE | keccak256("OPERATOR_ROLE") | Register/remove OP Standard Bridge L1→L2 token mappings |
DEFAULT_ADMIN_ROLE can also call setPaused directly.
Fee model
Every bridge transaction pays a flat fee in native currency (ETH, POL, AVAX, or BNB depending on chain). The fee is set by the admin via setFlatFee(uint256) and readable as flatFee.
Individual tokens can be exempt from the flat fee (setTokenFeeExempt). The effective fee for a given token is returned by getEffectiveFee(address token) (returns 0 if exempt, flatFee otherwise).
CCTP fast route charges an additional percentage fee retained as USDC in the EntryPoint: fastCctpFeeBps basis points (currently 25 = 0.25%). This is separate from the flat native fee.
Accumulated fees are collected permissionlessly by anyone calling collectFees() (ETH) or collectTokenFees(address token) (ERC-20). Both functions sweep the full balance to the feeCollector address (the Fee Safe 0xA953B9DF3b081709eA75895cF5a8fAf7DCC29354). There is no FEE_COLLECTOR_ROLE - fee custody is a stored address, not a role.
Rail routing
Rail 1 - OP Standard Bridge
Forwards ETH or listed ERC-20s to the OP L1StandardBridge on OP-stack L2s (Base, Optimism, Unichain, Soneium, Ink). The contract source comments label this rail "legacy" and recommend CCIP for new integrations, but it remains an available, supported rail.
Key functions:
| Function | Description |
|---|---|
bridgeOPStandardETH(chainId, minGasLimit, extraData) | Bridge ETH to an OP-stack L2 |
bridgeOPStandardETHTo(chainId, to, ...) | Bridge ETH to a specific recipient |
bridgeOPStandardERC20(chainId, l1Token, amount, ...) | Bridge an ERC-20 (must be registered in the L1→L2 token registry) |
bridgeOPStandardERC20To(chainId, l1Token, to, ...) | Bridge ERC-20 to a specific recipient |
The L2 token for each l1Token is resolved from an on-chain registry (opBridgeL2Tokens); the caller cannot specify the L2 token directly.
Rail 3 - Chainlink CCIP
Routes through the Diamond (BridgeFacet / ERC721BridgeFacet). Works for any CCIP-supported ERC-20 and ERC-721 across all 11 chains.
Key functions:
| Function | Returns | Description |
|---|---|---|
bridgeCCIP(token, amount, chainSelector, receiver) | bytes32 messageId | Bridge an ERC-20 via CCIP |
bridgeCCIPNativeETH(amount, chainSelector, receiver) | bytes32 messageId | Bridge native ETH (Diamond wraps to WETH internally; only works on chains with a registered WETH CCIP pool) |
bridgeCCIPNFT(token, tokenId, chainSelector, receiver) | bytes32 messageId | Bridge a single ERC-721 |
bridgeCCIPNFTBatch(token, tokenIds[], chainSelector, receiver) | bytes32 messageId | Bridge multiple NFTs from the same collection |
getBridgeCCIPQuote(token, amount, chainSelector, receiver) | (ccipFee, protocolFee, totalFee) | Get fee quote before bridging |
getBridgeCCIPNFTQuote(token, tokenId, chainSelector, receiver) | (ccipFee, protocolFee, totalFee) | Get NFT fee quote |
Fee-on-transfer tokens are rejected automatically (pre/post balance check). Blacklisted tokens revert with TokenBlacklisted.
Rail 4 - Circle CCTP
Burns USDC via Circle's TokenMessengerV2 using the Forwarding Service hook, which instructs Circle's relayer to auto-mint on the destination chain.
Key function:
| Parameter | Description |
|---|---|
token | USDC address on this chain |
amount | Total USDC (protocol fee deducted before burn on fast route) |
destinationDomain | Circle CCTP destination domain ID |
mintRecipient | Recipient (bytes32-padded) |
maxFee | Maximum fee for Circle's Forwarding Service market maker |
minFinalityThreshold | 1000 = Fast, 2000 = Standard |
The 0.25% fast-route fee is retained as USDC in the EntryPoint (swept later via collectTokenFees(usdc)). burnAmount must exceed maxFee after the deduction.
Rail 5 - Arbitrum Canonical (native ETH)
Moves native ETH between Ethereum and Arbitrum-stack chains by calling Arbitrum's canonical contracts directly - no Diamond involvement. Native ETH only. Deposits go through the destination chain's Delayed Inbox (IArbitrumInbox.createRetryableTicket) and are only active on the L1 EntryPoints; withdrawals go through the ArbSys precompile and are only active on the L2 EntryPoints.
Key functions:
| Function | Description |
|---|---|
bridgeArbitrumETHTo(chainId, to, amount, maxSubmissionCost, gasLimit, maxFeePerGas) | Deposit native ETH to a specific recipient on an Arbitrum-stack chain via a retryable ticket |
getBridgeArbitrumETHQuote(chainId, gasLimit, maxFeePerGas) | Returns (submissionFee, gasFunds, protocolFee, totalExtra) - quote everything except the deposit amount itself |
bridgeArbitrumETHWithdraw(to) | Withdraw native ETH from an Arbitrum-stack chain back to to on the parent chain via ArbSys.withdrawEth |
bridgeArbitrumETHTo requires exact msg.value: flatFee + amount + maxSubmissionCost + gasLimit * maxFeePerGas. Any mismatch reverts InsufficientETHForFee - unlike Rail 3's CCIP fee, there is no overpayment-as-dust allowance on this rail, because all gas surplus is refunded to the user automatically on L2 by the retryable-ticket mechanism.
Both the excess-fee refund address and the call-value refund address (the ticket's beneficiary, who can cancel or reclaim funds on a 7-day expiry) are set to _to - never the EntryPoint. gasLimit and maxFeePerGas must both be greater than 1 (the values 0/1 are Arbitrum's reserved gas-estimation sentinels) or the call reverts InvalidRetryableGasParams.
Emits ArbitrumETHBridgeInitiated(sender, to, chainId, amount, maxSubmissionCost, gasLimit, maxFeePerGas, ticketNumber, fee), where ticketNumber is the L1 message number returned by the Inbox.
Withdrawals
bridgeArbitrumETHWithdraw(to) takes the opposite path and is much simpler - there are no retryable gas parameters, because a canonical exit is asynchronous rather than auto-redeemed. Unlike the deposit function it does not require an exact msg.value: the withdrawn amount is msg.value - flatFee, so any surplus is simply withdrawn along with the rest. msg.value must exceed flatFee or the call reverts InsufficientETHForFee.
The function guards on the ArbSys precompile having code, reverting WithdrawalNotSupportedOnChain on any chain where it is absent - which is what keeps this rail off the parent chain. A zero to reverts ZeroAddress.
Emits ArbitrumETHWithdrawalInitiated(sender, to, chainId, amount, withdrawalId, fee), where withdrawalId is the value returned by ArbSys.withdrawEth and identifies the exit for claiming on the parent chain after the ~7-day challenge period.
Chain registry (Rail 1)
OP-stack chain registrations are managed via addChain / updateChain / removeChain (admin). L1→L2 token mappings for Rail 1 are batch-registered via setOpBridgeTokens (operator) and queried via getOpBridgeL2Token(chainId, l1Token).
Inbox registry (Rail 5)
Arbitrum Delayed Inbox addresses are managed per destination chainId via setArbitrumInbox(chainId, inbox) (DEFAULT_ADMIN_ROLE) - setting the inbox to address(0) removes the entry. Registered inboxes are readable via the public arbitrumInboxes(chainId) mapping. Admin changes emit ArbitrumInboxChanged(chainId, previousInbox, newInbox, executedBy).
Unlike Rail 1's chain registry, there is no on-chain enumeration of registered chain IDs - the API and UI already know the supported pair list, mirroring the minimal design of Rail 1's registry.
Upgradeability
_authorizeUpgrade is restricted to DEFAULT_ADMIN_ROLE. The reinitializer(2) slot is reserved for the first post-launch state migration and must not be used for no-op upgrades.
Emergency recovery
Admin-only functions recoverETH, recoverERC20, and recoverERC721 allow retrieving assets stuck in the EntryPoint.