Overview
The HubPool is the central contract deployed on Ethereum L1 that houses token liquidity for all SpokePools across supported chains. It acts as the cross-chain administrator and owner of all L2 SpokePools, coordinating governance actions and pool rebalances. Contract Location:contracts/HubPool.sol:41
Core Responsibilities
- Liquidity Management: Stores L1 token liquidity provided by LPs who earn fees for funding cross-chain relays
- Root Bundle Validation: Accepts and validates merkle root bundles proposed by data workers using UMA’s Optimistic Oracle
- Pool Rebalancing: Sends tokens to SpokePools via canonical bridges to refund relayers and fulfill slow relays
- Cross-Chain Administration: Relays admin function calls to SpokePools on all supported chains
- Protocol Fee Collection: Captures a percentage of LP fees for protocol development
Architecture
Inheritance
Key Dependencies
- UMA Optimistic Oracle: For dispute resolution on root bundles
- Finder: UMA’s service registry for locating oracle and whitelist contracts
- LP Token Factory: Deploys ERC20 LP tokens for each enabled L1 token
- Chain Adapters: Stateless contracts for bridging tokens/messages to each L2
Storage Variables
Currently active root bundle proposal. Only one can exist at a time. Contains pool rebalance, relayer refund, and slow relay merkle roots along with proposal metadata.
Maps L1 token addresses to their pool state including LP token address, enabled status, reserves, and undistributed fees.PooledToken struct fields:
lpToken: ERC20 LP token given to liquidity providersisEnabled: Whether accepting new depositslastLpFeeUpdate: Timestamp of last fee distribution updateutilizedReserves: Funds sent to SpokePools awaiting return (can be negative)liquidReserves: Available funds in contractundistributedLpFees: Fees accumulated but not yet distributed to LPs
Maps chain ID to adapter and SpokePool addresses for cross-chain communication.CrossChainContract struct:
adapter: Address of chain adapter for bridging (called via delegatecall)spokePool: Address of SpokePool on destination chain
Token used for bonding when proposing root bundles. Slashed if bundle is disputed and found invalid.
Amount of bond token required to propose a root bundle. Set to bond token final fee + configurable bond.
Challenge period duration in seconds. Defaults to 7200 (2 hours). Bundles can be disputed only during this window.
Interest rate for distributing LP fees. Set to 0.0000015e18 by default, paying out full fees in ~7.72 days.
Percentage of LP fees captured by protocol (0-1e18 scale).
Key Functions
proposeRootBundle
Publishes a new root bundle containing merkle roots for pool rebalances, relayer refunds, and slow relays. Proposer stakes a bond that can be slashed if invalid.Latest block number for each chain at bundle evaluation time. Used by off-chain validators to verify bundle correctness.
Number of leaves in the pool rebalance merkle tree. Must be > 0. Max is number of whitelisted chains.
Merkle root of pool rebalance leaves that instruct token transfers from HubPool to SpokePools.
Merkle root published to SpokePools for relayers to claim refunds via inclusion proofs.
Merkle root published to SpokePools for executing slow relay fulfillments.
- Caller must approve this contract to spend
bondAmountofbondToken - No active bundle proposal exists (all previous leaves executed)
- Contract is not paused
poolRebalanceLeafCount > 0
ProposeRootBundle(challengePeriodEndTimestamp, poolRebalanceLeafCount, bundleEvaluationBlockNumbers, poolRebalanceRoot, relayerRefundRoot, slowRelayRoot, proposer)
executeRootBundle
Executes a pool rebalance leaf after the challenge period. Sends tokens to a SpokePool and relays refund/slow relay roots if this is the first leaf for that chain.Destination chain ID where tokens will be sent.
Organizational index. If 0, this leaf will also relay merkle roots to the SpokePool. Only one leaf per chain should have groupIndex == 0.
Total LP fees per token in this bundle for all relays. Parallel array to l1Tokens.
Amount to send to (+) or receive from (-) the SpokePool for each token. Parallel array to l1Tokens.
Running balance tracking between HubPool and SpokePool. Positive means HubPool owes SpokePool. Parallel array to l1Tokens.
Unique identifier for this leaf in the merkle tree (0-255).
Array of L1 token addresses corresponding to the fee/amount arrays.
Merkle inclusion proof for this leaf.
- Current time > challenge period end timestamp
- Leaf not already claimed
- Valid merkle proof
- Pool rebalance routes configured for all (l1Token, chainId) pairs
- Cross-chain contracts (adapter, spokePool) set for chainId
- Sends tokens to SpokePool via chain adapter (if netSendAmount > 0)
- Updates utilized/liquid reserves for L1 tokens
- Allocates LP and protocol fees
- Relays relayer refund and slow relay roots to SpokePool (if groupIndex == 0)
- Returns bond to proposer after last leaf executed
RootBundleExecuted(groupIndex, leafId, chainId, l1Tokens, bundleLpFees, netSendAmounts, runningBalances, caller)
disputeRootBundle
Disputes the current root bundle proposal by staking a bond and sending the dispute to UMA’s Optimistic Oracle. Can only be called during the challenge period.- Current time ≤ challenge period end timestamp
- Caller must approve this contract to spend
bondAmountofbondToken - Bond amount > final fee (otherwise bundle is canceled instead)
- Requests price from Optimistic Oracle with proposer’s bond
- Immediately disputes the price request with disputer’s bond
- Deletes root bundle proposal, allowing new proposal
- If dispute succeeds, disputer receives both bonds; if fails, proposer receives both
RootBundleDisputed(disputer, requestTime)
relaySpokePoolAdminFunction
Sends an admin function call to a SpokePool on another chain. Only callable by owner. Used for all governance actions on SpokePools.Chain ID of the target SpokePool.
ABI-encoded function call to send to SpokePool. Can be any arbitrary data.
- Only owner can call
- Cross-chain contracts configured for chainId
- Delegates to chain adapter to relay message cross-chain
- SpokePool receives message and verifies it came from this HubPool
SpokePoolAdminFunctionTriggered(chainId, message)
Example Usage:
Liquidity Provider Functions
addLiquidity
Deposit L1 tokens to earn LP fees. Receives LP tokens representing pool share.- Token enabled for liquidity provision
- If
l1Tokenis WETH,msg.valuemust equall1TokenAmount - Otherwise
msg.valuemust be 0 - Caller must approve token transfer
LiquidityAdded(l1Token, l1TokenAmount, lpTokensMinted, liquidityProvider)
removeLiquidity
Burn LP tokens to redeem underlying L1 tokens plus accrued fees.If true and l1Token is WETH, unwrap and send ETH. Recipient must be able to receive ETH.
LiquidityRemoved(l1Token, l1TokensToReturn, lpTokenAmount, liquidityProvider)
Events
Emitted when a new root bundle is proposed.
Emitted when a pool rebalance leaf is executed.
Emitted when a root bundle is disputed.
Emitted when admin function is relayed to SpokePool.
Emitted when LP adds liquidity.
Emitted when LP removes liquidity.
Admin Functions
setPaused
setPaused
Pause or unpause bundle proposal and execution. Used during upgrades or emergencies.
emergencyDeleteProposal
emergencyDeleteProposal
Delete active proposal in emergency situations (e.g., unexecutable bundle that passed liveness due to bug).Returns bond to proposer if bundle had unclaimed leaves.
setProtocolFeeCapture
setProtocolFeeCapture
Set protocol fee capture address and percentage.Fee percentage must be ≤ 1e18.
setBond
setBond
Set bond token and amount for root bundle proposals.Requires token on UMA whitelist. Cannot change while active proposal exists.
setLiveness
setLiveness
Set challenge period duration for root bundles.Must be > 10 minutes.
setCrossChainContracts
setCrossChainContracts
Configure adapter and SpokePool addresses for a chain.
setPoolRebalanceRoute
setPoolRebalanceRoute
Map L1 token to its counterpart on a destination chain.Setting destinationToken to 0x0 disables the route.
setDepositRoute
setDepositRoute
Enable/disable deposit route from origin to destination chain.Relays message to origin SpokePool to update its routing config.
enableL1TokenForLiquidityProvision
enableL1TokenForLiquidityProvision
Enable L1 token for LP deposits. Deploys new LP token if first time.
disableL1TokenForLiquidityProvision
disableL1TokenForLiquidityProvision
Disable L1 token for new LP deposits.
Security Considerations
Cross-Chain Ownership: HubPool owns all SpokePools. Admin verification on SpokePools uses chain-specific logic (address aliasing on Arbitrum, CrossDomainMessenger on Optimism, etc.).
- Uses UMA Optimistic Oracle V2 for dispute resolution
- Bond slashing incentivizes honest behavior
- Emergency functions allow admin intervention
- Reentrancy protection on all state-changing functions
- Exchange rate calculation prevents fee manipulation
- LP fee distribution uses continuous interest rate model
Related Contracts
- SpokePool - L2 contracts that receive rebalances and execute refunds
- Chain Adapters - Bridge-specific adapters for each supported chain