{"slug":"web3-audit","title":"web3-audit","summary":"Smart contract security audit — 10 DeFi bug classes (accounting desync, access control, incomplete path, off-by-one, oracle, ERC4626, reentrancy, flash loan, signature replay, proxy), pre-dive kill signals (TVL < $500K etc), Foundry PoC template, grep patterns for each class, and","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-08-24T05:37:54.524812Z","repo":{"url":"https://github.com/elementalsouls/Claude-BugHunter","stars":4626,"forks":696,"license":"MIT","updatedAt":"2026-09-23T09:21:09Z"},"bodyHtml":"<hr>\n<h2>name: web3-audit\ndescription: Smart contract security audit — 10 DeFi bug classes (accounting desync, access control, incomplete path, off-by-one, oracle, ERC4626, reentrancy, flash loan, signature replay, proxy), pre-dive kill signals (TVL &lt; $500K etc), Foundry PoC template, grep patterns for each class, and real Immunefi paid examples. Use for any Solidity/Rust contract audit or when deciding whether a DeFi target is worth hunting.</h2>\n<h1>WEB3 SMART CONTRACT AUDIT</h1>\n<p>10 bug classes. Pre-dive kill signals. Foundry PoC template. Real paid examples.</p>\n<hr>\n<h2>PRE-DIVE KILL SIGNALS (check BEFORE any code review)</h2>\n<blockquote>\n<p>ZKsync lesson: $322M TVL + OZ audit + 750K LOC + 5 sessions = 0 findings. Large well-audited bridges are extremely hard.</p>\n</blockquote>\n<ol>\n<li><strong>TVL &lt; $500K</strong> → max payout capped too low for effort</li>\n<li><strong>2+ top-tier audits</strong> (Halborn, ToB, Cyfrin, OpenZeppelin) on simple protocol → bugs already found</li>\n<li><strong>Protocol &lt; 500 lines, single A→B→C flow</strong> → minimal attack surface</li>\n<li><strong>Formula</strong>: <code>max_realistic_payout = min(10% × TVL, program_cap)</code> — if &lt; $10K, skip</li>\n</ol>\n<p><strong>Soft kill:</strong> OZ/ToB/Cyfrin audit on current version + codebase &gt; 500K LOC → expect 40+ hours for maybe 1 finding. Only proceed if bounty floor &gt; $50K AND you have protocol-specific expertise.</p>\n<p><strong>Target scoring (go if &gt;= 6/10):</strong></p>\n<ul>\n<li>TVL &gt; $10M: +2</li>\n<li>Immunefi program with Critical &gt;= $50K: +2</li>\n<li>No top-tier audit on current version: +2</li>\n<li>&lt; 30 days since deploy: +1</li>\n<li>Protocol you've hunted before: +1</li>\n<li>Source code + natspec comments: +1</li>\n<li>Upgradeable proxies: +1</li>\n</ul>\n<hr>\n<h2>THE ONE RULE</h2>\n<blockquote>\n<p>\"Read ALL sibling functions. If <code>vote()</code> has a modifier, check <code>poke()</code>, <code>reset()</code>, <code>harvest()</code>. The missing modifier on the sibling IS the bug.\"</p>\n</blockquote>\n<p>This single rule explains 19% of all Critical findings.</p>\n<hr>\n<h2>1. ACCOUNTING STATE DESYNCHRONIZATION</h2>\n<blockquote>\n<p>#1 Critical bug class — 28% of all Criticals on Immunefi.</p>\n</blockquote>\n<h3>What It Is</h3>\n<p>Two state variables supposed to stay in sync. One code path updates A but forgets B. Later code reads both and makes decisions based on stale B.</p>\n<pre><code>Real Value = A - B\nIf A updated but B isn't → Real Value appears larger → phantom value\n</code></pre>\n<h3>Root Cause Patterns</h3>\n<p><strong>Variant 1: Phantom Yield</strong> (Yeet protocol — 35 duplicate reports)</p>\n<pre><code>function startUnstake(uint256 amount) external {\n    totalSupply -= amount;  // decremented BEFORE transfer\n    // aToken.balanceOf(this) still reflects old value\n    // yieldAmount = aToken.balanceOf - totalSupply = phantom yield\n}\n</code></pre>\n<p><strong>Variant 2: Fast Path Skips State Update</strong> (Alchemix V3)</p>\n<pre><code>function claimRedemption(uint256 tokenId) external {\n    if (transmuter.balance &gt;= amount) {\n        transmuter.transfer(user, amount);\n        _burn(tokenId);\n        return;  // EARLY RETURN — cumulativeEarmarked, _redemptionWeight, totalDebt never updated\n    }\n    // Slow path: updates all state vars correctly\n    alchemist.redeem(...);\n}\n</code></pre>\n<p><strong>Variant 3: Update Happens in Wrong Order</strong> (Alchemix)</p>\n<pre><code>function deposit(uint256 amount) external {\n    _shares = (amount * totalShares) / totalAssets;  // calculated BEFORE deposit\n    totalAssets += amount;   // assets added AFTER shares calculated → wrong rate\n}\n</code></pre>\n<h3>Grep Patterns</h3>\n<pre><code># Find all accounting variables\ngrep -rn \"totalSupply\\|totalShares\\|totalAssets\\|totalDebt\\|cumulativeReward\\|rewardPerShare\" contracts/\n\n# Find all early returns in claim/redeem functions\ngrep -rn \"\\breturn\\b\" contracts/ -B3 | grep -B3 \"if\\b\"\n\n# For each early return: which state updates in normal path are skipped?\n</code></pre>\n<hr>\n<h2>2. ACCESS CONTROL</h2>\n<blockquote>\n<p>#2 Critical — 19% of Criticals. $953M lost in 2024 alone.</p>\n</blockquote>\n<h3>Variant 1: Missing Modifier on Sibling Function</h3>\n<pre><code>function vote(uint256 tokenId) external onlyNewEpoch(tokenId) {  // guarded\nfunction reset(uint256 tokenId) external onlyNewEpoch(tokenId) { // guarded\nfunction poke(uint256 tokenId) external {                         // NO GUARD → infinite FLUX inflation\n}\n</code></pre>\n<h3>Variant 2: Wrong Check (Existence vs Ownership)</h3>\n<pre><code>function split(uint256 tokenId, uint256 amount) external {\n    _requireOwned(tokenId);  // checks if token EXISTS, not if caller OWNS it\n    _burn(tokenId);\n    _mint(msg.sender, amount);  // attacker steals tokens they don't own\n}\n</code></pre>\n<h3>Variant 3: Silent Modifier (if vs require)</h3>\n<pre><code>// VULNERABLE — non-admin silently gets through:\nmodifier onlyAdmin() {\n    if (msg.sender == admin) {\n        _;  // body only executes for admin, but non-admin doesn't revert\n    }\n}\n// CORRECT: require(msg.sender == admin, \"Not admin\"); _;\n</code></pre>\n<h3>Variant 4: Uninitialized Proxy</h3>\n<pre><code>function initialize(address _owner) public {  // MISSING: initializer modifier\n    owner = _owner;  // anyone can call → become owner\n}\n// Fix: constructor() { _disableInitializers(); }\n</code></pre>\n<h3>Grep Patterns</h3>\n<pre><code># Find sibling function families — do ALL have the same modifier set?\ngrep -rn \"function vote\\|function poke\\|function reset\\|function update\\|function claim\\|function harvest\" contracts/ -A2\n\n# Ownership check: existence vs ownership?\ngrep -rn \"_requireOwned\\|ownerOf\\|_isApprovedOrOwner\\|_checkAuthorized\" contracts/ -B5\n\n# Silent modifiers\ngrep -rn \"modifier\\b\" contracts/ -A8 | grep -B3 \"if (\" | grep -v \"require\\|revert\"\n\n# Uninitialized initializer\ngrep -rn \"function initialize\\b\" contracts/ -A3\ngrep -rn \"_disableInitializers()\" contracts/\n</code></pre>\n<h3>Real Paid Examples</h3>\n<table>\n<thead>\n<tr>\n<th>Protocol</th>\n<th>Payout</th>\n<th>Bug</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Wormhole</td>\n<td>$10M</td>\n<td>Uninitialized UUPS proxy → anyone calls initialize()</td>\n</tr>\n<tr>\n<td>ZeroLend</td>\n<td>n/a</td>\n<td>split() uses existence check, not ownership check</td>\n</tr>\n<tr>\n<td>Alchemix</td>\n<td>n/a</td>\n<td>poke() missing onlyNewEpoch → infinite FLUX inflation</td>\n</tr>\n<tr>\n<td>Parity</td>\n<td>$150M frozen</td>\n<td>No access control on initWallet() in library</td>\n</tr>\n</tbody>\n</table>\n<hr>\n<h2>3. INCOMPLETE CODE PATH</h2>\n<blockquote>\n<p>#3 Critical — 17% of Criticals.</p>\n</blockquote>\n<h3>The Function Family Comparison Test</h3>\n<pre><code>1. List all state changes in function A (deposit/place/create)\n2. List all state changes in function B (withdraw/update/cancel)\n3. For each state change in A: does B have the corresponding reverse?\n4. For each token transfer in A: does B have the corresponding refund?\nIf A does X but B doesn't do the reverse of X → BUG.\n</code></pre>\n<h3>Variant 1: Update Function Missing Refund (ThunderNFT)</h3>\n<pre><code>function place_order(OrderInput calldata order) external {\n    token.safeTransferFrom(msg.sender, address(this), order.price);  // takes tokens\n    orders[orderId] = order;\n}\nfunction update_order(OrderInput calldata updatedOrder) external {\n    // BUG: NO REFUND for sell orders when price decreases → tokens permanently stuck\n    orders[orderId] = updatedOrder;\n}\n</code></pre>\n<h3>Variant 2: Partial Fill Token Stuck (Plume)</h3>\n<pre><code>function swapForETH(uint256 amountIn) external {\n    token.safeTransferFrom(msg.sender, address(this), amountIn);\n    uint256 filled = dex.swap(amountIn);  // partial fill possible\n    _refundExcessEth(amountIn - filled);  // BUG: refunds ETH only, not ERC20\n}\n</code></pre>\n<h3>Variant 3: mint() Bypasses Check That deposit() Has (MetaPool)</h3>\n<pre><code>function deposit(uint256 assets, address receiver) public override {\n    shares = _deposit(assets, receiver);  // includes receipt validation\n}\nfunction mint(uint256 shares, address receiver) public override {\n    assets = convertToAssets(shares);\n    _mint(receiver, shares);  // MISSING: _deposit() validation → mints without receiving assets\n}\n</code></pre>\n<h3>Grep Patterns</h3>\n<pre><code>grep -rn \"function place_\\|function create_\\|function add_\\|function open_\" contracts/ -A5\ngrep -rn \"function update_\\|function modify_\\|function cancel_\" contracts/ -A5\ngrep -rn \"safeApprove\\b\" contracts/    # safeApprove without zero-reset before\ngrep -rn \"delete\\b\" contracts/ -B5 -A5  # delete before operation completes\ngrep -rn \"function deposit\\|function mint\\|function withdraw\\|function redeem\" contracts/ -A10\n</code></pre>\n<hr>\n<h2>4. OFF-BY-ONE &amp; BOUNDARY CONDITIONS</h2>\n<blockquote>\n<p>#4 High — 22% of Highs. Single character change. Massive impact.</p>\n</blockquote>\n<h3>Root Cause</h3>\n<pre><code>// VeChain Stargate — post-exit reward drain:\nfunction _claimableDelegationPeriods(address delegator) internal view returns (uint256) {\n    if (endPeriod &gt; nextClaimablePeriod) {  // BUG: should be &gt;=\n        return 0;  // exited users get nothing\n    }\n    return nextClaimablePeriod - lastClaimedPeriod;  // rewards for period AFTER exit\n}\n</code></pre>\n<h3>Mental Test for Every Comparison</h3>\n<blockquote>\n<p>For every <code>if (A &gt; B)</code>: \"What happens when A == B?\" Is that correct?</p>\n</blockquote>\n<h3>6 Boundary Locations to Check</h3>\n<ol>\n<li>Period/Epoch boundaries: <code>&gt;</code> vs <code>&gt;=</code> at period end</li>\n<li>Time-based locks: does <code>block.timestamp == deadline</code> lock or unlock?</li>\n<li>Loop break conditions: <code>break</code> with <code>&gt;</code> vs <code>&gt;=</code></li>\n<li>Array index boundaries: <code>i &lt;= array.length</code> (should be <code>i &lt; array.length</code>)</li>\n<li>Amount/balance boundaries: <code>&gt;= amount</code> allows exact full withdrawal?</li>\n<li>Rounding/precision: can any input produce 0 output that should be non-zero?</li>\n</ol>\n<h3>Grep Patterns</h3>\n<pre><code># Boundaries in comparisons\ngrep -rn \"Period\\|Epoch\\|Round\\|Deadline\\|period\\|epoch\\|deadline\" contracts/ -A3 | grep \"[&lt;&gt;][^=]\"\n\n# Loop breaks\ngrep -rn \"\\bbreak\\b\" contracts/ -B10\n\n# Off-by-one in array access\ngrep -rn \"\\.length\\s*-\\s*1\\|i\\s*&lt;=\\s*.*\\.length\\b\" contracts/\n</code></pre>\n<hr>\n<h2>5. ORACLE / PRICE MANIPULATION</h2>\n<blockquote>\n<p>12% of all reports. Largest individual payouts. $117M Mango, $70M Curve.</p>\n</blockquote>\n<h3>Bug A: Missing Staleness Check (most common)</h3>\n<pre><code>// VULNERABLE:\n(, int256 price,,,) = priceFeed.latestRoundData();\nreturn uint256(price);  // If Chainlink node goes down, stale price returned indefinitely\n\n// CORRECT:\n(, int256 price,, uint256 updatedAt,) = priceFeed.latestRoundData();\nrequire(block.timestamp - updatedAt &lt;= MAX_PRICE_AGE, \"Stale price\");\nrequire(price &gt; 0, \"Invalid price\");\n</code></pre>\n<h3>Bug B: Missing Confidence Interval (Pyth)</h3>\n<pre><code>// VULNERABLE:\nPythStructs.Price memory p = pyth.getPriceUnsafe(priceFeed);\nreturn p.price;  // ignores p.conf (confidence interval)\n\n// CORRECT:\nrequire(p.conf * 10 &lt;= uint64(p.price), \"Price too uncertain\");\n// conf &gt; 10% of price = untrustworthy\n</code></pre>\n<h3>Bug C: TWAP Too Short (flash loan manipulatable)</h3>\n<pre><code>// VULNERABLE: 60-second TWAP\nuint32[] memory secondsAgos = new uint32[](2);\nsecondsAgos[0] = 60; secondsAgos[1] = 0;\n// Flash loan can shift price for entire 60s window\n\n// CORRECT: 1800s minimum TWAP (30 min)\n</code></pre>\n<h3>Bug D: Single-Source Oracle</h3>\n<pre><code>// VULNERABLE: only Uniswap spot price\nuint price = getUniswapSpotPrice(token);  // flash loan manipulatable\n\n// CORRECT: Chainlink primary, Uniswap TWAP as fallback, require close agreement\n</code></pre>\n<h3>Grep Patterns</h3>\n<pre><code># Missing staleness check\ngrep -rn \"latestRoundData\" contracts/ -A5 | grep -v \"updatedAt\\|timestamp\"\n\n# Pyth price usage — confidence interval checked?\ngrep -rn \"getPriceUnsafe\\|getPrice\\b\" contracts/ -A8 | grep -v \"conf\\|confidence\"\n\n# TWAP windows — short TWAP flag\ngrep -rn \"secondsAgo\\|TWAP\\|cardinality\" contracts/ -A5\n</code></pre>\n<hr>\n<h2>6. ERC4626 VAULT ATTACKS</h2>\n<h3>Exchange Rate Manipulation (near-empty vault)</h3>\n<pre><code>// VULNERABLE — first depositor attack:\n// 1. Attacker deposits 1 wei → gets 1 share\n// 2. Attacker donates large amount directly (transfer, not deposit)\n// 3. Exchange rate: 1 share = (1 + donation) assets\n// 4. Victim deposits → rounds down to 0 shares → free donation to attacker\n\n// CORRECT: virtual shares (OpenZeppelin v4.9+)\nfunction _decimalsOffset() internal view virtual override returns (uint8) {\n    return 9;  // add 1e9 virtual shares + assets to prevent manipulation\n}\n</code></pre>\n<h3>ERC4626 Transfer (moves shares but not stake/lock records)</h3>\n<pre><code>// VULNERABLE: shares transferred, but lock records stay with original owner\n// → shares stuck, can't redeem → permanent freeze (Belong pattern)\nfunction transfer(address to, uint256 amount) external override {\n    _transfer(msg.sender, to, amount);  // moves shares\n    // MISSING: transfer lock record from msg.sender to `to`\n}\n</code></pre>\n<h3>Grep Patterns</h3>\n<pre><code>grep -rn \"function transfer\\|function transferFrom\" contracts/ -A15\ngrep -rn \"function deposit\\|function mint\\|function withdraw\\|function redeem\" contracts/ -A10\n</code></pre>\n<hr>\n<h2>7. REENTRANCY</h2>\n<blockquote>\n<p>2016–present. CEI pattern prevents it. Still found in DeFi.</p>\n</blockquote>\n<h3>Variants</h3>\n<ul>\n<li><strong>Single-function</strong>: attacker re-enters same function before state updated</li>\n<li><strong>Cross-function</strong>: re-enters a sibling function with stale state</li>\n<li><strong>Cross-contract</strong>: re-enters via a callback to another protocol</li>\n<li><strong>Read-only</strong>: re-enters a view function that returns stale data used by attacker</li>\n</ul>\n<h3>Root Cause Pattern</h3>\n<pre><code>// VULNERABLE (effects after interaction):\nfunction withdraw(uint256 amount) external {\n    require(balances[msg.sender] &gt;= amount);\n    (bool success,) = msg.sender.call{value: amount}(\"\");  // INTERACTION first\n    require(success);\n    balances[msg.sender] -= amount;  // EFFECT after → reentrancy window\n}\n\n// CORRECT (CEI — Checks, Effects, Interactions):\nfunction withdraw(uint256 amount) external {\n    require(balances[msg.sender] &gt;= amount);  // CHECK\n    balances[msg.sender] -= amount;            // EFFECT\n    (bool success,) = msg.sender.call{value: amount}(\"\");  // INTERACTION last\n    require(success);\n}\n</code></pre>\n<h3>Grep Patterns</h3>\n<pre><code># External calls before state updates\ngrep -rn \"\\.call{value\\|safeTransfer\\|transfer(\" contracts/ -B10 | grep -v \"require\\|revert\"\n\n# Missing nonReentrant modifier on critical functions\ngrep -rn \"function withdraw\\|function redeem\\|function claim\" contracts/ -A2 | grep -v \"nonReentrant\"\n\n# Storage slot for reentrancy guard\ngrep -rn \"nonReentrant\\|ReentrancyGuard\\|_notEntered\" contracts/\n</code></pre>\n<hr>\n<h2>8. FLASH LOAN ATTACKS</h2>\n<h3>Oracle Manipulation via Flash Loan</h3>\n<pre><code>// Attack flow:\n// 1. Borrow $100M from Aave flash loan\n// 2. Dump token in Uniswap pool → crash spot price\n// 3. Protocol reads Uniswap spot → undercollateralized loans accepted\n// 4. Borrow max against cheap collateral\n// 5. Repay flash loan, keep profits\n</code></pre>\n<h3>Price Oracle Sanity Checks (what to look for)</h3>\n<pre><code>grep -rn \"getReserves\\|getAmountsOut\\|slot0\\b\" contracts/ -A5\n# spot price from reserves = manipulatable with flash loan\n# slot0 = Uniswap V3 spot price = manipulatable\n</code></pre>\n<hr>\n<h2>9. SIGNATURE REPLAY</h2>\n<h3>Missing Nonce</h3>\n<pre><code>// VULNERABLE:\nfunction permit(address owner, address spender, uint256 value,\n                uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {\n    bytes32 hash = keccak256(abi.encodePacked(owner, spender, value, deadline));\n    // MISSING: nonce not included → same signature usable multiple times\n    require(ecrecover(hash, v, r, s) == owner);\n}\n</code></pre>\n<h3>Missing Chain ID</h3>\n<pre><code>// VULNERABLE: signature valid on mainnet AND testnet AND all forks\nbytes32 hash = keccak256(abi.encodePacked(params));\n// MISSING: block.chainid not in hash → works on any chain\n</code></pre>\n<h3>Grep Patterns</h3>\n<pre><code>grep -rn \"ecrecover\\|ECDSA\\.recover\" contracts/ -B20\n# Check: does the signed hash include nonce + chainId + contract address?\n\ngrep -rn \"nonce\\|_nonces\\|nonces\\[\" contracts/\n</code></pre>\n<hr>\n<h2>10. PROXY / UPGRADE ISSUES</h2>\n<h3>Storage Collision</h3>\n<pre><code>// Implementation and proxy share storage layout\n// Proxy slot 0: _owner\n// Implementation slot 0: _initialized\n// → writing to _initialized overwrites _owner\n</code></pre>\n<h3>Uninitialized Implementation</h3>\n<pre><code>// If implementation can be initialized directly → anyone becomes owner of implementation\n// Attack: call initialize() on implementation contract → call upgradeTo() → replace logic\n</code></pre>\n<h3>delegatecall to User-Controlled Address</h3>\n<pre><code>function execute(address target, bytes calldata data) external onlyOwner {\n    target.delegatecall(data);  // target is validated, but what if owner is compromised?\n}\n</code></pre>\n<h3>Grep Patterns</h3>\n<pre><code># UUPS initialization protection\ngrep -rn \"function initialize\\b\\|_disableInitializers\\|initializer\" contracts/\n\n# Delegate call\ngrep -rn \"delegatecall\\b\" contracts/ -B3 -A5\n\n# Storage layout — proxy uses EIP-1967 slots?\ngrep -rn \"0x360894\\|EIP1967\\|_IMPLEMENTATION_SLOT\" contracts/\n</code></pre>\n<hr>\n<h2>FOUNDRY POC TEMPLATE</h2>\n<pre><code>// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport \"../src/VulnerableContract.sol\";\n\ncontract ExploitTest is Test {\n    VulnerableContract target;\n    address attacker = makeAddr(\"attacker\");\n    address victim = makeAddr(\"victim\");\n\n    function setUp() public {\n        // Fork mainnet at specific block\n        vm.createSelectFork(\"mainnet\", BLOCK_NUMBER);\n\n        // Deploy or load target\n        target = VulnerableContract(TARGET_ADDRESS);\n\n        // Fund accounts\n        deal(address(token), attacker, INITIAL_BALANCE);\n        deal(address(token), victim, VICTIM_BALANCE);\n    }\n\n    function test_exploit() public {\n        console.log(\"Attacker balance before:\", token.balanceOf(attacker));\n\n        vm.startPrank(attacker);\n\n        // Step 1: Setup conditions\n        // Step 2: Execute exploit\n        // Step 3: Verify impact\n\n        vm.stopPrank();\n\n        console.log(\"Attacker balance after:\", token.balanceOf(attacker));\n        assertGt(token.balanceOf(attacker), INITIAL_BALANCE, \"Exploit failed\");\n    }\n}\n</code></pre>\n<h3>Key Foundry Cheatcodes</h3>\n<pre><code>vm.prank(address)           // next call from address\nvm.startPrank(address)      // all calls from address until stopPrank()\nvm.deal(address, amount)    // set ETH balance\ndeal(token, address, amount) // set ERC20 balance\nvm.warp(timestamp)          // set block.timestamp\nvm.roll(blockNumber)        // set block.number\nvm.createSelectFork(\"mainnet\", blockNumber)  // fork mainnet\nvm.expectRevert(bytes)      // next call should revert\nvm.label(address, \"name\")   // label for trace output\nvm.assume(condition)        // fuzz: discard inputs where false\n</code></pre>\n<h3>Running Tests</h3>\n<pre><code># Run specific test\nforge test --match-test test_exploit -vvvv\n\n# Run with fork\nforge test --match-test test_exploit -vvvv --fork-url $MAINNET_RPC\n\n# Gas report\nforge test --gas-report\n\n# Coverage\nforge coverage --report summary\n</code></pre>\n<hr>\n<h2>Related Skills &amp; Chains</h2>\n<ul>\n<li><strong><code>meme-coin-audit</code></strong> — When the target is a meme coin / SPL token rather than a DeFi protocol. Workflow primitive: pre-dive kill signals diverge — this skill's \"TVL &lt; $500K skip\" doesn't apply to meme coins where the rug check (mint authority, freeze authority, LP lock) is the entire audit; route to <code>meme-coin-audit</code> instead.</li>\n<li><strong><code>triage-validation</code></strong> — When a contract finding is ready to be filed on Immunefi. Workflow primitive: Immunefi has its own report format, but the impact-validated, chain-end-to-end discipline of <code>triage-validation</code> still applies; run the 7Q gate against the Foundry PoC before submitting.</li>\n<li><strong><code>report-writing</code></strong> — When writing the Immunefi report body. Workflow primitive: <code>report-writing</code>'s Immunefi template (with Foundry PoC, root cause code snippet, quantified economic impact) is the body skeleton this skill's findings feed into.</li>\n<li><strong><code>offensive-osint</code></strong> — When auditing a protocol's off-chain attack surface (frontend, admin API, RPC gateways). Workflow primitive: on-chain audit is this skill's job; any web2 component of the protocol (web-frontend, admin panel, indexer API) routes to <code>offensive-osint</code> for recon.</li>\n<li><strong><code>bb-methodology</code></strong> — When deciding whether to dive at all. Workflow primitive: PART 0 of <code>bb-methodology</code> confirms engagement (web3 bug bounty / private audit / smart-contract review); this skill's pre-dive kill signals replace the standard scoring rubric for that engagement type.</li>\n</ul>\n<hr>\n<h2>Operator Notes (Claude-BugHunter)</h2>\n<blockquote>\n<p>Engagement-derived + 2026-specific additions to the vendored foundation.\nWisdom from real authorized engagements + Phase 2 verification across\nthis repo's 31+ skill-area live tests. The upstream content covers the WHAT;\nthis layer covers the WHEN-IT-WORKS-vs-WHEN-IT-DOESN'T.</p>\n</blockquote>\n<h3>Bug classes still paying in 2026</h3>\n<p>Flash-loan attacks remain top-paid on Immunefi (top 5 in 2024-2026 by bounty). The economic primitive — borrow $50M, manipulate price oracle, drain pool, repay — keeps reappearing because new protocols keep shipping with composability assumptions that don't hold under flash-loaned imbalance.</p>\n<p>Reentrancy IS still paying because new protocols keep shipping with ERC-777 / hooks / callbacks. Don't assume the class is dead — the 2023-2025 paid corpus contains 40+ reentrancy bugs against post-Checks-Effects-Interactions codebases (cross-function reentrancy, read-only reentrancy via view functions called during state-mid-flight).</p>\n<p>Oracle manipulation: still paid heavily but harder. Most projects use Chainlink price feeds now; the attack target is the SECONDARY oracle most projects also use (TWAP from a low-liquidity Uniswap V2 pair, the protocol's own internal oracle, a stale fallback path). Audit the failover chain, not just the primary feed.</p>\n<h3>What's new since the vendored content was written</h3>\n<ul>\n<li><strong>EIP-1153 (transient storage)</strong> — introduced in 2024. New reentrancy classes: transient-storage reads cached across the same transaction can desync from persistent storage. Audit any <code>tload</code>/<code>tstore</code> usage for read-after-external-call.</li>\n<li><strong>EIP-7702 (Pectra hard fork 2025)</strong> — added EOA-to-smart-account upgrades. New ATO-like primitives via re-delegation: an EOA signed-once can delegate to a contract that the attacker controls, then signature replay across delegations.</li>\n<li><strong>Account abstraction (ERC-4337 bundlers)</strong> — paymaster sponsorship abuse and bundler griefing. Paymaster contracts that don't enforce strict sender allowlists drain on first call.</li>\n<li><strong>ZK-rollup bridge bugs</strong> — proof-replay across rollups, off-chain prover compromise, sequencer censorship leading to forced-inclusion edge cases.</li>\n<li><strong>LST/LRT depeg dynamics</strong> — liquid-staking and liquid-restaking tokens that assume 1:1 peg under loss conditions; oracle assumes peg, market reflects depeg, liquidation logic breaks.</li>\n</ul>\n<h3>Tool stack for 2026</h3>\n<p>Foundry remains the test framework. <code>forge test --gas-report --debug</code> for invariant testing; <code>forge fuzz</code> for property-based testing; <code>forge inspect</code> for storage-layout audits. Slither + Echidna for static + fuzz. Mythril for symbolic execution on smaller contracts. tenderly.co for forking + simulation (best UX for replicating attacks against mainnet state).</p>\n<p>For Solana: anchor framework, sealevel-attacks corpus (curated PoCs by anchor maintainers), soteria-sec / sec3 scanner. For Move (Aptos, Sui): move-prover, aptos-cli <code>aptos move test</code>.</p>\n<p>For cross-chain: hyperlane and LayerZero each have audit-tooling repos; bridge bugs require simulating both endpoints, not just one.</p>\n<h3>Where pre-dive kill signals matter</h3>\n<p>TVL under $500K isn't worth the audit time unless the bounty floor is high. Audit firm already covered it = low ROI unless you find what they missed — look at the audit-report scope-exclusion section for what they EXPLICITLY didn't audit (oracles, governance, off-chain components, frontend, the admin path).</p>\n<p>Multisig signers &gt; 5 + timelock &gt; 48h = low rug-pull risk; if your finding requires team-malicious assumptions, it's low-impact and likely out of scope per Immunefi's \"centralization risk\" exclusion. Read the program brief — most Immunefi programs explicitly downgrade or reject findings that assume admin malice.</p>\n<h3>Reporting discipline</h3>\n<p>Immunefi requires Foundry PoC. Submission without PoC is auto-rejected. Submission with a PoC that requires manual setup (\"first deploy this, then call that\") usually gets downgraded — the PoC should be a single <code>forge test</code> invocation that proves the impact, with explicit <code>assertEq</code> on the drained balance / minted token / corrupted state.</p>\n<p>Severity claims must use Immunefi's severity matrix exactly; don't invent severities. The matrix gates on direct economic loss percentage of TVL — a critical against a $500K protocol pays differently than a critical against a $500M one. Read the program's specific severity assignment before claiming Critical.</p>\n","files":[{"path":"SKILL.md","sizeBytes":24000,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-08-25T17:14:53.420714Z","sha256":"E1F746F65E51296B1C0674731D7286CCEEE9D722D3528C54CEE13E5605DD6FFB","sizeBytes":9897},"review":null,"source":{"repositoryUrl":"https://github.com/elementalsouls/Claude-BugHunter","path":"skills/web3-audit","license":"MIT","commit":"4d7b4cdfddb7ec67fba87821e54c768248a544bd","subtreeSha":"D4A0941FB14C4E011B53FB853A7D205F17D6D2799140A2E19A38CEA24D3874A4","lastSyncedAt":"2026-09-24T06:49:51.293025Z"},"reviewedAt":"2026-08-25T17:20:28.56552Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/elementalsouls/Claude-BugHunter/tree/main/skills/web3-audit"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install elementalsouls-claude-bughunter@llmmart"},{"target":"git","command":"git clone https://github.com/elementalsouls/Claude-BugHunter.git"}]}