Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ERC4626SharePriceAssertion

Git Source

Inherits: ERC4626BaseAssertion

Title: ERC4626SharePriceAssertion

Author: Phylax Systems

Asserts that the vault’s endpoint share price (totalAssets / totalSupply) does not decrease beyond a configurable tolerance. Invariants covered:

  • Non-dilutive entry/exit: absent explicit fee accrual or loss recognition, deposit/mint/withdraw/redeem must not reduce assets-per-share for remaining holders.
  • Rounding favors incumbents: the share price must not move against the vault (i.e., existing holders) during ordinary user operations.

Compares only the relevant pre/post endpoints. Healthy vault operations often update assets and shares at different internal call boundaries, so intermediate snapshots are not part of this property. Increases are permitted. Empty-supply endpoints have no holder share price and are outside this check.

Constants

sharePriceToleranceBps

Maximum acceptable share-price decrease in basis points.

uint256 public immutable sharePriceToleranceBps

Functions

constructor

constructor(uint256 _toleranceBps) ;

_registerSharePriceTriggers

Register the default trigger set: the exhaustive tx-wide envelope + per-call checks.

The tx-wide envelope (assertSharePriceEnvelope) uses the all-fork-points assetsMatchSharePrice scan, which re-reads totalAssets()/totalSupply() at ~1+2*callFrames fork points. This is fine for vaults with a cheap totalAssets(). Vaults with an expensive computed totalAssets() (e.g. MetaMorpho, which loops the Morpho Blue supply queue) should instead use _registerBoundedSharePriceTriggers(): the exhaustive scan can exhaust the assertion gas limit on unrelated high-call-count transactions that merely touch the vault and revert (PrecompileOOG) — a false invalidation.

function _registerSharePriceTriggers() internal view;

_registerBoundedSharePriceTriggers

Register a gas-bounded trigger set: a pre/post tx-wide envelope + per-call checks.

The tx-wide check (assertSharePriceEnvelopeBounded) compares share price only between the pre-tx and post-tx forks — 2 fork points, O(1) in call-frame count — so it never triggers the all-forks scan that OOGs on expensive-totalAssets() vaults. It is two-sided: it catches both dilution (share price down) and unexpected inflation (share price up, e.g. a donation / direct-transfer manipulation) reached through ANY entrypoint, including non-ERC-4626 selectors the per-call check never observes.

function _registerBoundedSharePriceTriggers() internal view;

_registerPerCallSharePriceTriggers

Register only the per-call share-price checks (deposit/mint/withdraw/redeem).

Each fires once per matching ERC-4626 operation and uses the cheap 2-fork assetsMatchSharePriceAt around that call.

function _registerPerCallSharePriceTriggers() internal view;

assertSharePriceEnvelope

Verifies the share price did not decrease beyond tolerance across the entire transaction.

Compares PreTx and PostTx only. Intermediate call snapshots can contain healthy temporary asset/share ordering differences and are deliberately ignored.

function assertSharePriceEnvelope() external;

assertSharePriceEnvelopeBounded

Tx-wide share-price envelope bounded to the pre-tx vs post-tx comparison.

Two-sided within tolerance — catches both dilution (decrease) and unexpected inflation (increase, e.g. a donation / direct-transfer share-price manipulation), regardless of the entrypoint used. Evaluated at exactly two fork points via assetsMatchSharePriceAt, so it stays within the assertion gas budget even for vaults with an expensive computed totalAssets(). Unlike assertSharePriceEnvelope, it does not inspect intermediate call-boundary forks; the per-call checks cover settlement during standard ERC-4626 ops.

function assertSharePriceEnvelopeBounded() external;

assertPerCallSharePrice

Verifies each individual deposit/mint/withdraw/redeem call does not decrease the share price beyond tolerance.

Uses the triggering call’s endpoint snapshots and permits share-price increases.

function assertPerCallSharePrice() external;