Audit-grade security review for EVM smart contracts and DeFi protocols
Scanned 9/2/2026
Install to Claude Code
npx -y skills add Canhada-Labs/ceo-orchestration --skill blockchain-security-audit --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Blockchain Security Audit?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/canhada-labs-blockchain-security-audit)More formats (shields.io, HTML) on the badges page.
---
name: blockchain-security-audit
description: Audit-grade security review for EVM smart contracts and DeFi protocols
on Solidity ^0.8.24 with OpenZeppelin Contracts v5.x. Codifies EthTrust Security
Levels + OWASP SCSVS as primary coverage with SWC + DASP Top 10 retained as
legacy mapping, vulnerability-class detection technique, formal-verification
workflow (Certora / Halmos / SMTChecker), severity schema with mandatory
proof-of-exploitability for High+ findings, audit-report structure, and DeFi
composability threat model (oracle dependence, flash-loan surface, governance
vector, bridge trust). Operates under ADR-052 VETO floor — sign-off authority
delegated to the existing security-engineer archetype on Opus (claude-opus-4-8);
Critical or High findings without a verified fix BLOCK ship. Use when reviewing pre-mainnet
contracts, pre-upgrade diffs, post-incident forensics, governance-parameter
changes, treasury-flow code, bridge messaging code, or any contract surface where
a single exploit transaction can drain user funds or brick the protocol.
owner: Smart Contract Auditor (domain persona; sign-off delegated to security-engineer archetype on Opus)
secondary_owner: DeFi Threat Researcher (domain persona)
tier: domain:fintech
scope_tags: [smart-contract-audit, defi-security, vulnerability-research, formal-verification, exploit-analysis, audit-reporting]
veto_floor: true
model_required: claude-opus-4-8
inspired_by:
- source: msitarzewski/agency-agents/specialized/blockchain-security-auditor.md@783f6a72bfd7f3135700ac273c619d92821b419a
license: MIT
relationship: structural_inspiration
authored_by: ceo-orchestration framework
authored_at: 2026-05-07
- source: affaan-m/ecc/skills/nodejs-keccak256/SKILL.md@81af40761939056ab3dc54732fd4f562a27309d0
license: MIT
relationship: structural_inspiration
authored_by: ceo-orchestration framework
authored_at: 2026-07-07
# --- smart-loading fields (PLAN-083 Wave 0b sub-agent 0.7b) ---
domain: fintech
priority: 2
risk_class: high
stack: [solidity]
context_budget_tokens: 1500
inactive_but_retained: false
repo_profile_binding:
frontend: {active: true, priority: 9}
engine: {active: true, priority: 4}
fintech: {active: true, priority: 2}
trading-readonly: {active: true, priority: 2}
generic: {active: true, priority: 7}
activation_triggers:
- {event: file-edit, glob: "**/*.sol"}
- {event: help-me-invoked, regex: "(?i)smart.?contract|audit|reentr|exploit|swc|owasp.?scsvs"}
# --- K1 paths: native file-touch activation (PLAN-135 W3 unit k1a) ---
paths:
- "**/*.sol"
- "**/contracts/**"
- "**/bridge/**"
- "**/treasury/**"
source: affaan-m/ecc@81af4076 skills/nodejs-keccak256/
license: MIT
---
# Blockchain Security Audit
Smart contracts are bearer assets. A single reachable bug drains the
treasury in one transaction and the chain remembers forever. This skill
codifies the audit doctrine that turns a code-review into an audit:
named threat coverage, runnable proofs of exploitability, formal
property checks, and a report whose findings a developer can fix
directly. It operates under ADR-052 VETO floor — sign-off is delegated
to the existing `security-engineer` archetype running on Opus, and
Critical or High findings without a verified patch BLOCK ship.
## Cardinal Rule
Every Critical or High finding MUST ship with a runnable Foundry test
asserting attacker profit or invariant violation, OR a step-by-step
exploit transcript with concrete impact in USD or token-units. A
finding without proof-of-exploitability is either Medium-or-below, or
the auditor downgrades it and explains why. Severity without proof is
severity-by-feeling, and severity-by-feeling is how audit reports
become marketing copy.
## Fail-Fast Rule
Halt the audit and escalate to Owner BEFORE writing findings if ANY:
1. Deployed bytecode does not match submitted source
(`forge verify-bytecode` mismatch or block-explorer source unverified).
2. Compiler pragma is unpinned or pinned below `0.8.x` (`pragma solidity
>=0.7.0` rejected; `pragma solidity ^0.8.24` accepted as a minor
range per Solidity SemVer — deployments requiring exact
reproducibility additionally pin a specific patch in build config).
3. Test suite < 70% line coverage on contracts in scope.
4. Critical functions (admin, upgrade, withdrawal) lack any test.
5. Scope is moving — files change during the audit window without a
frozen commit hash.
A halted audit emits a single-line gate report ("Audit blocked —
condition #N — resume after team resolves") and zero findings.
Findings written against moving scope are unfalsifiable.
## When to Apply
Trigger an audit (this skill MUST be loaded by the `security-engineer`
archetype operating on Opus) in any of the following situations:
- **Pre-mainnet deployment** of any contract handling user funds,
governance votes, or bridge messages — mandatory regardless of TVL.
- **Pre-upgrade** of a proxy whose new implementation changes storage
layout, access control, or external-call surface.
- **Post-incident forensics** within 24h of an exploit detection
(incident-commander archetype owns the runbook; this skill owns
root-cause attribution).
- **Governance-parameter change** altering fee math, oracle feeds,
liquidation thresholds, or quorum / timelock values.
- **Regulatory or compliance review** triggered by jurisdictional
filing (MiCA, BitLicense, TVT-rated custody) where the audit
artifact is a legal deliverable.
- **Dependency bump** of a security-critical library (OpenZeppelin
major, Chainlink aggregator, Uniswap router, LayerZero endpoint).
- **New external integration** (oracle, AMM pair, bridge connector)
introducing a fresh trust assumption.
A `core/code-review-checklist` sweep is NOT a substitute for any of
the above triggers.
## ADR-052 VETO Floor — Audit Authority
This skill is registered `veto_floor: true` and
`model_required: claude-opus-4-8`. Sign-off authority is delegated to
the existing `security-engineer` archetype operating under this skill;
the same-LLM author's verdict is insufficient. Sign-off is mandatory on
any L3+ change touching the surface listed under §When to Apply. A
reviewer waiver is insufficient.
| Audit verdict | Ship action |
|---------------|-------------|
| **PASS** (no Critical, no High) | Ship with documented Medium / Low remediation plan |
| **PASS-WITH-FIXES** (High patched + re-tested) | Ship after re-audit confirms patch closes the exploit |
| **ADJUST** (open Mediums, no Critical/High) | Ship; attach Medium remediation timeline to PR |
| **SOFT REJECT** (open High) | Block ship; team patches; auditor re-runs PoC |
| **VETO** (open Critical) | Block ship; Owner override + ADR amendment + auditor amendment required |
A VETO is lifted only by: (a) verified patch where the auditor's PoC
fails on the patched contract, or (b) Owner-signed ADR amendment
explicitly acknowledging residual risk + compensating control +
monitoring rule. Reviewer override of a VETO is a governance
violation logged to audit trail.
## Severity Schema
Severity is CVSS-style impact × likelihood with a mandatory PoC gate
at High and above. Likelihood reads attacker capability (privileges,
transaction count, capital) against the threat model.
| Severity | Impact | Likelihood | PoC requirement |
|----------|--------|------------|-----------------|
| **Critical** | Direct loss of user funds; protocol insolvency; permanent DoS; sovereign-key compromise | No special privilege; single tx; capital ≤ flash-loan limit | Runnable Foundry test asserting attacker profit > 0 OR invariant violation |
| **High** | Conditional fund loss (specific state); privilege escalation; brick-by-admin; locked withdrawals | Privileged role compromise OR market state OR multi-step ≤ 5 tx | Runnable Foundry test OR step-by-step exploit transcript with on-chain trace |
| **Medium** | Griefing; temporary DoS; value leakage under specific conditions; missing access on non-fund function | Ordinary user OR adversarial market participant | Concrete scenario + impact range; PoC strongly recommended |
| **Low** | Best-practice deviation with security implication; gas-DoS-enabling inefficiency; missing forensic event | Minor; no direct exploit | Description + why-it-matters |
| **Informational** | Code quality; documentation gap; style inconsistency | n/a | Description only |
A finding without the listed PoC artifact is downgraded one level, or
the auditor explains in-finding why the artifact is infeasible.
Marking a fund-loss bug "Informational" to soften delivery is an
audit anti-pattern (§Anti-patterns).
## Coverage Standards — EthTrust + SCSVS Primary, SWC + DASP Legacy
Every audit MUST sweep the **current maintained** standards as
primary coverage:
- **EEA EthTrust Security Levels Specification** (Enterprise Ethereum
Alliance) — actively maintained Levels [S]/[M]/[Q] classification.
- **OWASP SCSVS** (Smart Contract Security Verification Standard) —
actively maintained verification checklist.
The **SWC Registry** (https://swcregistry.io) and **DASP Top 10** are
retained as **legacy mapping** for historical findings and
defense-in-depth — SWC is no longer actively maintained, and the EEA
project explicitly references EthTrust + SCSVS as the current baseline.
The table below maps SWC IDs (legacy) ↔ vulnerability class ↔ detection
technique. An audit that skips a class without justification fails
Pass-1 review regardless of which taxonomy surfaces it.
| SWC | DASP | Class | Primary detection |
|-----|------|-------|-------------------|
| SWC-101 | DASP-3 | Integer over/underflow | Solidity 0.8+ default check; scrutinise `unchecked { ... }` line-by-line |
| SWC-104 | DASP-4 | Unchecked call return | Slither `unchecked-lowlevel` / `unchecked-send` |
| SWC-105 | DASP-2 | Unprotected ether withdrawal | Slither `arbitrary-send-eth`; manual auth sweep |
| SWC-106 | DASP-2 | Unprotected SELFDESTRUCT | Slither `suicidal` |
| SWC-107 | DASP-1 | Reentrancy | Slither `reentrancy-eth` / `reentrancy-no-eth`; CEI review |
| SWC-112 | DASP-2 | Delegatecall to untrusted callee | Slither `controlled-delegatecall`; manual proxy review |
| SWC-114 | DASP-7 | Transaction-order dependence | Manual: tx-ordering-dependent state; commit-reveal? |
| SWC-115 | DASP-2 | tx.origin authentication | Slither `tx-origin` |
| SWC-116 / 120 | DASP-8 / DASP-6 | Block values as time + weak randomness | Manual: `block.timestamp` / `block.number` / `blockhash` in critical paths |
| SWC-118 | n/a | Initializer callable twice | Slither `uninitialized-state`; `_disableInitializers` in ctor |
| SWC-124 | n/a | Write to arbitrary storage slot | Manual: assembly `sstore` review |
| SWC-128 | DASP-5 | DoS via gas limit | Echidna fuzz unbounded loops; array-growth review |
| SWC-132 | DASP-5 | Unexpected ether balance | Manual: `address(this).balance` for accounting |
| SWC-136 | n/a | Unencrypted private data on-chain | Manual: `private` storage holding secret-by-design |
DASP categories not surfacing directly in the SWC subset above:
DASP-9 (Short Address) is a post-Constantinople non-issue; DASP-10
(Unknown Unknowns) is the residual class formal verification targets.
DASP-8 (Time Manipulation) maps onto SWC-116; DASP-6 (Bad Randomness)
maps onto SWC-120 — both are present in the table.
## Vulnerability Classes
Each class names: detection technique and a real-exploit reference
where one exists. The exploit corpus IS the audit pattern library —
the next exploit is usually a variant of a previous one.
**Reentrancy.** Slither (`reentrancy-eth`, `reentrancy-no-eth`,
`reentrancy-benign`) catches the structural pattern at high
confidence. Manual review extends to ERC-777 / ERC-1155 hook-induced
reentrancy and read-only reentrancy through view functions consumed
as oracle inputs. Findings cite: external-call site, following state
update, attacker re-entry path, victim balance-impact. References:
The DAO 2016 (~$60M ETH), Curve Finance July 2023 (Vyper compiler
reentrancy-guard miscompilation, ~$70M).
**Integer over/underflow.** Solidity 0.8.0+ enables checked arithmetic
by default; the real surface is `unchecked { ... }` blocks and
assembly. Every `unchecked` block is reviewed line-by-line and the
auditor records which invariant guarantees no wraparound. Reference:
bzx 2020 margin-trade integer issue.
**Access control.** Enumerate every state-modifying external / public
function and verify an explicit modifier OR inline auth check from
a verified token / role registry. Cross-check upgrade paths
(`_authorizeUpgrade`, `initialize` / `_disableInitializers`).
References: Parity Wallet 2017 (uninitialized library — anyone
calls `initWallet`; ~$30M + ~$150M frozen across two incidents),
Wormhole 2022 (guardian-set signature-verification bypass, ~$320M).
**Oracle manipulation.** Identify every price source (Uniswap V2 spot,
Uniswap V3 TWAP, Chainlink aggregator, custom). Spot prices from a
single AMM pool are flash-loan-manipulable in one transaction — flag
as Critical unless flash-loan-resistant (checkpointed TWAP, multi-
oracle median). Verify Chainlink staleness window + roundId
monotonicity. Reference: Mango Markets 2022 (~$117M; thin perp
market).
**Flash-loan attacks.** Model every external view or state read that
influences a financial decision against an actor renting ≥ $100M for
one block. Examples: collateral valuation reads spot price; voting
weight reads token balance at `block.timestamp`; liquidation reads
under-margin pools without TWAP smoothing. References: Euler Finance
2023 (~$197M; donate-to-reserves + liquidation-incentive math),
Beanstalk 2022 (~$182M; flash-loan governance vote).
**MEV / sandwich.** Identify swap / deposit / withdraw paths with a
slippage parameter. Verify `minAmountOut` is set from the user, not
from `getAmountsOut(...)` at call-time (current-reserves reads make
slippage ineffective against sandwiches). Verify deadline parameter
is enforced and short-bounded.
**Signature replay.** Every `permit` / EIP-712 path MUST include
`nonce`, `deadline`, and `chainId` in the signed digest. Verify
nonce-incremented-on-use and contract chainId matches
`block.chainid`. Reference: Optimism / Wintermute 2022 (~$20M;
chainId omission enabled multi-chain replay).
**Upgradeability.** Verify storage layout compatibility across
versions (`forge inspect ... storageLayout` diff or
`@openzeppelin/upgrades`). Verify `initialize()` has `initializer`
modifier; implementation has `_disableInitializers()` in constructor.
Verify `_authorizeUpgrade` is owner / multisig / timelock. Verify no
function-selector clash between proxy admin and implementation.
Reference: Audius 2022 (~$6M; storage collision in upgrade).
**Governance attacks.** Model voting-power source (snapshot vs
balance at vote time), delegation, timelock, quorum. Voting that
reads token balance at `block.timestamp` of cast (not snapshot) is
flash-loan-attackable. If attacker capital required for quorum ×
token price < typical exploit profit, flag Critical.
**Cross-chain bridge.** Verify signer-set freshness (rotated keys
honored, revoked keys rejected), per-message nonce committed on
destination, proof verification matches the attestation format,
and no source-emit / destination-redeem desync path. References:
Ronin Bridge 2022 (~$625M; 5-of-9 multisig key compromise), Wormhole
2022 (~$320M; signature-verification bypass), Nomad Bridge 2022
(~$190M; uninitialized trusted-root + replayable proofs), BNB Bridge
2022 (~$570M; IAVL Merkle proof verification flaw).
**ERC-4626 inflation (donation attack).** Any ERC-4626 vault MUST
mitigate the first-depositor share-price inflation attack — virtual
shares + virtual assets (OpenZeppelin v5 default), or deployer seed
deposit locked forever, or per-share floor enforced. Test: compute
share price after a 1 wei deposit + large direct-transfer donation;
if the next depositor receives 0 shares for any non-zero deposit,
flag Critical.
**JIT (just-in-time) liquidity.** Concentrated-liquidity AMMs
(Uniswap V3) and lending protocols with lender-rotation surface let
LPs / lenders provide liquidity for one block, capture fees /
interest, and withdraw. When protocol fee math assumes time-weighted
exposure but implementation pays per-block, JIT extracts fees from
passive LPs.
**Off-chain hash-algorithm mismatch (Keccak-256 vs NIST SHA3-256).**
The audit surface extends past on-chain bytecode to the off-chain
helpers a contract *trusts* to produce hashes byte-identical to its
own `keccak256(...)` — indexers, signing backends, allowlist / Merkle
generators, storage-slot readers. Ethereum uses original Keccak-256;
several standard libraries expose NIST FIPS-202 SHA3-256 under a
confusingly similar name (Node's `crypto.createHash('sha3-256')`
being the canonical trap), and the two return **different** digests
for the same input with no error raised. Note the scope: inside
Solidity, `keccak256(...)` is already correct — this class lives in
the JS/TS/tooling layer. Where it bites: function selectors and event
topics in an off-chain indexer; EIP-712 digests in a signing service
(the digest silently diverges from the contract's, so valid signatures
fail — or a "working" test-only path masks the divergence); Merkle
roots in an allowlist generator (an off-chain root built with the
wrong hash cannot be proven against on-chain `keccak256` — legitimate
users locked out of a mint/claim, or a wrong root committed); and
storage-slot derivation in a state reader (reads the wrong slot).
Severity tracks whether the mismatch gates funds or access: an
allowlist that controls a claim is High; a read-only indexer
inconsistency is Medium/Low. **Detection:** grep the off-chain code
(`grep -rn "createHash.*sha3"` over `*.ts`/`*.js`, excluding
`node_modules`); confirm JS/TS hashing uses a Keccak-aware helper
(ethers `keccak256` / `id` / `solidityPackedKeccak256`, viem
`keccak256`, web3 `keccak256` / `soliditySha3`); and require a
**parity test** asserting the off-chain digest equals the contract's
`keccak256` for a known vector before trusting any off-chain hash in a
fund or access path.
## Static + Dynamic Analysis Toolchain
Tools find different bug classes. Run all of them; do not assume any
single tool covers the surface.
| Tool | Author | Catches | Misses |
|------|--------|---------|--------|
| **Slither** | Trail of Bits | Reentrancy, suicidal, arbitrary-send, controlled-delegatecall, uninitialized-state, ERC-conformance, function-summary | Economic exploits, business-logic bugs, cross-contract composability |
| **Mythril** | ConsenSys | Symbolic-execution path coverage on small contracts; assertion violations; reachable selfdestruct | Scales poorly on > ~500 LOC; deep-call-graph timeouts |
| **Echidna** | Trail of Bits | Property-based fuzz on user-defined invariants; coverage-guided | Invariants the auditor did not write; rare-event paths |
| **Foundry invariants** | Paradigm | Property-based + stateful fuzz; faster than Echidna for smaller scopes | Same as Echidna; deep state-machine paths |
| **Halmos** | a16z | Symbolic execution with SMT backend; bounded-loop unrolling | Unbounded loops; non-linear arithmetic edge cases |
| **SMTChecker** | Solidity built-in | Inline assertion proof attempts; off-by-one in arithmetic | Cross-function reasoning; storage-aliased state |
| **forge verify-bytecode** | Foundry | Bytecode-vs-source mismatch | Compiler-introduced bugs (Vyper Curve case) |
The audit MUST run Slither + at least one fuzz tool (Echidna or
Foundry) + at least one symbolic tool (Mythril or Halmos) on every
contract in scope. Tool output is triaged — every flagged item is
either confirmed as a finding or annotated as a false positive with a
reason.
## Formal Verification Workflow
Formal verification proves properties hold across all inputs, not just
fuzz-explored inputs. Use it where the cost of an undiscovered bug
exceeds the cost of writing the spec.
| Tool | When to use | Spec language | Example property |
|------|-------------|---------------|------------------|
| **Certora Prover** | Critical money-flow contracts (vault accounting, AMM math, lending health-factor) | CVL (Certora Verification Language) | "totalAssets() ≥ Σ user_balances() always" |
| **Halmos** | Bounded symbolic checks during CI; faster iteration than Certora | Solidity test functions with `assert(...)` | "for all amount in [0, 2^96], deposit(amount) then withdraw(shares) returns ≤ amount" |
| **SMTChecker** | Function-local arithmetic safety; off-by-one detection | Solidity `assert` annotations | "no `unchecked` block can wrap" |
The auditor writes the property specification BEFORE running the tool;
a tool-passing run with no specs proves nothing. A passing Certora run
on three properties of a vault is more meaningful than a coverage-only
fuzz run on the same code. Properties MUST be derived from the
protocol invariants the team documents — if no invariants are
documented, the auditor extracts them and submits them as Finding
class "Informational — invariants not specified" before formal work
begins.
## Audit Report Structure
Reports the team can act on without follow-up clarifications are the
deliverable. Every report has these sections in this order:
**Front matter:** Project, Audit lead handle (domain persona) + delegated
VETO sign-off authority (`security-engineer` archetype on Opus per
ADR-052), Scope commit (40-hex SHA), Audit window (ISO start → ISO end),
Methodology pointer.
**§1 Executive Summary** — one-paragraph protocol description;
contract count and Solidity SLOC at `^0.8.24`; severity count matrix
(Critical / High / Medium / Low / Informational × Open / Fixed /
Acknowledged); verdict (PASS / PASS-WITH-FIXES / ADJUST / SOFT REJECT
/ VETO per §ADR-052 VETO Floor); one-paragraph residual-risk summary.
**§2 Scope** — per-contract SLOC and cyclomatic-complexity table;
explicit out-of-scope list with reasons.
**§3 Methodology** — numbered list: manual line-by-line review
(auditor + secondary), static analysis (Slither + Mythril symbolic),
property-based fuzz (Foundry invariants or Echidna), formal
verification (Certora / Halmos), economic and game-theory modelling,
bytecode verification (`forge verify-bytecode`).
**§4 Findings** — one subsection per finding. Mandatory fields:
- **ID:** `[C-NN]` / `[H-NN]` / `[M-NN]` / `[L-NN]` / `[I-NN]`
- **Severity / Status:** Critical|High|Medium|Low|Informational ×
Open|Fixed|Acknowledged
- **Location:** `ContractName.sol#Lxx-Lyy`
- **Class:** EthTrust SL ([S]/[M]/[Q]) and/or SCSVS V-NN section as
primary taxonomy; SWC-ID + DASP category as legacy mapping where
applicable
- **Description:** concrete vulnerability narrative
- **Impact:** attacker outcome; financial estimate; affected users
- **Proof of Concept:** runnable Foundry test in
`test/exploits/<ID>.t.sol` with `forge test --match-test <name>
-vvvv`, asserting `profit > 0` OR invariant violation (mandatory
for Critical / High per §Cardinal Rule)
- **Recommendation:** specific code change citing the line
**§5 Appendix** — tool output summaries, invariant catalogue, fuzz
seed list, bytecode-verification log.
Canonical Foundry PoC scaffold:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Test} from "forge-std/Test.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract Exploit_C_01 is Test {
IERC20 internal constant token = IERC20(0x0000000000000000000000000000000000000000); // replace with target token address
function setUp() public { vm.createSelectFork("mainnet", 18_500_000); }
function test_C_01_drainsVault() public {
address attacker = makeAddr("attacker");
uint256 before_ = token.balanceOf(attacker);
vm.startPrank(attacker);
// exploit steps ...
vm.stopPrank();
uint256 profit = token.balanceOf(attacker) - before_;
assertGt(profit, 0, "exploit should be net-profitable");
}
}
```
## Threat Model — DeFi Composability
Composability is the multiplier that turns a Medium finding in
contract A into a Critical finding in protocol B that integrates A.
Every audit MUST enumerate composability dependencies.
| Dependency | Threat | Audit question |
|------------|--------|----------------|
| **Oracle (price)** | Manipulable spot, stale Chainlink, single-point failure | Flash-loan-resistant? Staleness window enforced? Round-ID monotonicity? |
| **Flash loan provider** | Attacker rents ≥ $100M for one block | Does any path compute a decision from single-block-readable state? |
| **Governance** | Voting-weight attack via flash loan, vote-buying, timelock bypass | Snapshot-based? Quorum × token price > exploitable surplus? |
| **Bridge** | Source-chain message replay; signer-set compromise | Per-message nonce committed on destination? Signer-set rotation honored? |
| **Token (ERC-20)** | Fee-on-transfer, rebasing, blacklist, custom callbacks | Protocol assumes `transfer` returns exact `amount`? Supports ERC-777 / ERC-1155 hooks? |
| **AMM pool** | Pair-creation race; liquidity-removal during settlement | Pool address pinned or factory-derived per call? |
| **Off-chain hashing helper** | NIST SHA3 vs Keccak-256 divergence in an allowlist / signing / indexer path the contract trusts | Off-chain hash proven byte-equal to on-chain `keccak256` for a known vector? |
| **Liquidator bot** | Gas-DoS in liquidation path | Liquidation gas-bounded? Partial-liquidation when full reverts? |
A protocol integrating `n` upstream protocols inherits the threat
model of all `n`; the audit MUST list those threats and confirm the
integrating contract handles each.
## Anti-patterns
| Anti-Pattern | Why It's Wrong | Correct Approach |
|---|---|---|
| Marking a fund-loss finding "Informational" to soften delivery | Severity-by-feeling; team ships, attacker drains | Use §Severity Schema; downgrade only with documented reason |
| "Tested in testnet" treated as audit | Testnet ≠ mainnet adversary model; no flash-loan, no real capital, no MEV | Independent + adversarial review on mainnet fork (`vm.createSelectFork`) |
| Gas-cost review presented as security review | Gas optimisation can introduce bugs (storage packing, custom errors) | Separate gas from security; flag gas changes that alter access-check ordering |
| LLM-only review with no manual line-by-line | LLMs miss novel logic and economic exploits; first-pass triage only | Manual review is the floor; tools and LLMs are amplifiers |
| Slither / Mythril clean → "no findings" | Tools catch ~30% of real bugs; the other 70% are logic + economic | Manual review mandatory; tool-clean is necessary, not sufficient |
| Trusting OpenZeppelin = no review | Misuse of safe libraries is its own class (Initializable misuse, ReentrancyGuard wrong scope) | Review the integration even when the library is audited |
| `onlyOwner` accepted without checking ownership | Owner is often an EOA; one stolen key = full compromise | Verify owner is multisig OR timelock; flag EOA owner on production |
| Auditing source while deployed bytecode differs | Supply-chain attack vector | `forge verify-bytecode`; halt audit on mismatch (§Fail-Fast) |
| Off-chain helper hashes Ethereum data with `crypto.createHash('sha3-256')` | NIST SHA3-256 ≠ Keccak-256; digest diverges from on-chain `keccak256` silently — breaks allowlists, EIP-712, selectors | Keccak-aware lib (ethers / viem / web3) + a parity test vs on-chain `keccak256` for a known vector |
| High finding without runnable PoC | Unfalsifiable; team disputes; auditor loses leverage | Foundry test asserting profit > 0 OR invariant violation (§Cardinal Rule) |
| Audit window includes scope-changing commits | Findings against moving target are stale on delivery | Halt audit (§Fail-Fast #5); resume on frozen commit |
| Auditor amends own VETO without re-PoC | Defeats VETO floor purpose | Re-run PoC against patched contract; verdict only changes if PoC fails |
## Cross-References
- `core/security-and-auth` — broader OWASP / threat-model worksheet /
detection-as-code; this skill is the EVM-smart-contract sub-domain.
- `core/code-review-checklist` — general code-review patterns; an
audit subsumes a code-review, NOT vice-versa (see §When to Apply).
- `domains/fintech/skills/solidity-smart-contracts` — Solidity
authoring patterns this skill audits against; auditor and author
MUST agree on Solidity / OpenZeppelin version pin.
- `domains/fintech/skills/financial-correctness-and-math` — fixed-
point math, round-to-protocol direction, invariant-system patterns;
vault-accounting findings cite this skill's invariants.
- `core/incident-management` — runbook for active-exploit response;
incident-commander owns execution, auditor owns root-cause and
post-mortem authoring.
- `core/observability-and-ops` — audit-log shape for on-chain events
the auditor recommends emitting; SIEM ingestion contract.
## ADR Anchors
- **ADR-052 (multi-model dispatch by role)** — registers `veto_floor`
semantics and Opus-mandate for security archetypes. This skill
declares `veto_floor: true` and `model_required: claude-opus-4-8`
per ADR-052 §VETO_FLOOR_ROLES. Audit sign-off authority is delegated
to the existing `security-engineer` archetype already in
`_lib/agent_frontmatter.VETO_FLOOR_ROLES` (5-role floor post-Wave-1c);
no new agent role is added in Wave 2.
- **ADR-058 (brainstorm gate and two-pass review)** — the audit
workflow IS a two-pass review: Pass 1 inventories scope and builds
threat model; Pass 2 generates findings against the model. A
single-pass audit fails ADR-058 §two-pass review.
- **ADR-095 (calendar-gate retraction + Codex MCP cross-LLM gate)** —
auditor findings are eligible for same-LLM-bias review via Codex
MCP. Confirmations #25-#29 (S90-S93) show Codex catching ~5-10
unique findings per ceremony that same-LLM Claude archetypes
missed; this skill recommends Codex re-pass on every Critical /
High finding before closeout.
Runnable parity proof (the digests differ on the SAME input — no error is
raised, which is exactly why the class survives review):
```js
// node — SHA3-256 (NIST FIPS-202) vs Keccak-256 (original, Ethereum)
const { createHash } = require("node:crypto");
const { keccak256 } = require("js-sha3"); // or ethers' keccak256
const nist = createHash("sha3-256").update("").digest("hex");
const eth = keccak256("");
console.log(nist);
// a7ffc6f8bf1ed76651c14756a061d62683576285280f30987fda07fda0f9724c
console.log(eth);
// c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
console.log(nist === eth); // false — pin this assertion in a parity test
```
Both calls succeed; only a parity test against a known Keccak-256 vector
(e.g. the empty-string digest above, or any digest cross-checked with
Solidity's `keccak256`) catches a wrong-primitive substitution.
## References
- SWC Registry — https://swcregistry.io
- DASP Top 10 — DeFi-era class taxonomy
- Standards: EIP-712, ERC-20 / ERC-4626 / ERC-777 / ERC-1155,
EIP-2535 (Diamond), EIP-1967 (Proxy storage), EIP-1153 (transient)
- Solidity ^0.8.24 docs — checked-arithmetic, `unchecked` block
- OpenZeppelin Contracts v5.x — Initializable, ReentrancyGuard,
AccessControl, ERC4626 default-virtual-shares mitigation
- Off-chain Ethereum hashing — Keccak-256 (original) vs NIST FIPS-202
SHA3-256 divergence; use ethers / viem / web3 Keccak helpers and a
parity test against on-chain `keccak256`
- Tooling: Trail of Bits Slither + Echidna; ConsenSys Mythril;
Foundry / Forge; a16z Halmos; Certora Prover
- Real-exploit corpus: rekt.news, DeFiHackLabs, immunefi.com/exploits
- Inspiration: `msitarzewski/agency-agents` specialized/blockchain-
security-auditor @ `783f6a72bfd7f3135700ac273c619d92821b419a` (MIT)
- Inspiration: off-chain Keccak-256 vs NIST SHA3-256 hashing-parity
## Changelog
- **PLAN-153 Wave G (SP-031, 2026-07-09):** wrong-primitive keccak/sha3 doctrine + runnable parity proof folded in (clean-room ADAPT; provenance in frontmatter/NOTICE).
Skill-Import-Attestation: reviewed-by=AE9B236FDAF0462874060C6BCFCFACF00335DC74; sha256=0f5a91f905447e711b371f13909e51864eb2e8414df2ccc6d226aff4fffe7c7f
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!