Skip to main content

Overview

The Arbitrum_SpokePool is deployed on Arbitrum One and Arbitrum Nova. It implements Arbitrum’s unique address aliasing mechanism for cross-chain admin verification and integrates with Arbitrum’s native token gateway for bridging tokens back to L1. Contract: contracts/Arbitrum_SpokePool.sol

Key Characteristics

  • Address aliasing: Uses Arbitrum-specific L1→L2 address transformation for admin verification
  • Custom bridge integration: Integrates with Arbitrum’s L2 Gateway Router for token withdrawals
  • Token whitelisting: Maintains mapping of L2→L1 token addresses required by Arbitrum bridge
  • CCTP support: Integrates Circle’s CCTP for USDC transfers
  • OFT support: Supports LayerZero OFT tokens as alternative bridging mechanism

Inheritance

contract Arbitrum_SpokePool is SpokePool, CircleCCTPAdapter
  • Inherits base SpokePool functionality
  • Inherits CircleCCTPAdapter for USDC bridging via Circle CCTP

Constructor

constructor(
    address _wrappedNativeTokenAddress,
    uint32 _depositQuoteTimeBuffer,
    uint32 _fillDeadlineBuffer,
    IERC20 _l2Usdc,
    ITokenMessenger _cctpTokenMessenger,
    uint32 _oftDstEid,
    uint256 _oftFeeCap
)
    SpokePool(
        _wrappedNativeTokenAddress,
        _depositQuoteTimeBuffer,
        _fillDeadlineBuffer,
        _oftDstEid,
        _oftFeeCap
    )
    CircleCCTPAdapter(
        _l2Usdc,
        _cctpTokenMessenger,
        CircleDomainIds.Ethereum
    )
Parameters:
  • _wrappedNativeTokenAddress: Address of WETH on Arbitrum
  • _depositQuoteTimeBuffer: Max age for deposit quote timestamps
  • _fillDeadlineBuffer: Max future offset for fill deadlines
  • _l2Usdc: Circle USDC address on Arbitrum (or 0x0 to disable CCTP)
  • _cctpTokenMessenger: Circle TokenMessenger contract for CCTP bridging
  • _oftDstEid: LayerZero endpoint ID for OFT messaging
  • _oftFeeCap: Maximum fee for OFT transfers

Initialization

function initialize(
    uint32 _initialDepositId,
    address _l2GatewayRouter,
    address _crossDomainAdmin,
    address _withdrawalRecipient
) public initializer
Parameters:
  • _initialDepositId: Starting deposit nonce
  • _l2GatewayRouter: Address of Arbitrum’s L2 Gateway Router for token bridging
  • _crossDomainAdmin: L1 HubPool address (will be aliased for verification)
  • _withdrawalRecipient: Address receiving bridged tokens on L1 (typically HubPool)

Admin Verification

_requireAdminSender()

function _requireAdminSender() internal override onlyFromCrossDomainAdmin {}

onlyFromCrossDomainAdmin Modifier

modifier onlyFromCrossDomainAdmin() {
    require(
        msg.sender == CrossDomainAddressUtils.applyL1ToL2Alias(crossDomainAdmin),
        "ONLY_COUNTERPART_GATEWAY"
    );
    _;
}
Arbitrum-specific logic: When L1 contracts call L2 contracts on Arbitrum, the msg.sender address is transformed using an alias to prevent address collisions:
// From CrossDomainAddressUtils library
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
    uint160 offset = uint160(0x1111000000000000000000000000000000001111);
    l2Address = address(uint160(l1Address) + offset);
}
This ensures that messages from HubPool on L1 are properly authenticated when received on L2.

Token Bridging

_bridgeTokensToHubPool()

function _bridgeTokensToHubPool(
    uint256 amountToReturn,
    address l2TokenAddress
) internal override {
    address oftMessenger = _getOftMessenger(l2TokenAddress);

    // If the l2TokenAddress is USDC, use the CCTP bridge
    if (_isCCTPEnabled() && l2TokenAddress == address(usdcToken)) {
        _transferUsdc(withdrawalRecipient, amountToReturn);
    }
    // If token has OFT messenger, use LayerZero OFT
    else if (oftMessenger != address(0)) {
        _fundedTransferViaOft(
            IERC20(l2TokenAddress),
            IOFT(oftMessenger),
            withdrawalRecipient,
            amountToReturn
        );
    }
    // Otherwise use Arbitrum's native gateway
    else {
        address ethereumTokenToBridge = whitelistedTokens[l2TokenAddress];
        require(ethereumTokenToBridge != address(0), "Uninitialized mainnet token");
        
        ArbitrumL2ERC20GatewayLike(l2GatewayRouter).outboundTransfer(
            ethereumTokenToBridge,  // _l1Token - L1 token address
            withdrawalRecipient,    // _to - Recipient on L1
            amountToReturn,         // _amount
            ""                      // _data
        );
    }
}
Three bridging mechanisms:
  1. CCTP for USDC: Uses Circle’s Cross-Chain Transfer Protocol for native USDC
  2. LayerZero OFT: For tokens with configured OFT messengers
  3. Arbitrum Gateway: Default mechanism using l2GatewayRouter.outboundTransfer()

Token Whitelisting

Arbitrum’s bridge requires specifying the L1 token address when bridging. The contract maintains a mapping:
mapping(address => address) public whitelistedTokens; // L2 token => L1 token

whitelistToken()

function whitelistToken(
    address l2Token,
    address l1Token
) public onlyAdmin nonReentrant {
    _whitelistToken(l2Token, l1Token);
}

function _whitelistToken(address _l2Token, address _l1Token) internal {
    whitelistedTokens[_l2Token] = _l1Token;
    emit WhitelistedTokens(_l2Token, _l1Token);
}
Purpose: Maps L2 token addresses to their L1 counterparts so the bridge knows which L1 token to mint/release.

Admin Functions

setL2GatewayRouter()

function setL2GatewayRouter(
    address newL2GatewayRouter
) public onlyAdmin nonReentrant {
    _setL2GatewayRouter(newL2GatewayRouter);
}

function _setL2GatewayRouter(address _l2GatewayRouter) internal {
    l2GatewayRouter = _l2GatewayRouter;
    emit SetL2GatewayRouter(l2GatewayRouter);
}
Purpose: Updates the Arbitrum L2 Gateway Router address used for token bridging.

State Variables

// Address of the Arbitrum L2 token gateway to send funds to L1
address public l2GatewayRouter;

// Admin controlled mapping of arbitrum tokens to L1 counterpart
mapping(address => address) public whitelistedTokens;

Events

event SetL2GatewayRouter(address indexed newL2GatewayRouter);
event WhitelistedTokens(address indexed l2Token, address indexed l1Token);

Unique Features

  1. Address aliasing: Arbitrum’s unique L1→L2 address transformation for security
  2. Multi-bridge support: CCTP, OFT, and native Arbitrum bridge
  3. Token mapping requirement: L2 tokens must be whitelisted with their L1 counterparts
  4. Gateway router pattern: Uses Arbitrum’s router contract to route to appropriate token gateways

Architecture Notes

  • HubPool on L1 sends cross-chain messages through Arbitrum’s Inbox contract
  • The L2 message sender address is aliased by adding a fixed offset
  • The onlyFromCrossDomainAdmin modifier checks the aliased address
  • Token bridging prioritizes CCTP for USDC, then OFT, then native gateway
  • Requires pre-configuration of L2→L1 token mappings via whitelistToken()