For the complete documentation index, see llms.txt. This page is also available as Markdown.

Uniswap V2

The Uniswap V2 adapter reports an account's V2 LP across any pair as the dilution-adjusted pro-rata share of both reserves, emitted as one Supplied leg per token.

The Uniswap V2 adapter reports an account's V2 LP across any pair as the dilution-adjusted pro-rata share of both reserves, emitted as one Supplied leg per token. A single immutable meta-adapter serves every pair; the active pair set lives on NAVCalculator.

  • Type: Meta-adapter

  • protocolSubId: keccak256("uniswap-v2")

  • Instance key: the Uniswap V2 pair address, widened to bytes32.

  • Source: UniswapV2PoolsMetaBalanceAdapter.sol


Positions returned

Per pair instance, up to two legs:

Leg
PositionKind
isDebt
Description

token0 share

Supplied

false

Account's pro-rata share of reserve0

token1 share

Supplied

false

Account's pro-rata share of reserve1

Legs with zero amount, unregistered assets, or assets excluded by assetFilter are dropped.


Balance calculation

Flowchart of the Uniswap V2 balance adapter: read calls derive position legs into PositionBalance entries.
Uniswap V2 adapter — how the underlying balances reported to the NAV Calculator are derived.

Delegated to UniswapV2PairLib.pairAmounts(pair, account). The account's LP balance is converted to underlying reserves against an effective supply that includes the protocol fee LP the pair would mint on its next mint/burn — so the reported amounts match what an LP would actually receive on burn:

The dilution term mirrors V2's internal _mintFee, the LP the pair would mint to feeTo on the next burn:

The three coefficients (numFactor, rootKCoeff, rootKLastCoeff) come from a virtual _mintFeeConstants() hook, so byte-compatible forks can override them. Uniswap V2 (and SushiSwap, which shares them) use (1, 5, 1) — i.e. (√k − √kLast) / (5·√k + √kLast), capturing 1/6 of fee growth.

It is 0 when feeTo is unset, kLast == 0, or √k <= √kLast. feeTo() and kLast() are read defensively, so forks omitting them are treated as having no protocol fee.

An absent feeTo() and a malformed one are treated differently, because this is the one defensive read whose failure direction is upward: dropping the dilution term shrinks effectiveSupply and so raises the reported share. A read that fails means a fork without protocol fees, and reporting undiluted is correct — that is why the defensive read exists. A read that succeeds but returns a word that is not a valid address means a malformed factory: the dilution is unknowable, so the pair is dropped rather than valued on a guess. Treating the second case as "no protocol fee" over-reports the position.


Identity

  • positionId: abi.encode(address pair) — one positionId per pair instance.

  • positionKind: Supplied for both legs.

There is no labels field on the position. The adapter implements positionLabels(positionId), which returns ["AMM Liquidity Pool", "sym0/sym1"] from the pair's on-chain token symbols — surfaced only by the verbose reads (getAccountPositionsVerbose / getAccountNavVerbose); the full breadcrumb is [protocolName, ...labels].


Constructor

Parameter
Description

navCalculator_

NAVCalculator contract for asset-registry access

No protocol addresses are pinned — every pair is self-describing (token0, token1, factory, reserves are read from the pair itself).


Registration

Register the adapter once with addMetaBalanceAdapter, then enable each pair with addMetaInstances. The instance coordinate is the Uniswap V2 pair address widened to bytes32. The adapter holds no instance state — it reads its active set from NAV_CALCULATOR.getMetaInstances(address(this)).

Last updated