Fatskills
Practice. Master. Repeat.
Study Guide: Blockchain and Web3 Development: Enterprise and Regulation Smart Contract Auditing and Formal Verification
Source: https://www.fatskills.com/cryptocurrency-bitcoin-blockchain-and-more/chapter/blockchain-and-web3-development-blockchain-and-web3-development-enterprise-and-regulation-smart-contract-auditing-and-formal-verification

Blockchain and Web3 Development: Enterprise and Regulation Smart Contract Auditing and Formal Verification

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~6 min read

Smart Contract Auditing & Formal Verification – Study Guide
(Designed for software engineers & finance professionals moving into Web3)


What This Is

Smart contract auditing is the systematic review of Solidity (or other EVM‑compatible) code to find bugs, economic flaws, and security vulnerabilities before the contract is deployed. Formal verification goes a step further: it uses mathematical proofs and tools (e.g., SMT solvers, model checkers) to prove that the contract’s logic always satisfies a set of safety properties. Together they give users confidence that a DeFi swap, NFT minting flow, or DAO voting contract will behave exactly as intended—​a prerequisite for handling real money on‑chain.

Real‑world example: The Uniswap V2 router contract was audited and formally verified for the “no‑loss” invariant reserve0 * reserve1 = k. The proof guarantees that a swap cannot magically create or destroy tokens, protecting liquidity providers.


Key Terms & Code Snippets

  • Reentrancy Attack – A malicious contract re‑enters a vulnerable function before the first call finishes, draining funds.
    solidity // vulnerable function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    (bool ok,) = msg.sender.call{value: amount}("");
    require(ok);
    balances[msg.sender] -= amount; // <-- wrong order }

  • Check‑Effects‑Interactions (CEI) Pattern – The safe ordering: check conditions, effect state changes, then interact with external contracts.
    solidity balances[msg.sender] -= amount; (bool ok,) = msg.sender.call{value: amount}(""); require(ok);

  • Static Analyzer – Automated tool (e.g., Slither, MythX) that scans Solidity source for known vulnerability patterns without executing the code.

  • Formal Specification – A precise, math‑based description of what a contract must do (e.g., “totalSupply never decreases”). Written in languages like Solidity’s assert, require, or external DSLs such as K‑framework.

  • SMT SolverSatisfiability Modulo Theories engine (e.g., Z3) that checks logical formulas generated from the contract; used by tools like Certora and VeriSol.

  • Model‑Checking – Exhaustively explores all possible states of a contract (within a bounded depth) to find violations. Example tool: Echidna (property‑based fuzzing).

  • Delegatecall Vulnerabilitydelegatecall runs code in the context of the calling contract, allowing storage‑slot clashes. Audits must verify that only trusted libraries are used.

  • Gas‑Profiler – A tool (Hardhat’s hardhat-gas-reporter or Tenderly) that measures gas consumption per function, helping auditors spot expensive patterns.

  • Economic Attack – Not a code bug but a design flaw (e.g., price‑oracle manipulation). Auditors model attacker incentives and simulate worst‑case scenarios.

  • ERC‑4626 (Vault Standard) – A recent tokenized vault interface; auditors check that deposit/withdraw maintain a 1:1 share‑to‑asset ratio under all conditions.

  • Optimistic Rollup Fraud Proof – In an Optimistic rollup, auditors must ensure that any state transition can be challenged within the dispute window; formal verification often includes the fraud‑proof logic.

  • Zero‑Knowledge (ZK) Rollup Proof – ZK rollups provide on‑chain validity proofs; auditors verify that the SNARK verifier contract correctly validates the proof and that the off‑chain prover respects the same spec.


Step‑by‑Step / Process Flow

  1. Write & Unit‑Test – Develop the contract in Remix or VS Code, add a comprehensive test suite with Hardhat/Foundry.
    bash
    npx hardhat test

  2. Static Analysis – Run Slither and MythX on the source. Fix every “high‑severity” finding before moving on.
    bash
    slither contracts/MyToken.sol

  3. Formal Specification – Encode critical invariants with require/assert and, if needed, write a Certora/VeriSol spec file (.spec).

  4. Model‑Check / Fuzz – Execute Echidna (property‑based fuzz) and/or Foundry’s forge fuzz to explore edge‑case inputs.
    bash
    echidna-test contracts/MyToken.sol --contract MyToken

  5. Formal Verification – Run the chosen verifier (e.g., Certora Prover) against the spec. Address any counter‑example it produces.
    bash
    certora run MyToken.spec --solc solc-0.8.20

  6. Deploy & Monitor – Deploy to a testnet (Goerli) via Hardhat + Infura, then attach a monitoring bot (Tenderly) that watches for abnormal state changes.
    bash
    npx hardhat run scripts/deploy.js --network goerli


