1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.13;3
4import {IPuzzle} from "curta/src/interfaces/IPuzzle.sol";5
6contract 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 allowed19
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
51contract DAO {52 // gotta go fast53 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 desc119 ) 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 false129 );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
162library 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
204library 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 _code213 );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
243library 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_BYTECODE277 )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}
Time Left
Solve locally (WIP)
- Clone GitHub repo + install deps
git clone https://github.com/waterfall-mkt/curta-puzzles.git && cd curta-puzzles && forge install
- Set
RPC_URL_MAINNET
in.env
.env
RPC_URL_MAINNET=""
- Write solution + run script
forge script <PATH_TO_PUZZLE> -f mainnet -vvv
This is still WIP.