CCIP v2.0.0 TokenPool API Reference
Summary
TokenPool is the abstract base contract for all CCIP EVM token pool implementations.
It centralizes:
- Router-based ramp gating
- RMN curse enforcement
- Remote chain and remote pool validation
- Rate limiting
- Optional advanced hooks
- Optional pool-level fee overrides
- Decimal normalization logic
Concrete pool variants implement token custody semantics.
Contract
chains/evm/contracts/pools/TokenPool.sol
Import
import {TokenPool} from "chainlink-ccip/chains/evm/contracts/pools/TokenPool.sol";
Inheritance
abstract contract TokenPool
is IPoolV1V2,
Ownable2StepMsgSender,
IERC165
Implements:
IPoolV1IPoolV2
Purpose in system
TokenPool defines the canonical CCIP token flow:
- Source chain →
lockOrBurn - Destination chain →
releaseOrMint
It guarantees:
- Only authorized OnRamp may initiate outbound transfers.
- Only authorized OffRamp may finalize inbound transfers.
- RMN curse state halts operations.
- Per-chain rate limits are enforced.
- Remote pool addresses are allowlisted.
- Optional hooks may extend validation.
Concrete pools implement:
_lockOrBurn_releaseOrMint
State
Immutables
IERC20 internal immutable i_token;
uint8 internal immutable i_tokenDecimals;
IRMNRemote internal immutable i_rmnProxy;
Storage (selected)
IRouter internal s_router;
mapping(uint64 => ChainConfig) internal s_chainConfigs;
IAdvancedPoolHooks internal s_advancedPoolHooks;
address internal s_rateLimitAdmin;
address internal s_feeAdmin;
Additional mappings store:
- Remote pools per chain
- Rate limiter buckets
- Token transfer fee overrides
Extension points
Concrete pools must override:
function _lockOrBurn(
uint64 remoteChainSelector,
uint256 amount
) internal virtual;
function _releaseOrMint(
address receiver,
uint256 amount,
uint64 remoteChainSelector
) internal virtual;
These define custody logic:
- Burn/mint
- Lock/release
- Bridge delegation (Lombard, CCTP, etc.)
Key internal flow / invariants
Outbound: lockOrBurn
_validateLockOrBurn(...)- Deduct pool-level fee (if configured)
- Enforce rate limits
- Call
_lockOrBurn - Emit
LockedOrBurned
Inbound: releaseOrMint
_validateReleaseOrMint(...)- Convert remote amount to local decimals
- Enforce rate limits
- Call
_releaseOrMint - Emit
ReleasedOrMinted
Validation guarantees
Outbound:
- Caller must equal
router.getOnRamp(remoteChainSelector) - Chain must be supported
- RMN not cursed
- Token supported
- Hooks (if configured) must pass
Inbound:
- Caller must equal
router.isOffRamp(...) - Source pool must be allowlisted
- RMN not cursed
- Chain supported
- Hooks (if configured) must pass
Decimal normalization
Functions:
function _encodeLocalDecimals() internal view returns (bytes memory);
function _parseRemoteDecimals(bytes memory sourcePoolData)
internal
view
returns (uint8);
function _calculateLocalAmount(
uint256 remoteAmount,
uint8 remoteDecimals
)
internal
view
returns (uint256);
- Rounds down when remoteDecimals > localDecimals.
- Ensures consistent cross-chain denomination.
Rate limiting
Separate buckets for:
- Default finality
- Custom block confirmations
Uses RateLimiter library.
Fee overrides
Optional pool-level fee configuration:
applyTokenTransferFeeConfigUpdatesgetFee
Fee capped by:
uint16 internal constant BPS_DIVIDER = 10_000;
Events (selected)
event LockedOrBurned(
uint64 indexed remoteChainSelector,
address indexed sender,
uint256 amount
);
event ReleasedOrMinted(
uint64 indexed remoteChainSelector,
address indexed receiver,
uint256 amount
);
event ChainAdded(uint64 remoteChainSelector);
event ChainRemoved(uint64 remoteChainSelector);
event RemotePoolAdded(uint64 remoteChainSelector, bytes remotePoolAddress);
event RemotePoolRemoved(uint64 remoteChainSelector, bytes remotePoolAddress);
Additional events exist for:
- Rate limit consumption
- Fee configuration updates
Security model
- Router is the only ingress/egress authority.
- RMN curse halts flows.
- Remote pools strictly allowlisted.
- Rate limiting prevents draining.
- Fee logic isolated from custody logic.
- Hooks cannot bypass base validation.