Overview
TheZkSync_SpokePool is deployed on ZkSync Era. It implements ZkSync’s unique bridging system with L2AssetRouter for ERC20 tokens, special ETH handling, and optional USDC bridges (either CCTP or Matter Labs’ custom bridge).
Contract: contracts/ZkSync_SpokePool.sol
Important: This contract must be compiled with @matterlabs/hardhat-zksync-solc, not standard Solidity compiler.
Key Characteristics
- L2AssetRouter integration: Uses ZkSync’s asset router for token withdrawals
- Asset ID calculations: Computes asset IDs from L1 token addresses and chain ID
- Dual USDC bridges: Supports both CCTP and Matter Labs’ custom USDC bridge
- ETH as ERC20: Handles ZkSync’s unique ETH implementation
- No EIP-7702 support: ZkSync Era VM does not support EIP-7702 delegated wallets
- Address aliasing: Uses same L1→L2 address transformation as Arbitrum
- Constructor parameters: Uses constructor parameters instead of constants for gas optimization on zkEVM
Inheritance
- Inherits base
SpokePoolfunctionality - Inherits
CircleCCTPAdapterfor optional CCTP bridging
Constructor
_wrappedNativeTokenAddress: Address of WETH on ZkSync Era_circleUSDC: Circle USDC address (if using CCTP) or bridged USDC (if using zkUSDCBridge)_zkUSDCBridge: Matter Labs’ custom USDC bridge (or address(0) to use CCTP)_l1ChainId: Chain ID of L1 (used in asset ID calculation)_cctpTokenMessenger: Circle TokenMessenger for CCTP (or address(0) to use zkUSDCBridge)_depositQuoteTimeBuffer: Max age for deposit quote timestamps_fillDeadlineBuffer: Max future offset for fill deadlines
Initialization
_initialDepositId: Starting deposit nonce_crossDomainAdmin: L1 HubPool address (will be aliased for verification)_withdrawalRecipient: Address receiving bridged tokens on L1 (typically HubPool)
l2Eth to ZkSync’s system contract for ETH (0x800A).
Admin Verification
_requireAdminSender()
onlyFromCrossDomainAdmin Modifier
msg.sender is the aliased address.
Token Bridging
_bridgeTokensToHubPool()
- ETH/WETH: Unwrap to ETH, call
IL2ETH.withdraw() - USDC: Use CCTP or Matter Labs’ zkUSDCBridge
- Other ERC20s: Use L2AssetRouter with computed asset ID
Asset ID Calculation
_getAssetId()
- Token must have been bridged from L1 via L2AssetRouter
- Era-native tokens without L1 mapping will revert with
InvalidTokenAddress
_encodeBridgeBurnData()
l2AssetRouter.withdraw() call.
ETH Handling
L2 ETH System Contract
_preExecuteLeafHook()
EIP-7702 Support
_is7702DelegatedWallet()
false.
ZkSync Bridge Interfaces
IL2AssetRouter
0x10003 (user contracts offset + 3)
ZkBridgeLike (Legacy USDC Bridge)
IL2ETH
0x800A.
State Variables
Constants
0x10003: L2AssetRouter0x10004: L2NativeTokenVault0x800A: L2ETH
Events
Errors
Unique Features
- Asset ID calculation: Unique to ZkSync’s bridge architecture
- Dual USDC bridges: Supports both CCTP and Matter Labs’ custom bridge
- ETH as system contract: Special handling for ZkSync’s ETH implementation
- Constructor optimization: Uses constructor params instead of constants for zkEVM gas savings
- No EIP-7702: Explicit override to disable delegated wallet detection
- L1 chain ID parameter: Needed for multi-chain ZkSync deployments (elastic chains)
Architecture Notes
- ZkSync uses the same address aliasing mechanism as Arbitrum
- Token bridging requires looking up L1 token address from L2AssetRouter
- Asset IDs are computed from L1 chain ID + L2NativeTokenVault address + L1 token address
- USDC can use either CCTP or Matter Labs’ bridge, but not both
- Contract must be compiled with ZkSync-specific compiler
- System contracts are deployed at predefined addresses in the
0x10000+range
Compilation Notes
From contract comments:- Compile with:
@matterlabs/hardhat-zksync-solc - Docs: https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-solc.html
Related Contracts
- SpokePool - Base contract
- ZkSync_Adapter - L1→L2 message relay from HubPool (see source code)