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

NAV Calculator

The core accounting contract — registers assets, feeds, and adapters, and returns NAV and positions.

The NAV Calculator (NAVCalculator) is the core accounting contract. It registers the assets a fund can hold, the price feeds that value them, and the balance adapters that locate them, then returns the fund's NAV and full position breakdown for any account on that chain.

Source: NAVCalculator.sol

At a glance

Contract

NAVCalculator

Type

UUPS upgradeable proxy — one instance per chain

Roles

DEFAULT_ADMIN_ROLE · MANAGER (governance Safe)

Chains

Ethereum · Optimism · Arbitrum · Base · Gnosis (+ 19 canonical-only)

Addresses

Deployment addresses


It is fund-agnostic — a single instance per chain prices any account. It is deployed as a UUPS proxy at a canonical CREATE2 address, identical across all chains, and is upgradeable by the governance Safe.

Deployed addresses are listed on the Deployment addresses page. Always call the proxy; the implementation may change on upgrade.

How it works

getAccountNav(account, quoteAsset)
  ├─ for each registered balance adapter:
  │    └─ adapter.getAdapterPositions(account, …) → PositionBalance[]
  ├─ for each position:
  │    ├─ (price, decimals, stale) = primary feed for balanceAsset
  │    ├─ irregular = primary vs monitor feeds beyond tolerance (signal only)
  │    └─ value = ±(amount × price) / 10^(assetDecimals)          // − when isDebt
  └─ return NAV {
         value,               // total in quote asset units (USD = 8 decimals)
         quoteAsset,
         timestamp,
         stalePriceAssets,    // assets whose primary feed was stale
         irregularPriceAssets,// assets whose primary diverged from monitors / peg
         sequencerDown,       // L2 sequencer status
         quoteAssetStale,     // quote-asset feed stale → value falls back to USD
         monitorsUnhealthyPriceAssets  // monitors configured, none readable → no divergence check ran
     }

Solidity interface

Structs

The interface defines the position model, the NAV snapshot, and the price-feed snapshot. Expand each group for the full definitions.

Position model — Asset, PositionBalance, Position, VerbosePosition, VerboseNAV
Partial NAV snapshot — PartialNAV

Returned by getAccountNavForAdapters, for paging a NAV read that no longer fits in a single eth_call. It mirrors NAV's first eight fields in order, then appends the adapters the slice actually covered:

The positional mirror stops at those eight fields. monitorsUnhealthyPriceAssets sits after adaptersCovered here, while in NAV it is the last field — so the two structs no longer line up past the prefix. That is deliberate: keeping adaptersCovered at the offset a live off-chain reassembler already decodes matters more than the documentation convenience of a positional mirror. Decode each struct against its own ABI; never assume the mirror extends to new fields.

It is a distinct type from NAV, deliberately: a partial result must not be assignable or decodable where a complete NAV is expected. Both reads run the same underlying computeNav, with the validated slice in place of the full adapter set, so a complete read and the sum of its slices agree because they are literally the same code path.

A meta-adapter is the atomic unit. Naming one in a slice includes all of its governed instances; there is no paging within a single adapter, since the instance set is itself swap-and-pop mutable and an instance range would carry the identical hazard. If one meta-adapter alone exceeds the budget, pagination cannot help.

Price-feed snapshot — PriceFeedData

For a Custom feed — a composite of a rate and a base/USD leg — (updatedAt, chainlinkHeartbeat) report the governing leg: the underlying Chainlink leg with the greatest age / heartbeat ratio, i.e. the one closest to its own staleness deadline. The pair is therefore a real, matched pair from one actual oracle rather than a mix of two, and it reduces to the single leg's values for a single-leg feed. Which leg governs can change between reads as the ratios cycle, so a two-leg feed's reported heartbeat may alternate between its legs' values. See Price feeds.

Read functions

Function
Purpose

getAccountNav

Total chain-local NAV for an account in a quote currency (address(0) = USD)

getAccountNavForAdapters

NAV over a caller-supplied subset of adapters, for paging a read too large for one call (returns PartialNAV)

getAccountPositions

One priced Position per balance entry reported for an account

getAccountPositionsForAsset

Priced positions for a single asset across all adapters that report it

getAccountNavVerbose

getAccountNav plus per-position protocolName + labels[] (returns VerboseNAV)

getAccountPositionsVerbose

getAccountPositions enriched with protocolName + labels[] (returns VerbosePosition[])

