Pool legs

A token is a leg of a pool when it has an assets[token] slot. Every core designates one of its legs as the base asset: its unit of account, and the root its anchor tree resolves to. Which assets sit on which core, and which one is the base, is per instance: see Pool Composition. Routing across pools is in Swapping; per-asset addresses, feeds and ERC-20 quirks are in the Asset Registry.


1. What a listing is

On chain, a listing is one assets[token] slot written by PoolConfig.initAsset:

  • Decimals are read from the token, never passed in. initAsset calls the ERC-20 and stores what it returns. A token reporting 0 or more than 18 decimals cannot be listed.
  • decimals != 0 is the is-configured sentinel. Every reader gates on it, which is why 0 is unlistable: it collides with “not configured”.

Listing is admin-only, and one-way-latched. Before sealBootstrap(pool) the deployer lists through Admin.addAsset. After it, addAsset reverts permanently and the only path is the timelocked ADD_ASSET queue: Admin.requestOp(..., ADD_ASSET, ...) then Admin.executeAddAsset(pool, token). See Admin for the queue and Access Control for who may drive it.

Every listed asset, the base included, must carry kappaCovBps > 0 (Invariants §I-9).

2. Why every core has a base leg

Each pool stores one baseToken, fixed at initialize. A new listing defaults its anchor to the base (initAsset sets anchor = (token == baseToken) ? 0 : baseToken) and adminSetAnchor can afterwards re-parent it to any correlated listed asset, up to MAX_DEPTH = 4 edges from the root. A core whose whole roster still carries that default is a depth-1 hub-and-spoke star, and any cross there is at most spoke → base → spoke in a single Pool.swap. Deeper trees are configuration, not a different mechanism: a swap between two assets sharing a parent prices through that parent without touching the base at all (Anchor Path Pricing).

The base is the numeraire, so it carries constraints no spoke does:

Requirement on the baseWhy
EXTERNAL-quotedits own depeg halt must be able to bite (Flow Guards)
QUOTE_UNIT_ANCHORit is the unit other legs are attested in
spokes carry an independent ref banda spoke’s mark is checked against a second attestation, not against the base alone

Changing the base is not a pointer swap. PoolConfig.setBaseToken(newBase, spokes), reachable only through Admin.executeBaseMigration, reverts unless every one of these holds:

  • the incoming base is already listed;
  • it is EXTERNAL-quoted;
  • it is QUOTE_UNIT_UOA;
  • it is trading within BASE_DEPEG_HALT_BPS of parity;
  • the demoted base gains a ref band in the same call.

Off chain the same leg is the router’s hub: a pair with no shared pool is fillable only as one hop through the hub symbol (Swapping §2). pool.baseToken() is the authority for which asset that is; no deployment artifact records it.

A cross-core route takes the hub out of the source pool and into the destination, so the hub has to be replenishable. On a testnet deployment that rules out an official issuer token nobody can mint, and the base is the mintable twin instead (Faucets §2).

3. What an LP holds against a leg

One ERC-20 receipt per leg, single-sided, with the leg’s own liquidity index: mint, cooldown and exit are in Providing Liquidity. Moving a claim from one leg to another without withdrawing is Liability Swaps. A leg’s coverage ratio prices both sides of a swap and caps a same-asset exit; it is derived in Inventory Management §5.

4. Reading a leg’s state

Everything a user sees as “this leg is not trading right now” is one of four on-chain reads, each documented where the mechanism lives:

What you seeReadWhere it is specified
leg disabled for an actionthe leg’s flag bits (swap, deposit, withdraw, liability swap)Basic Operations §2
leg or pool haltedthe halt bits an admin or guardian setsAccess Control, Guardian
quotes rejected as stale or off-bandfeed age, deviation, ref band, depeg bandOracle Keeper, Flow Guards
a size that quoted refuses to fillper-block and per-window flow capsFlow Guards

5. Decimals, and gas

The pool rescales per hop with a pure power-of-ten shift between the two legs’ stored decimals (Pricing.sol), so decimals never touch pricing and show up only in raw amounts. Read decimals() on the deployed token before scaling one: the decimals column of the Asset Registry describes an asset’s mainnet reference, which is not always the token in your wallet on a testnet instance.

Where a chain pays gas in a token that is also a listed leg, a “max” that spends the whole balance cannot afford its own transaction fee, so the app reserves a gas buffer on that input. Which chains those are, and what the gas token’s ERC-20 view looks like: Deployments §1.


  • Swapping: how a swap is routed, within a pool and across pools
  • Faucets: mintable twins and how testnet balances are obtained
  • Asset Registry: per-asset addresses, feeds, ERC-20 oddities
  • Pool Composition: which assets sit on which core
  • Anchor Path Pricing: the anchor column and leg pricing
  • Admin: the timelocked listing and base-migration queues
  • Spread & Fees: what a listed asset’s fee floor and coverage wall cost