Accounting & yield math
Level: Advanced · Models below are for understanding. The exact contract math is private; use on-chainpreview*/convert*for real values.
Core identities
sharePrice = totalAssets / totalSupply
sharesOut = assets × (totalSupply + 1) / (totalAssets + 1) // deposit, floor
assetsOut = shares × (totalAssets + 1) / (totalSupply + 1) // redeem, floorRounding floors in the vault's favour, so a deposit→redeem round trip can never return more than you put in (verified by a unit test in examples/python).
Accrual algorithm (documented order)
On every deposit / mint / withdraw / redeem / allocate:
Why a cached snapshot? Donation / inflation attacks
Classic ERC-4626 attack: attacker deposits a tiny amount, then donates tokens directly to the vault to inflate totalAssets, making the next depositor's shares round to zero. Concrete's defences (as documented): (1) conversions inside guarded operations use the cached total, refreshed in a controlled way; (2) the +1 virtual terms in the share formula; (3) accrual before conversion.
Fees as dilution
To pay a fee worth F assets, the vault mints s new shares to the recipient so that their claim equals F:
s / (supply + s) × totalAssets = F ⇒ s = F × supply / (totalAssets − F)Every other holder is diluted proportionally. See examples/python/fee_dilution_model.py.
Hurdle rates
perfFee = perfRate × max(0, yield − hurdle), where the hurdle is a fixed APY (compounds), fixed APR (simple), or a dynamic rate tracking an external benchmark. Yield up to the hurdle goes entirely to depositors.
APR ↔ APY
APY = (1 + APR/n)^n − 1 (n = compounding periods per year)8% APR, daily compounding → 8.328% APY.
Losses & marks
With async accounting, an operator can reduce reported total assets (adjustTotalAssets-style flow) after a loss or an unwind cost; the next accrual reconciles the strategy's tracked value. All holders take the loss pro rata via the exchange rate.
Async accounting timeline
t0 operator pushes signed valuation V0 (valid for accountingValidityPeriod)
t1 users deposit/withdraw → vault reads V0 → OK
t2 validity window expires with no new push → strategy value reverts → vault halts
t3 admin unpauseAndAdjustTotalAssets OR strategy toggled inactive → resumesReconciling numbers yourself
| Question | How |
|---|---|
| Current share price | convertToAssets(10**decimals) on the vault, or subgraph Vault.sharePrice |
| Historical price | subgraph vaultStats(interval: day) |
| Fees paid to date | subgraph vaultFeesStats cumulative fields |
| Strategy allocation | subgraph Strategy.allocatedValue or on-chain totalAllocatedValue() |