Skip to main content

web3-poc-foundry

Complete Foundry PoC writing guide + all cheatcodes + DeFiHackLabs reproduction patterns. Use this when building a proof of concept exploit, setting up a fork test, using Foundry cheatcodes, or reproducing a known DeFi hack for learning.

Jump to install

Source facts

Repository
tradecatlabs/vibe-coding-cn
Last source activity
September 12, 2026 at 13:03
Detected SKILL.md language
English
Stars
16,256
Forks
1,645

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
web3-poc-foundry
description
Complete Foundry PoC writing guide + all cheatcodes + DeFiHackLabs reproduction patterns. Use this when building a proof of concept exploit, setting up a fork test, using Foundry cheatcodes, or reproducing a known DeFi hack for learning.
# PoC WRITING + FOUNDRY COMPLETE REFERENCE Immunefi requires RUNNABLE code. Not pseudocode. Not steps. Running Foundry tests with before/after logs and a passing assert. --- ## QUICK START ```bash # Immunefi official templates (preferred for submissions) forge init my-poc --template immunefi-team/forge-poc-templates --branch default forge init my-poc --template immunefi-team/forge-poc-templates --branch reentrancy forge init my-poc --template immunefi-team/forge-poc-templates --branch flash_loan forge init my-poc --template immunefi-team/forge-poc-templates --branch price_manipulation # Or blank Foundry project forge init my-poc cd my-poc # Setup .env echo "MAINNET_RPC_URL=https://eth.llamarpc.com" > .env echo "BASE_RPC_URL=https://base.llamarpc.com" >> .env echo "ARB_RPC_URL=https://arb1.arbitrum.io/rpc" >> .env # Run exploit source .env forge test --match-test testExploit -vvvv --fork-url $MAINNET_RPC_URL ``` --- ## STANDARD PoC TEMPLATE (Production Quality for Immunefi) ```solidity // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.10; import "forge-std/Test.sol"; import "forge-std/console.sol"; /** * @title [Protocol Name] - [Bug Description] * @notice PoC for Immunefi submission * @dev Demonstrates [impact] by exploiting [root cause] * * Vulnerable contract: [address] ([name]) * Vulnerable function: [functionName] * Immunefi program: [URL] * Severity: [Critical/High/Medium/Low] */ // Minimal interfaces — only what you need interface IVulnProtocol { function deposit(uint256 amount) external; function withdraw(uint256 amount) external; function balanceOf(address) external view returns (uint256); } interface IERC20 { function approve(address, uint256) external returns (bool); function balanceOf(address) external view returns (uint256); function transfer(address, uint256) external returns (bool); function transferFrom(address, address, uint256) external returns (bool); } contract ExploitPoC is Test { // ============================================================ // CONFIGURATION // ============================================================ uint256 constant ATTACK_BLOCK = 18_000_000; // pin block for reproducibility address constant VULN_CONTRACT = 0x...; address constant TOKEN = 0x0000000000000000000000000000000000000000; // example token placeholder IVulnProtocol vuln = IVulnProtocol(VULN_CONTRACT); IERC20 token = IERC20(TOKEN); // ============================================================ // SETUP // ============================================================ function setUp() public { vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), ATTACK_BLOCK); vm.label(VULN_CONTRACT, "VulnerableProtocol"); vm.label(TOKEN, "USDC"); vm.label(address(this), "Attacker"); } // ============================================================ // EXPLOIT // ============================================================ function testExploit() public { uint256 attackerBefore = token.balanceOf(address(this)); uint256 protocolBefore = token.balanceOf(VULN_CONTRACT); console.log("=== INITIAL STATE ==="); console.log("Attacker USDC: ", attackerBefore); console.log("Protocol USDC: ", protocolBefore); console.log("--------------------"); // Step 1: [description] deal(TOKEN, address(this), 1e6); // 1 USDC starting capital // Step 2: [description] token.approve(VULN_CONTRACT, type(uint256).max); vuln.deposit(1e6); // Step 3: [the exploit] // ... exploit logic ... uint256 attackerAfter = token.balanceOf(address(this)); uint256 protocolAfter = token.balanceOf(VULN_CONTRACT); console.log("=== FINAL STATE ==="); console.log("Attacker USDC: ", attackerAfter); console.log("Protocol USDC: ", protocolAfter); console.log("Profit: ", attackerAfter - attackerBefore); console.log("Protocol loss: ", protocolBefore - protocolAfter); assertGt(attackerAfter, attackerBefore, "Exploit failed: no profit"); } } ``` ### What a Passing PoC Output Looks Like ``` Running 1 test for test/Exploit.t.sol:ExploitPoC [PASS] testExploit() (gas: 1234567) Logs: === INITIAL STATE === Attacker USDC: 100000 Protocol USDC: 5000000 -------------------- === FINAL STATE === Attacker USDC: 600000 Protocol USDC: 4500000 Profit: 500000 Protocol loss: 500000 Test result: ok. 1 passed; 0 failed ``` The before/after numbers ARE your proof. Paste this output directly into the Immunefi report. --- ## ESSENTIAL CHEATCODES — FULL REFERENCE ### Identity / Caller Control ```solidity vm.prank(address who); // Next single call is from `who` // vm.prank(owner); target.setAdmin(attacker); vm.startPrank(address who); vm.stopPrank(); // ALL calls between start/stop are from `who` vm.startPrank(address msgSender, address txOrigin); // Set both msg.sender AND tx.origin simultaneously vm.assume(bool condition); // Skip fuzz test case if condition is false ``` ### State Manipulation ```solidity vm.deal(address who, uint256 ethAmount); // Give ETH to any address // vm.deal(attacker, 10 ether); deal(address token, address to, uint256 amount); // Give ERC20 tokens — works with any verified contract // deal(USDC, attacker, 1_000_000e6); — gives 1M USDC without a source vm.store(address target, bytes32 slot, bytes32 value); // Write directly to any storage slot vm.load(address target, bytes32 slot) returns (bytes32); // Read any storage slot directly vm.warp(uint256 timestamp); // Set block.timestamp // vm.warp(block.timestamp + 24 hours); vm.roll(uint256 blockNumber); // Set block.number // vm.roll(block.number + 1000); vm.fee(uint256 basefee); // Set block.basefee vm.chainId(uint256 id); // Set block.chainid (for cross-chain signature tests) ``` ### Fork Control ```solidity vm.createFork(string memory urlOrAlias) returns (uint256 forkId); vm.createFork(string memory urlOrAlias, uint256 blockNumber) returns (uint256 forkId); vm.createSelectFork(string memory urlOrAlias, uint256 blockNumber) returns (uint256 forkId); // Creates AND selects the fork — use this one vm.selectFork(uint256 forkId); // Switch between forks (for cross-chain tests) vm.activeFork() returns (uint256); // Get current fork ID // Cross-chain test pattern: uint256 mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"), 18_000_000); uint256 baseFork = vm.createFork(vm.envString("BASE_RPC_URL"), 5_000_000); vm.selectFork(mainnetFork); // do mainnet action vm.selectFork(baseFork); // do base action ``` ### Snapshot / Revert ```solidity uint256 snapshot = vm.snapshot(); // Save entire EVM state vm.revertTo(uint256 snapshotId); // Restore to saved state // Pattern: test multiple attack paths from same starting state uint256 snap = vm.snapshot(); // test path A vm.revertTo(snap); // test path B ``` ### Mocking ```solidity vm.mockCall(address callee, bytes calldata data, bytes calldata returnData); // Make any call to callee with data return returnData // Example: mock stale Chainlink price (4 hours ago) vm.mockCall( PRICE_FEED, abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector), abi.encode(uint80(1), int256(63000e8), uint256(0), block.timestamp - 4 hours, uint80(1)) ); vm.mockCallRevert(address callee, bytes calldata data, bytes calldata revertData); // Make a call revert vm.clearMockedCalls(); // Remove all mocks ``` ### Signature Helpers ```solidity (uint8 v, bytes32 r, bytes32 s) = vm.sign(uint256 privateKey, bytes32 digest); // Sign a hash with a private key // Usage: bytes32 hash = keccak256(abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, nonce, deadline)) )); (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, hash); vm.addr(uint256 privateKey) returns (address); // Get address from private key // uint256 key = 0xBEEF; address user = vm.addr(key); // Generate named test address: address attacker = makeAddr("attacker"); // deterministic, labeled ``` ### Expect Assertions ```solidity vm.expectRevert(); // Next call MUST revert (any reason) vm.expectRevert(bytes4 errorSelector); // Next call MUST revert with specific custom error selector vm.expectRevert(bytes memory revertData); // Next call MUST revert with specific data vm.expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData); // Assert event is emitted — MUST precede the call vm.expectEmit(true, true, false, true); emit Transfer(from, to, amount); // declare expected event target.transferFrom(from, to, amount); // then the actual call vm.expectCall(address callee, bytes calldata data); // Assert callee is called with data during next call ``` ### Labels (for Readable Traces) ```solidity vm.label(address addr, string memory name); // Makes traces show "USDC" instead of "0xA0b86..." // Always label in setUp(): vm.label(USDC, "USDC"); vm.label(TARGET, "VulnerableVault"); vm.label(attacker, "Attacker"); ``` ### Assert Helpers ```solidity assertEq(a, b, "message"); // a == b assertGt(a, b, "message"); // a > b assertLt(a, b, "message"); // a < b assertGe(a, b, "message"); // a >= b assertLe(a, b, "message"); // a <= b assertTrue(condition, "msg"); // condition is true assertFalse(condition, "msg"); ``` --- ## FORK TESTING PATTERNS ### Standard Mainnet Fork (Pin Block) ```solidity function setUp() public { vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), 18_000_000); vm.label(USDC, "USDC"); vm.label(TARGET, "Target"); } ``` ### Multi-Fork Test (Cross-Chain Signature Replay PoC) ```solidity uint256 mainnetFork; uint256 arbFork; function setUp() public { mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"), 18_000_000);
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub