Invariant Testing Explained in Detail
Invariant testing checks properties that should always remain true. The test runner generates sequences of calls, not only single inputs, then checks whether the property still holds.
Common smart contract invariants include solvency, conservation of assets, total supply consistency, access control boundaries, and limits on protocol state.
Smart contract example
A lending vault might claim that it can always cover user withdrawals. An invariant can express that directly:
function invariant_vaultSolvent() public view {
assertGe(vault.totalAssets(), vault.totalLiabilities());
}
A stronger test runs deposits, withdrawals, borrows, repayments, liquidations, and oracle updates in many orders, closer to how users and markets interact with the system.
Invariant Testing in Auditing
Many protocol bugs only appear after a sequence of valid actions. A single deposit test may pass. A deposit, partial withdrawal, price update, liquidation, and second withdrawal may break accounting.
Invariant testing fits DeFi well because protocol safety often depends on relationships between balances, shares, debt, collateral, and prices. It can expose issues behind flash loan attacks, oracle manipulation, and accounting bugs.
Red flags in code
-
No clear statement of what must always be true.
-
Accounting spread across contracts without system-level tests.
-
Invariants that only check one contract in isolation.
-
Handlers that never call risky functions.
-
Ghost variables that are updated differently from the real protocol.
-
Invariants that pass because inputs are over-constrained.
How to test or review it
-
Write the invariant in plain English first.
-
Use handlers to model realistic actors and call sequences.
-
Track off-chain expectations with ghost variables when the contract does not expose enough state.
-
Check call coverage. If the fuzzer never reaches liquidations, admin transitions, oracle updates, or withdrawal paths, the result is weak.
-
Reduce failing traces to the smallest sequence that explains the bug.
-
Use fuzz testing first when the risk is input-specific rather than sequence-specific.
Keep learning this topic
Fuzz Testing
Fuzz testing sends many generated inputs through smart contract code to find edge cases, unexpected reverts, broken assumptions, and state transitions that normal unit tests miss.
ERC-4626 Vaults
ERC-4626 is the tokenized vault standard where users deposit an asset and receive vault shares that represent a claim on the vault's assets.
Price Manipulation
Price manipulation is the intentional movement of an asset, pool, share, or collateral price so a protocol values assets incorrectly.
Reentrancy
Reentrancy in Solidity explained with real exploit flow, vulnerable patterns, and practical defenses like checks-effects-interactions and reentrancy guards.
Flash Loans Attacks
Learn how flash loan attacks amplify oracle, accounting, and governance bugs in one transaction. See DeFi examples, exploit mechanics, and audit defenses.
Oracle Manipulation & Price Manipulation
Oracle manipulation attacks distort price feeds, spot prices, and exchange rates. Learn Chainlink checks, TWAP defenses, and auditor review steps.
Smart Contract Audit Checklist
Use this SCH tool to turn the concept into practical audit work.
Practice this in real audit scenarios
Definitions help, but auditors need reps. SCH turns concepts like Invariant Testing into exploit labs, code review habits, and report-writing practice.
Start the free trial or see the full smart contract auditing course.