Puzzle #6
Fraud
Author
0xb49bf876be26435b6fae1ef42c3c82c5867fa149
chainlight.io
SoliditySolidity's logo.Puzzle
Curtacallsverify()
1
// SPDX-License-Identifier: MIT
2
pragma solidity ^0.8.13;
3
4
import {IPuzzle} from "curta/src/interfaces/IPuzzle.sol";
5
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
6
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
7
import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";
8
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
9
10
import {IVerifier} from "./interfaces/IVerifier.sol";
11
12
contract Fraud is IPuzzle {
13
uint256 public constant BOND_AMOUNT = 1 ether;
14
15
bytes public constant encodedProof = hex"000000000000000000000000000000000000000000000000000000000000002024be65f9f4991921dff22d4c80a5a191b6d0605cc0b49bde4d31c5c79fd545471d35ba5a2ce6ccd4c70ba6d722db0673c290eba435b078abb3f36f38eafb131a01007463356f82e390874b004af5f7dc2f1c44ea2b225331dafe5d798ab2dc960de0533d3dc2da5dca293bf329608d19d0ffcc2c1ab84d715ed6b8e7ebea04ca2adf85a89df8f2879e5145237f301e3ba93daef945757b8e3d4cf384f275d855217519ac89c104f96181e51815c3f3a549bd11e6d5178a7bd2ad64f0cb678e632fb03eb8d37aefc71677b068c056346c68f2e376386a543f0a8401568d9fddd426aa5e49ea05994408716b5a14a850cff8bdf6a8491244db256adf49155a91f301df8d0a1b7a364368fe63a050dd317c2d9f217dc0ff42dabcf6234d90bc1d95198ce5f7fa954271722e3dc75b11b06ea63569b3d2338c25315267e4cf23d0112535a9e882db65ecfe0bac2c98f8ea64fb4afac00a0c5b67dfe9f421b5320b9022f0b8d96b282785bdb9032eb32fb2c8753e95df568083fc7a0f30188a1ba93e0f34f5d7b83663b1c8c1c7a2c5bffc33176dcd2b809193b67c387366615a00982d03dc133dcb7478a172d46a17528255035ff501171d73efd582fbfa5de9488718e4f77bfabb6fe0683eb6c564dcf19a576db9c142973aa9c902192be22b2c3025705b1b8a0215139f5bbfec2c9bd95049fc2a8efee83410bac1b7e441c7d5ef1d5bbfb61a6f537cf16924c8356df618f77e6febdceaa9b03cd5594d332224e001f3093011945010c52f700c79854622ffb85e2a1c00d79211d108fcf6cf8184012202838eceaf248a501ac11eeffe566bdded8935eb0a324e5621266cd23950191d1d3fc9ad859249195b4f4a0386f72e97bb5f81108fc077fc3a56bebfda2126098574a670b2813e351fc5dc555bf3e5a86bc86681497c26ca8ae88330f527215e3a8aae39a420354cb60af3aec32628e58fb02449d3c58b314b2afba0bd8a023c2d0b531084d496fbc597209b53eccf2b895ed247e7a97db4fae2eef23b180593e9f09b1197b2df5f34f194701de7818f056dc01245675d5cc06e8e4bb2d2013eb6d40fca6b86cb2dfa3c0f9081381f151083cf73cbe96891cc8b995881dc2638de71f94c2f516c61690630850657f9b33f2c797ecde28dd000261d0283920f5c53a3a3da178efe925573906a8b67425f17e56ba093352167264f9a5f3de21b08077c54db27372dc66305c9fba190057f5d46b7e685b618cc06f607bdc27d2784e7694261ae66b26ce237b8c1770355f7950b96a0ddaf4c8df5ea71b5566e14c0ac827b5559df3e076f374e5e781cccd80ea97dad5f91a0f7b37418b3d953161fcef9cc393b4d7e00af408468b822880556a6f3111b37cdd632e5be6ff85f2ada4baf4cfda13e68c36a3c27804527a9501a9372366f07f08b76870e49b36f2ee7ae89f616bae83e3a725d5e378fd5412c2e29ec3c9e0d55558e4540a1562005e5a40156a079bf37f4e05aa9b2fca16d01b874f1452d70e492a9de2afcc2a600000000000000000000000000000000000000000000000e3f7add0c7157177600000000000000000000000000000000000000000000000ce268cf63f20dfff200000000000000000000000000000000000000000000000b25dd2fc0229f7861000000000000000000000000000000000000000000000000000289365903ed7100000000000000000000000000000000000000000000000ce50b2a97bbd8193000000000000000000000000000000000000000000000000e2969b86b11e3a4b2000000000000000000000000000000000000000000000000dd9f90660b4690310000000000000000000000000000000000000000000000000002f8a3767d42d700000000000000000000000000000000000000000000000c0fd5d58118f1898c000000000000000000000000000000000000000000000009a969e866456bf453000000000000000000000000000000000000000000000002564e3215681963580000000000000000000000000000000000000000000000000002b598b50ce57a000000000000000000000000000000000000000000000001e525b890df956e9e0000000000000000000000000000000000000000000000007f21ddeeb91dcbcc00000000000000000000000000000000000000000000000b43d1f6f5c325e9b50000000000000000000000000000000000000000000000000001efd24e84a8eb0000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000eeac828d58ca4dbd000000000000000000000000000000000000000000000000d95bb92dbc3d16130000000000000000000000000000000000000000000000002630f815651f0565000000000000000000000000000000000000000000000000d0dbab5296f82e9a00000000000000000000000000000000000000000000000043b29d8dfa6a837c000000000000000000000000000000000000000000000000c918d5c834809b29000000000000000000000000000000000000000000000000e94858367e11d5c20000000000000000000000000000000000000000000000001410086ed146248900000000000000000000000000000000000000000000000042cf37d9dfccd3780000000000000000000000000000000000000000000000007575bfc99eb408490000000000000000000000000000000000000000000000003eaa247b9252baa3000000000000000000000000000000000000000000000000c1f9fc445b508c44119138d9fba0b8e62c9cee6c4b8a3ee91d9a1f833a6c7be81db36d5361e9a0e6";
16
17
BOND public bond = new BOND();
18
Claims public claims = new Claims(verifier, bond, BOND_AMOUNT);
19
IVerifier public constant verifier = IVerifier(0x05Dd66444aEF2e5D073D57Acf6A43535e0432bF5);
20
21
constructor() {
22
bond.approve(address(claims), uint256(int256(-1)));
23
}
24
25
function name() external pure returns (string memory) {
26
return "Fraud";
27
}
28
29
function makeNewClaim() external returns (uint256 claimId) {
30
bond.mint(address(this), BOND_AMOUNT);
31
32
IVerifier.Proof memory proof = abi.decode(encodedProof, (IVerifier.Proof));
33
claimId = claims.makeClaim(proof);
34
}
35
36
function generate(address seed) external view returns (uint256) {
37
return uint256(uint160(seed));
38
}
39
40
function verify(uint256 seed, uint256 solution) external view returns (bool) {
41
address solver = address(uint160(seed));
42
return bond.balanceOf(solver) >= BOND_AMOUNT;
43
}
44
}
45
46
contract BOND is ERC20, Ownable {
47
constructor() ERC20("Bond", "BOND") Ownable(msg.sender) { }
48
49
function mint(address who, uint256 amount) external onlyOwner {
50
_mint(who, amount);
51
}
52
}
53
54
contract Claims {
55
using SafeERC20 for IERC20;
56
57
struct Claim {
58
bytes32 proofHash;
59
uint32 timestamp;
60
bool finalized;
61
bool refuted;
62
address recipient;
63
}
64
65
uint256 constant CHALLENGE_WINDOW = 1 days;
66
67
IVerifier public immutable proofVerifier;
68
IERC20 public immutable bondToken;
69
uint256 public immutable bondAmount;
70
71
mapping(address => bool) public validator;
72
73
uint256 public nextClaimId;
74
mapping(uint256 => Claim) public claims;
75
76
constructor(IVerifier verifier, IERC20 token, uint256 amount) {
77
proofVerifier = verifier;
78
bondToken = token;
79
bondAmount = amount;
80
validator[msg.sender] = true;
81
}
82
83
modifier onlyValidator() {
84
require(validator[msg.sender], "only validators can make claims");
85
_;
86
}
87
88
function makeClaim(
89
IVerifier.Proof calldata proof
90
) external onlyValidator returns (uint256 id) {
91
bytes32 proofHash = keccak256(abi.encode(proof));
92
bondToken.safeTransferFrom(msg.sender, address(this), bondAmount);
93
id = nextClaimId++;
94
claims[id] = Claim(
95
proofHash,
96
uint32(block.timestamp),
97
false,
98
false,
99
msg.sender
100
);
101
}
102
103
function finalizeClaim(uint256 id) external {
104
Claim memory claim = claims[id];
105
106
require(claim.proofHash != bytes32(0), "claim does not exist");
107
require(
108
(block.timestamp < uint256(claim.timestamp) + CHALLENGE_WINDOW) ||
109
claim.refuted,
110
"must have passed the challenge window or been successfully challenged"
111
);
112
claims[id].finalized = true;
113
bondToken.safeTransfer(claim.recipient, bondAmount);
114
}
115
116
function disputeClaim(uint256 id, bytes calldata proof) external {
117
Claim memory claim = claims[id];
118
require(
119
keccak256(proof) == claim.proofHash,
120
"incorrect proof preimage provided"
121
);
122
123
IVerifier.Proof memory decodedProof = abi.decode(
124
proof,
125
(IVerifier.Proof)
126
);
127
128
bool valid;
129
try proofVerifier.verify(decodedProof) returns (bool result) {
130
valid = result;
131
} catch { }
132
require(!valid, "proof is actually valid");
133
claims[id].refuted = true;
134
claims[id].recipient = msg.sender;
135
}
136
}
First Blood
jinu.eth
00:23:22
18
Time Left

Solve locally (WIP)

  1. Clone GitHub repo + install deps
git clone https://github.com/waterfall-mkt/curta-puzzles.git && cd curta-puzzles && forge install
  1. Set RPC_URL_MAINNET in .env
.env
RPC_URL_MAINNET=""
  1. Write solution + run script
forge script <PATH_TO_PUZZLE> -f mainnet -vvv
This is still WIP.
Waterfall