Common Mistakes

  • Mistake: Using tx.origin for access control.
    Correction: Use msg.sender and role‑based libraries (OpenZeppelin AccessControl). tx.origin can be spoofed through a phishing contract, allowing attackers to bypass restrictions.

  • Mistake: Forgetting to reset a reentrancy guard after a require fails.
    Correction: Implement the nonReentrant modifier from OpenZeppelin; it automatically restores the guard even on revert, preventing a lock‑out.

  • Mistake: Assuming delegatecall is safe because the library contract is “audited”.
    Correction: Verify storage layout compatibility and restrict the delegatecall address to an immutable constant; otherwise an attacker could upgrade the library to malicious code.

  • Mistake: Relying solely on unit tests for security.
    Correction: Complement tests with static analysis, fuzzing, and formal verification; tests cover expected paths, while the other tools explore the unexpected.

  • Mistake: Hard‑coding block timestamps (block.timestamp) for critical deadlines.
    Correction: Use block.number or a trusted oracle for time‑sensitive logic; miners can manipulate timestamps within a small window, potentially triggering early withdrawals.


Blockchain Developer Interview / Practical Insights

  1. “Explain the difference between call and delegatecall and when each is appropriate.”
    Look for understanding of context (storage, msg.sender) and security implications.

  2. “How would you prove that an ERC‑20 token’s totalSupply never exceeds a cap?”
    Expect a mention of require(totalSupply <= CAP) plus a formal invariant checked by a tool like Certora.

  3. “What are the trade‑offs between Optimistic Rollups and ZK Rollups from an auditor’s perspective?”
    Key points: fraud‑proof window vs on‑chain proof verification, data availability, and the extra verification logic that must be audited.

  4. “Describe a scenario where a price‑oracle manipulation could break a DeFi contract, and how you’d mitigate it.”
    Look for multi‑source aggregation, time‑weighted averages, and fallback mechanisms.


Quick Check Questions

  1. Scenario: A contract uses tx.origin == owner to restrict withdrawals.
    Answer: Dangerous – a malicious contract can trick the owner into calling it, making tx.origin the owner’s address and allowing the attacker to withdraw.

  2. Scenario: Your ERC‑4626 vault’s withdraw function updates totalShares after transferring assets.
    Answer: Wrong order – if the external token transfer re‑enters, the attacker can withdraw more than their share; fix by updating state before the external call (CEI).

  3. Scenario: You run Slither and see a “Unchecked low‑level call” warning on a function that sends ETH.
    Answer: The call’s return value must be checked (require(success)) to ensure the transfer succeeded; otherwise the contract may think it sent funds when it didn’t.


Last‑Minute Cram Sheet (10 one‑liners)

  1. ⚠️ Never trust tx.origin for auth; always use msg.sender.
  2. CEI = Check → Effect → Interaction – the universal anti‑reentrancy recipe.
  3. require = user‑error (revert, no gas refund); assert = internal bug (consumes all remaining gas).
  4. Solidity 0.8.x introduced built‑in overflow checks; older versions need SafeMath.
  5. EIP‑2929 (gas cost changes) makes SLOAD 800 gas → 2100 gas; factor this into gas‑optimizations.
  6. Static analyzers catch ~80 % of known patterns; formal verification catches the rest.
  7. delegatecall shares the caller’s storage layout – mismatched slots = silent corruption.
  8. ERC‑20 vs ERC‑777: ERC‑777 adds hooks (tokensReceived) that can be abused if not vetted.
  9. Optimistic rollup fraud proofs require a dispute window (usually 7 days) – auditors must verify the challenge logic.
  10. Common attack vectors: Reentrancy, integer overflow, unchecked external calls, oracle manipulation, and improper access control.

Happy auditing! ?



ADVERTISEMENT