Accounting
MORE Vaults separates position logic from asset accounting. Each strategy facet owns its own storage and position-management code, while the invariant core is the single source of truth for the vault’s totalAssets
. This design keeps upgrades local to the facet that changes, yet guarantees that every share is always backed 1 : 1 by on‑chain‑verifiable value.
Available Assets
A vault can end up holding many different tokens beyond its deposit list including LP shares, yield tokens, debt receipts, even bridged assets on another chain. These tokens encompass anything that may appear in availableAssets()
and therefore in totalAssets
.
For each available asset the strategist must ensure two things:
Valuation path – An oracle, TWAP, or deterministic formula can convert the asset to the accounting unit at any time.
Registry entry – The oracle contract is published in an Oracle Registry or the pricing method is included in facet accounting so auditors and dashboards can trace the number.
If either requirement is missing, the asset will be excluded from its valuation and impact total asset accounting and share price.
Deposit Tokens
Before a vault goes live, the strategist whitelists a set of deposit tokens: the ERC-20s users are allowed to send when calling deposit()
. The list can be contain a single token (e.g. USDC) or multiple tokens (ETH, stETH, USDC). Each token must have a reliable oracle listed in an Oracle Registry so the core can convert incoming amounts into the accounting asset. If a user tries to deposit a token that is not whitelisted, the transaction reverts. Smaller sets reduce oracle risk, keep gas costs down, and simplify price charts.
Per-Facet Tracking
Accounting logic is dependent on the accounting of the protocol with which the facet interacts. In this way, accounting is inherited from each protocol and compiled independently from other facets so that it can be subsequently composed into the vault's NAV.
Facets expose a unique accounting selector that returns their current valuation in the vault’s accounting asset. There are no cross‑facet calls.
Some facets contain a hook that executes before accounting in order to include tokens or yield that is not exposed through the underlying protocol's native accounting functions. Tokens that are not included in the native NAV calculation do not reflect in the share price provided by front-ends unless a deposit or withdrawal action is initiated.
Valuation must be deterministic at deposit and withdrawal time. If pricing depends on a DEX TWAP or Chainlink round, the facet fetches and converts it internally.
NAV Aggregation
totalAssets = Σ facetAssets() + spotBalance
Every action on a vault updates the NAV calculation, with updates occurring at most once per block.
If a facet reverts or oracle is stale, operations will be reverted and vault governance can then inspect, replace, or pause the offending module without halting withdrawals.
Valuation Sources
Spot balance
Simple ERC‑20 or native token positions
Priced via Pyth, Chainlink, Redstone, etc. oracle; revert if price feed is older than MAX_DELAY
.
DEX LP positions
Uniswap V3, Balancer, Curve
Use an in‑facet math lib to value assets at pool’s virtual price; sanity‑check against on‑chain oracles.
Lending protocol deposits
Aave v3, Compound v3
Read getUserAccountData
.
Yield‑bearing tokens
LRT, wstETH, PT-sUSDe, etc.
Pull exchangeRate()
directly on-chain from protocol and multiply by holding balance.
Facets must convert their asset values into the vault’s single accounting asset using at most one intermediate oracle call to prevent gas‑bomb loops.
Fee Accrual
Management and performance fees are applied inside deposit()
and redeem()
in the VaultFacet before the NAV snapshot is finalised. When the fee is skimmed, new shares are minted to the fee recipient.
This keeps fee accounting transparent. Fees show up as an explicit delta rather than hidden dilution.
Last updated