getAccountPositionsForAssetVerbose

Single-asset verbose positions (returns VerbosePosition[])

healthCheck

Sequencer status + currently-stale and currently-irregular assets, without a full NAV

getPriceData

Detailed data for an asset's primary feed + divergence signal (price, stale, irregular …)

getPriceDataNoDivergence

Same as getPriceData but skips the monitor read (used on the base/USD hot path)

getPriceDivergence

Live primary-vs-monitor divergence read (median, worst-case bps, irregular, direction)

hasPositions

Whether an account has any non-zero position

getAssetsWithPositions

Assets for which an account holds a non-zero position

calculateValue

Value of a set of (asset, amount) pairs in a quote currency

getAssetInfo

One asset's classification and display labels, in a single call

getRegisteredAsset

Resolves one asset to its (address, symbol, decimals) in O(1), plus a found flag

getRegisteredAssets · isAssetRegistered · getAssetCount

Asset-registry introspection

usdDecimals · version

USD value decimals (e.g. 8); implementation version

Full read-function signatures

Examples

Illustrative calls and returns. Values are example data (not live); addresses are abbreviated. The account is a portfolio Safe, USDC = 0xA0b8…eB48 (6 dp), WETH = 0xC02a…6Cc2 (18 dp), and the USD quote asset is address(0).

Stale prices and sequencer status

The NAV struct carries hard reliability signals — stalePriceAssets, sequencerDown, and quoteAssetStale — that must block a pricing read, plus two soft signals: irregularPriceAssets (and quoteAssetIrregular), which flags a price the primary and monitor feeds disagree on, and monitorsUnhealthyPriceAssets, which flags assets where no monitor answered at all, so the disagreement check never ran. See Stale prices & sequencer for the full model, and use healthCheck() to poll chain health without computing a full NAV — noting that healthCheck does not carry the third array.


Admin / Manager API

Configuration is privileged and held by the governance Safe. Most mutations require the MANAGER role; the sequencer-feed setters and proxy upgrades require DEFAULT_ADMIN_ROLE. Both roles are held by the governance Safe. All batch calls are all-or-nothing — if any item fails its check, the whole transaction reverts.

Assets & price feeds (MANAGER)

An asset enters the system with its primary feed via registerAsset, carries one pricing feed plus optional monitor feeds and tolerances, and leaves via unregisterAsset. See Price feeds for the primary-feed + divergence-monitor model.

Function
What it does · key reverts

registerAsset(asset, priceFeed, priceType, heartbeat)

Registers a new asset and its primary price feed in one call; reads symbol/decimals from the ERC-20 (or the native-token constant) and adds the asset to the default (wallet) adapter's inclusion list. Reverts InvalidArguments for an address with no code (the native-token sentinel aside), so a typo'd or not-yet-deployed token cannot be registered.

addPriceFeed(asset, priceFeed, priceType, heartbeat)

Adds another pricing feed to an already-registered asset (transitional; production keeps one). Reverts PriceFeedAlreadyRegistered (same feed twice) or PriceFeedDecimalsMismatch (a new feed's decimals must equal the existing feeds'); heartbeat must be non-zero.

removePriceFeed(asset, priceFeed)

Removes one pricing feed by address. Reverts PriceFeedNotFound, or CannotRemoveLastFeed if it is the only pricing feed left.

removePriceFeedAt(asset, index)

Same, by array index. Reverts PriceFeedIndexOutOfBounds or CannotRemoveLastFeed.

setDivergenceTolerance(asset, bps)

Sets the primary-vs-monitor divergence tolerance. Must be in (0, 10000]; 0 disables (InvalidTolerance). Required before any monitor feed can be added.

setPegTolerance(asset, bps)

Sets the $1-peg tolerance (stablecoins). Must be in [0, 10000]; 0 disables the peg check (InvalidTolerance). Also the switch that decides the asset's peg classification — crossing zero flips it.

setAssetKindRegistry(registry)

Points this NAV at the chain's AssetKindRegistry. Rejects address(0) (InvalidArguments). Re-settable: the registry is a plain contract, so replacing it is a redeploy plus this call.

addMonitorFeed(asset, priceFeed, priceType, heartbeat)

Adds a divergence-only monitor feed. Reverts DivergenceToleranceRequired (no tolerance set), MonitorFeedDecimalsMismatch (monitor must report 8 decimals), or MonitorFeedAlreadyRegistered.

removeMonitorFeed(asset, priceFeed) · removeMonitorFeedAt(asset, index)

Removes a monitor feed by address or index (MonitorFeedNotFound). Monitors are optional — the last one may be removed.

unregisterAsset(asset)

Fully removes the asset: clears all its pricing and monitor feeds, removes it from the default-adapter list, and drops it from the asset registry. Use only after the asset is no longer held anywhere.

Balance adapters (MANAGER)

Plain (single-scope) adapters only — meta-adapters use the next group. See Balance adapters.

Function
What it does · key reverts

addBalanceAdapters(address[] adapters)

Batch-registers plain adapters into the global registry. Reverts InvalidArguments (empty list / zero address), DuplicateBalanceAdapter, or MetaAdapterNotAllowed if any adapter implements IMetaBalanceAdapter (those must use addMetaBalanceAdapter).

removeBalanceAdapters(address[] adapters)

Batch-removes adapters from the registry. Reverts if any is not registered or is the built-in ERC20Default adapter (which cannot be removed).

Meta-adapters & their instances (MANAGER)

A meta-adapter is registered with its initial instance set; the set is then grown or shrunk incrementally. See Meta balance adapters.

Function
What it does · key reverts

addMetaBalanceAdapter(adapter, bytes32[] instances)

Registers a meta-adapter and seeds its instance set atomically. The adapter must implement IMetaBalanceAdapter (else NotMetaAdapter / InvalidBalanceAdapter). Reverts DuplicateBalanceAdapter or DuplicateMetaInstance. An empty instance list is allowed (seed later).

addMetaInstances(adapter, bytes32[] instances)

Adds instance coordinates to a registered meta-adapter. Reverts DuplicateMetaInstance (already present or repeated in the batch — a duplicate would double-count NAV), InvalidArguments (empty), or NotMetaAdapter.

removeMetaInstances(adapter, bytes32[] instances)

Removes coordinates. Reverts MetaInstanceNotFound, InvalidArguments (empty), or NotMetaAdapter. Order is not preserved.

Adding a market/vault/pool to coverage is thus an addMetaInstances transaction — never a new contract deploy.

Default (wallet) adapter inclusion (MANAGER)

Function
What it does

excludeAssetFromDefaultAdapter(asset)

Stops the built-in ERC20Default adapter from reporting wallet balances for an asset, without unregistering the asset (its protocol positions still count).

includeAssetInDefaultAdapter(asset)

Re-enables wallet-balance reporting for the asset.

L2 sequencer & upgrades (DEFAULT_ADMIN_ROLE)

Function
What it does · key reverts

setChainlinkL2SequencerUptimeFeed(feed)

Sets the Chainlink L2 sequencer uptime feed (address(0) on L1). Admin-gated because disabling sequencer detection on an L2 has upgrade-level impact.

setChainlinkL2SequencerGracePeriod(seconds)

Sets the post-recovery grace period. Reverts UptimeFeedNotSet if no uptime feed is configured.

UUPS upgrade

upgradeToAndCall(...) (UUPSUpgradeable) is authorized by DEFAULT_ADMIN_ROLE; the implementation can change, the proxy address never does.

Admin / Manager function signatures

Plain adapters are registered with addBalanceAdapters; meta-adapters (one adapter covering many protocol instances) are registered with addMetaBalanceAdapter and have their queried instance set adjusted with addMetaInstances / removeMetaInstances. See Balance adapters.


Multichain aggregation

The offchain NAV Worker collects per-chain NAV values and sums them:

  1. Call getAccountNav on every chain the fund operates on.

  2. Verify stalePriceAssets is empty, sequencerDown is false, and quoteAssetStale is false on every chain.

  3. If any chain is unhealthy, the global NAV is withheld until the condition clears.

  4. Otherwise, sum all per-chain value fields to produce the global fund NAV.

  5. Treat a non-empty irregularPriceAssets as an alert, not a hard block: the value is still usable, but the divergence should be investigated before it widens into a stale/blocking condition.

  6. Treat a non-empty monitorsUnhealthyPriceAssets as an alert on the check itself rather than on the price: those assets lost their cross-check this read, so an empty irregularPriceAssets says nothing about them. Reconcile the two lists together — an asset in the third array is unverified, not verified-clean.

See Code examples for TypeScript and Python examples of the single-chain and multichain read paths.

Last updated