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 PoolConfigLib.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 of at least 50 (MIN_KAPPA_COV_BPS), and the base’s is never below a spoke’s (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_qe. 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 trigger (Flow Guards)
QUOTE_UNIT_UOAits mark attests <TOKEN>-USD, the denominator every other leg’s mark divides by
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. PoolConfigLib.setBaseToken(newBase, spokes), reachable only through Admin.executeMigrateBaseToken, reverts unless every one of these holds:

  • the incoming base is already listed;
  • it is EXTERNAL-quoted;
  • it is QUOTE_UNIT_UOA;
  • its kappaCovBps meets the floor above;
  • it is trading within its depeg band of parity;
  • the demoted base gains a ref band in the same call (the promoted base gives its band up);
  • the promoted base’s kappaCovBps dominates every remaining leg, the old base included;
  • every spoke in the spokes argument still anchors to the old base (InvalidAnchor otherwise).

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.

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 (halt; swap, liability swap, flash and coop enables; deposit and swap allowlist gates)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 bandMark Relayer, 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 (PricingLib.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 reference listing, 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: deployed instances.


  • 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