Puzzle #1
Usurper's Throne
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
6
contract Throne is IPuzzle {
7
DAO public dao;
8
9
mapping(address => bool) public usurpers;
10
mapping(uint256 => bool) public thrones;
11
12
constructor() {
13
address[] memory allowedTargets = new address[](1);
14
allowedTargets[0] = address(this);
15
16
bytes4[] memory allowedMethods = new bytes4[](1);
17
allowedMethods[0] = Throne.forgeThrone.selector;
18
// no usurpers allowed
19
20
dao = new DAO(allowedTargets, allowedMethods);
21
}
22
23
function name() external pure returns (string memory) {
24
return "Usurper's Throne";
25
}
26
27
modifier onlyDAO() {
28
require(msg.sender == address(dao), "only the DAO can add winners");
29
_;
30
}
31
32
function addUsurper(address who) external onlyDAO {
33
usurpers[who] = true;
34
}
35
36
function forgeThrone(uint256 throne) external onlyDAO {
37
thrones[throne] = true;
38
}
39
40
function generate(address seed) external view returns (uint256) {
41
return uint256(uint160(seed));
42
}
43
44
function verify(uint256 seed, uint256 solution) external view returns (bool) {
45
address solver = address(uint160(seed));
46
require(solution == uint256(keccak256(abi.encode(solver))));
47
return usurpers[solver] && thrones[solution];
48
}
49
}
50
51
contract DAO {
52
// gotta go fast
53
uint256 constant REQUIRED_VOTES = 3;
54
uint256 constant DURATION = 36;
55
56
mapping(address => bool) targets;
57
mapping(bytes4 => bool) methods;
58
59
struct Proposal {
60
address owner;
61
uint64 end;
62
uint32 votes;
63
address target;
64
bool executed;
65
}
66
67
mapping(uint256 => Proposal) proposals;
68
mapping(address => mapping(uint256 => bool)) voted;
69
70
constructor(address[] memory allowedTargets, bytes4[] memory allowedMethods) {
71
for (uint256 i = 0; i < allowedTargets.length; i++) {
72
targets[allowedTargets[i]] = true;
73
}
74
75
for (uint256 i = 0; i < allowedMethods.length; i++) {
76
methods[allowedMethods[i]] = true;
77
}
78
}
79
80
modifier onlyOwner(uint256 id) {
81
require(msg.sender == proposals[id].owner, "only proposal owner allowed");
82
_;
83
}
84
85
function validData(bytes memory data) internal view returns (bool) {
86
return data.length >= 4 && methods[bytes4(data)];
87
}
88
89
function validTarget(address target) internal view returns (bool) {
90
return targets[target];
91
}
92
93
function descriptionKey(uint256 id) internal pure returns (bytes32) {
94
return keccak256(abi.encode(id, id));
95
}
96
97
function getDescription(uint256 id) public view returns (string memory) {
98
return string(CheapMap.read(descriptionKey(id)));
99
}
100
101
function setDescription(uint256 id, string memory desc) internal {
102
CheapMap.write(descriptionKey(id), bytes(desc));
103
}
104
105
function getData(uint256 id) public view returns (bytes memory) {
106
return CheapMap.read(bytes32(id));
107
}
108
109
function setData(uint256 id, bytes memory data) internal {
110
require(validData(data), "payload data not allowed");
111
CheapMap.write(bytes32(id), data);
112
}
113
114
function createProposal(
115
uint256 id,
116
address target,
117
bytes memory data,
118
string memory desc
119
) external {
120
require(proposals[id].owner == address(0), "proposal already exists");
121
require(validTarget(target), "invalid payload target");
122
123
proposals[id] = Proposal(
124
msg.sender,
125
uint64(block.timestamp + DURATION),
126
1,
127
target,
128
false
129
);
130
131
setData(id, data);
132
if (bytes(desc).length > 0) setDescription(id, desc);
133
}
134
135
function vote(uint256 id) external {
136
Proposal storage proposal = proposals[id];
137
require(proposal.owner != address(0), "proposal does not exist");
138
require(block.timestamp <= uint256(proposal.end), "proposal ended");
139
require(!voted[msg.sender][id], "already voted");
140
voted[msg.sender][id] = true;
141
proposal.votes++;
142
}
143
144
function execute(uint256 id) external returns (bytes memory) {
145
require(
146
proposals[id].votes >= REQUIRED_VOTES,
147
"not enough votes"
148
);
149
require(
150
!proposals[id].executed,
151
"already executed"
152
);
153
154
(bool success, bytes memory result) = proposals[id].target.call(
155
getData(id)
156
);
157
proposals[id].executed = success;
158
return result;
159
}
160
}
161
162
library CheapMap {
163
// keccak256(bytes('CheapMap.slot'))
164
bytes32 private constant SLOT_KEY_PREFIX = 0x06fccbac10f612a9037c3e903b4f4bd03ffbc103781cbe821d25b33299e50efb;
165
166
function internalKey(bytes32 _key) internal pure returns (bytes32) {
167
return keccak256(abi.encode(SLOT_KEY_PREFIX, _key));
168
}
169
170
function write(string memory _key, bytes memory _data) internal returns (address pointer) {
171
return write(keccak256(bytes(_key)), _data);
172
}
173
174
function write(bytes32 _key, bytes memory _data) internal returns (address pointer) {
175
bytes memory code = Bytecode.creationCodeFor(_data);
176
pointer = Creator.create(internalKey(_key), code);
177
}
178
179
function read(string memory _key) internal view returns (bytes memory) {
180
return read(keccak256(bytes(_key)));
181
}
182
183
function read(string memory _key, uint256 _start) internal view returns (bytes memory) {
184
return read(keccak256(bytes(_key)), _start);
185
}
186
187
function read(string memory _key, uint256 _start, uint256 _end) internal view returns (bytes memory) {
188
return read(keccak256(bytes(_key)), _start, _end);
189
}
190
191
function read(bytes32 _key) internal view returns (bytes memory) {
192
return Bytecode.codeAt(Creator.addressOf(internalKey(_key)), 0, type(uint256).max);
193
}
194
195
function read(bytes32 _key, uint256 _start) internal view returns (bytes memory) {
196
return Bytecode.codeAt(Creator.addressOf(internalKey(_key)), _start, type(uint256).max);
197
}
198
199
function read(bytes32 _key, uint256 _start, uint256 _end) internal view returns (bytes memory) {
200
return Bytecode.codeAt(Creator.addressOf(internalKey(_key)), _start, _end);
201
}
202
}
203
204
library Bytecode {
205
error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end);
206
207
function creationCodeFor(bytes memory _code) internal pure returns (bytes memory) {
208
return abi.encodePacked(
209
hex"63",
210
uint32(_code.length),
211
hex"80_60_0E_60_00_39_60_00_F3",
212
_code
213
);
214
}
215
216
function codeSize(address _addr) internal view returns (uint256 size) {
217
assembly { size := extcodesize(_addr) }
218
}
219
220
function codeAt(address _addr, uint256 _start, uint256 _end) internal view returns (bytes memory oCode) {
221
uint256 csize = codeSize(_addr);
222
if (csize == 0) return bytes("");
223
224
if (_start > csize) return bytes("");
225
if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end);
226
227
unchecked {
228
uint256 reqSize = _end - _start;
229
uint256 maxSize = csize - _start;
230
231
uint256 size = maxSize < reqSize ? maxSize : reqSize;
232
233
assembly {
234
oCode := mload(0x40)
235
mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f))))
236
mstore(oCode, size)
237
extcodecopy(_addr, add(oCode, 0x20), _start, size)
238
}
239
}
240
}
241
}
242
243
library Creator {
244
error ErrorCreatingProxy();
245
error ErrorCreatingContract();
246
247
bytes internal constant PROXY_CHILD_BYTECODE = hex"68_36_3d_3d_37_36_3d_34_f0_ff_3d_52_60_09_60_17_f3_ff";
248
bytes32 internal constant KECCAK256_PROXY_CHILD_BYTECODE = 0x648b59bcbb41c37892d3b820522dc8b8c275316bb020f043a9068f607abeb810;
249
250
function codeSize(address _addr) internal view returns (uint256 size) {
251
assembly { size := extcodesize(_addr) }
252
}
253
254
function create(bytes32 _salt, bytes memory _creationCode) internal returns (address addr) {
255
return create(_salt, _creationCode, 0);
256
}
257
258
function create(bytes32 _salt, bytes memory _creationCode, uint256 _value) internal returns (address addr) {
259
bytes memory creationCode = PROXY_CHILD_BYTECODE;
260
addr = addressOf(_salt);
261
address proxy; assembly { proxy := create2(0, add(creationCode, 32), mload(creationCode), _salt)}
262
if (proxy == address(0)) revert ErrorCreatingProxy();
263
(bool success,) = proxy.call{ value: _value }(_creationCode);
264
if (!success || codeSize(addr) == 0) revert ErrorCreatingContract();
265
}
266
267
function addressOf(bytes32 _salt) internal view returns (address) {
268
address proxy = address(
269
uint160(
270
uint256(
271
keccak256(
272
abi.encodePacked(
273
hex'ff',
274
address(this),
275
_salt,
276
KECCAK256_PROXY_CHILD_BYTECODE
277
)
278
)
279
)
280
)
281
);
282
283
return address(
284
uint160(
285
uint256(
286
keccak256(
287
abi.encodePacked(
288
hex"d6_94",
289
proxy,
290
hex"01"
291
)
292
)
293
)
294
)
295
);
296
}
297
}
First Blood
orenyomtov.eth
02:37:58
14
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