Inventory and Yield
Learn how DualPool tracks pool inventory across vault shares, ERC-6909 claims, and raw ERC-20 balances.
Between swaps, a DualPool's capital does not sit in the Uniswap v4 PoolManager. For every (PoolId, Currency) pair, the hook maintains a pool-local ledger with three legs:
- ERC-4626 vault shares: the rest state. Idle capital is deposited into the pool's configured vault and earns lending yield continuously.
- ERC-6909 claims: tokens owed to the hook by the
PoolManager, held as ERC-6909 claim tokens. A short-lived settlement buffer (see below). - Raw ERC-20: token balances held by the hook and attributed to the pool, typically only transiently during a JIT cycle.
The hook never treats its global token balance as pool ownership. Many pools can share the same currency on one hook, so all accounting flows through per-pool mappings. Claims minted for one pool cannot be redeemed into another pool's ledger.
Why Claims Exist
After a swap, the hook may be owed tokens by the PoolManager, but it cannot always take ERC-20 immediately, because the swapper settles their side of the swap after afterSwap runs. Instead, the hook mints ERC-6909 claims to itself and records them in the pool's ledger.
At the start of the next JIT deployment, the hook burns those claims and takes the underlying ERC-20, crediting the pool's tracked balance. removeLiquidity also redeems claims before withdrawal math runs, so LPs can exit even when post-swap balances sit in claims.
Claims earn no yield
Incoming swap tokens lag one cycle as claims. Under sustained one-way flow, a meaningful fraction of pool inventory can sit in claims rather than in the vault until an opposing-direction swap or LP action sweeps it back. Interfaces displaying pool inventory should show the claims leg distinctly.
Reserves vs. Effective Liquidity
DualPool exposes two views of pool inventory, and the distinction matters:
getReserves(key)returns total economic assets:vault.convertToAssets(poolShares)+ claims + tracked ERC-20. This is what LPs own.getEffectiveLiquidity(key)returns immediately deployable assets:vault.previewRedeem(poolShares)+ claims + tracked ERC-20. This is what the hook can withdraw and put to work right now.
LP share math uses total assets, so LPs retain their full economic claim even when a vault is temporarily illiquid. JIT deployment and the quote views use effective assets, so routing only ever sees what the hook can realistically source this block.
When the configured vault is paused, capped, or utilization-constrained, getEffectiveLiquidity falls below getReserves. Swaps larger than effective liquidity fill less, or revert on the vault withdrawal.
previewRedeem is used instead of maxWithdraw because curated vaults (e.g. Morpho VaultV2) often return 0 from maxWithdraw by design while still reporting meaningful per-share exit value.
Yield Accrual
Vault yield accrues to the pool automatically: as the vault's share price rises, the same vault-share balance converts to more underlying tokens. This has two visible effects:
- LP positions appreciate without any pool activity, since share conversion is based on total assets
- The JIT curve deepens as yield accrues, so quotes for identical inputs improve over time
There is no harvest step and no separate yield token. Yield is realized whenever the hook withdraws from the vault, during a JIT cycle or an LPÂ exit.
Vault Requirements
Configured vaults must satisfy strict requirements, enforced at pool initialization:
vault.asset()must equal the pool currency- The vault must be feeless:
previewDeposit == convertToSharesandpreviewRedeem == convertToAssets. Entry or exit fees would bleed LPs on every JIT round-trip and break share math - The vault receives a standing max token allowance from the hook, so operators must choose vaults whose governance and upgrade risk they accept