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

BalanceConservationAssertion

Git Source

Inherits: AccessControlBaseAssertion

Title: BalanceConservationAssertion

Author: Phylax Systems

Asserts that specified account token balances do not change during the transaction, catching unauthorized balance changes for accounts that should remain untouched. Invariants covered:

  • Treasury / reserve protection: treasury or reserve account balances remain unchanged unless an authorized governance path is followed.
  • Escrow protection: escrow contract balances are conserved across the transaction.
  • Unauthorized transfer detection: catches privilege misuse, reentrancy side effects, or unexpected execution paths that drain protected accounts.
  • Custodial leg verification: for RWA protocols, outflows only along known subscription/redemption legs.

Uses the V2 conserveBalance(fork0, fork1, token, account) precompile to compare balanceOf(account) at PreTx vs PostTx for each protected (token, account) pair. The assertion makes one precompile call per protected pair, so keep the watched set small enough for the adopter’s assertion gas budget until a batched balance conservation precompile is available. Implementers must override _conservedBalances() to declare which (token, account) pairs to protect. For selector-aware outflow caps (e.g., looser limits on approved withdraw/redeem selectors), use per-function triggers and custom assertion logic instead of this mixin.

Functions

_conservedBalances

Returns the (token, account) pairs whose balances must not change.

Override to declare the protocol-specific protected balances. Common targets:

  • Protocol treasury / DAO treasury USDC/ETH balances
  • Pool cash reserves (Maple pool, Centrifuge escrow)
  • Vault reserves and fee-leftover accounts (Lido stVault)
  • Collateral and burner flows (Symbiotic vault)
  • RWAHub / subscription-redemption custodial balances (Ondo)
function _conservedBalances() internal view virtual returns (ConservedBalance[] memory balances);

Returns

NameTypeDescription
balancesConservedBalance[]Array of (token, account) pairs to conserve.

_registerBalanceConservationTriggers

Register the default trigger set for balance conservation.

Uses registerTxEndTrigger so the check fires once after the transaction completes. Call this inside your triggers().

function _registerBalanceConservationTriggers() internal view;

assertBalanceConservation

Verifies that all protected account balances are unchanged across the transaction.

Checks each (token, account) pair using the conserveBalance precompile at PreTx vs PostTx. Reverts on the first pair whose balance changed.

function assertBalanceConservation() external view;

Errors

BalanceChanged

error BalanceChanged(address token, address account);

Structs

ConservedBalance

A (token, account) pair whose balance must be conserved across the transaction.

struct ConservedBalance {
    /// @notice The ERC20 token address.
    address token;
    /// @notice The account whose balance should remain unchanged.
    address account;
}