├── .gitignore ├── .gitmodules ├── src ├── CommitHash.sol ├── IOptimizor.sol ├── IChallenge.sol ├── Solution.huff ├── Solution_1673.huff ├── Solution_1683.huff ├── Fixed18.sol └── SqrtChallenge.sol ├── foundry.toml ├── .github └── workflows │ └── test.yml ├── LICENSE ├── README.md ├── script └── MySolution.s.sol ├── test └── MySolution.t.sol └── assets └── sqrt.svg /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Dotenv file 11 | .env 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/foundry-huff"] 5 | path = lib/foundry-huff 6 | url = https://github.com/huff-language/foundry-huff 7 | -------------------------------------------------------------------------------- /src/CommitHash.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | function computeKey(address sender, bytes32 codehash, uint256 salt) pure returns (bytes32) { 5 | return keccak256(abi.encode(sender, codehash, salt)); 6 | } 7 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | ffi = true 6 | remappings = [ 7 | 'forge-std=lib/forge-std/src/', 8 | 'foundry-huff=lib/foundry-huff/src' 9 | ] 10 | 11 | [rpc_endpoints] 12 | flashbots = "https://rpc.flashbots.net/" 13 | 14 | [fuzz] 15 | runs = 4096 16 | 17 | [fmt] 18 | bracket_spacing = true 19 | -------------------------------------------------------------------------------- /src/IOptimizor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /// An interface for Optimizor for challenge operations 5 | interface IOptimizor { 6 | function commit(bytes32 key) external; 7 | 8 | function challenge(uint256 id, address target, address recipient, uint256 salt) external; 9 | } 10 | 11 | IOptimizor constant OPTIMIZOR_MAINNET = IOptimizor(0x66DE7D67CcfDD92b4E5759Ed9dD2d7cE3C9154a9); 12 | -------------------------------------------------------------------------------- /src/IChallenge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | interface IChallenge { 5 | /// Execute a given solution, using the seed to generate inputs. The actual 6 | /// implementation is specific to the challenge. 7 | /// @return The amount of gas consumed by the solution. 8 | function run(address target, uint256 seed) external view returns (uint32); 9 | 10 | /// @return An SVG snippet, which is embedded in the main NFT. 11 | function svg(uint256 tokenId) external view returns (string memory); 12 | 13 | /// @return The name of the challenge. 14 | function name() external view returns (string memory); 15 | 16 | /// @return The description of the challenge. 17 | /// @notice Should not have line breaks. 18 | function description() external view returns (string memory); 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: push 4 | 5 | env: 6 | FOUNDRY_PROFILE: ci 7 | 8 | jobs: 9 | check: 10 | strategy: 11 | fail-fast: true 12 | 13 | name: Foundry project 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | - name: Install Foundry 20 | uses: foundry-rs/foundry-toolchain@v1 21 | with: 22 | version: nightly 23 | - name: Install Huff 24 | uses: huff-language/huff-toolchain@v2 25 | with: 26 | version: nightly 27 | - name: Run Forge build 28 | run: | 29 | forge --version 30 | forge build 31 | id: build 32 | - name: Run Forge tests 33 | run: | 34 | LOCAL=false forge test -vvv 35 | id: test 36 | -------------------------------------------------------------------------------- /src/Solution.huff: -------------------------------------------------------------------------------- 1 | #define macro MAIN() = { 2 | SQRT(0x04) 3 | SQRT(0x24) 4 | SQRT(0x44) 5 | SQRT(0x64) 6 | SQRT(0x84) 7 | 8 | msize returndatasize return 9 | } 10 | 11 | /// https://github.com/Vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol#L443 12 | #define macro SQRT(ptr) = returns (1) { 13 | calldataload // [x] 14 | 15 | 0xa5000000 // [z, x] 16 | dup1 dup3 div // [x / z, z, x] 17 | add // [x / z + z, x] 18 | chainid shr // [z, x] 19 | dup1 dup3 div // [x / z, z, x] 20 | add // [x / z + z, x] 21 | chainid shr // [z, x] 22 | dup1 dup3 div // [x / z, z, x] 23 | add // [x / z + z, x] 24 | 0x1dcd6500 mul // [((x / z + z) >> 1) * 1e9, x] 25 | 26 | msize mstore // [x] 27 | } 28 | -------------------------------------------------------------------------------- /src/Solution_1673.huff: -------------------------------------------------------------------------------- 1 | #define macro MAIN() = takes (0) returns (0) { 2 | 0x3ffff // [mask] 3 | 4 | 0x400000 balance dup1 dup1 // [b, b, b, mask] 5 | 6 | 0x24 shr // [b>>0x24, b, b, mask] 7 | pc shl msize mstore // [b, b, mask] 8 | 9 | pc shr // [b>>0x12(pc), b, mask] 10 | dup3 and // [(b>>0x12) & mask, b, mask] 11 | 0xe shl msize mstore // [b, mask] 12 | 13 | dup2 and // [b & mask, mask] 14 | 0xe shl msize mstore // [mask] 15 | 16 | selfbalance dup1 // [b, b, mask] 17 | 0x10 shr // [b>>0x10, b, mask] 18 | msize mstore // [b, mask] 19 | 20 | and // [b & mask] 21 | 0xe shl msize mstore // [] 22 | 23 | msize 0x04 return 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/Solution_1683.huff: -------------------------------------------------------------------------------- 1 | #define macro MAIN() = takes (0) returns (0) { 2 | chainid chainid 0x12 shl sub // [mask := (1<<0x12)-1] 3 | 4 | 0x37 balance dup1 dup1 // [b, b, b, mask] 5 | 6 | 0x24 shr // [b>>0x24, b, b, mask] 7 | pc shl msize mstore // [b, b, mask] pc: 0xe 8 | 9 | pc shr // [b>>0x12(pc), b, mask] 10 | dup3 and // [(b>>0x12) & mask, b, mask] 11 | 0xe shl msize mstore // [b, mask] 12 | 13 | dup2 and // [b & mask, mask] 14 | 0xe shl msize mstore // [mask] 15 | 16 | selfbalance dup1 // [b, b, mask] 17 | 0x10 shr // [b>>0x10, b, mask] 18 | msize mstore // [b, mask] 19 | 20 | and // [b & mask] 21 | 0xe shl msize mstore // [] 22 | 23 | msize 0x04 return 24 | } 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Optimizor Club 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Optimizor Club SQRT challenge 4 | 5 | This repo contains my solution to the [Optimizor Club SQRT challenge](https://optimizor.club/). 6 | 7 | > In the SQRT problem your contract is given an array of Fixed18 numbers, and it 8 | > must return the square root of each number with an error margin of 10^-5. 9 | > The challenge contract can be found at 10 | > [0x2747096ff9e0fce877cd168dcd5de16040a4ab85](https://etherscan.io/address/0x2747096ff9e0fce877cd168dcd5de16040a4ab85#code#F3#L1). 11 | > 12 | > The interface that your solution contract must adhere to is below. The `sqrt` function will be called by the challenge 13 | > contract when you call its `challenge` function. 14 | > 15 | > ```solidity 16 | > interface ISqrt { 17 | > function sqrt(Fixed18[INPUT_SIZE] calldata) external view returns (Fixed18[INPUT_SIZE] memory); 18 | > } 19 | > ``` 20 | 21 | ## Test 22 | 23 | This solution is written in [Huff](https://github.com/huff-language), so you will need 24 | [huffc](https://huff.sh) installed to compile it. 25 | 26 | > **Note** 27 | > In order to use the huff compiler within foundry tests / scripts, FFI must be enabled. If you'd like to 28 | > read into what this repo uses FFI for before running anything, see [foundry-huff](https://github.com/huff-language/foundry-huff) 29 | > and [huff-rs](https://github.com/huff-language/huff-rs). 30 | 31 | To test locally: 32 | 33 | ```bash 34 | $ LOCAL=true forge test 35 | ``` 36 | -------------------------------------------------------------------------------- /src/Fixed18.sol: -------------------------------------------------------------------------------- 1 | // SPADIX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | type Fixed18 is uint256; 5 | 6 | uint256 constant FIXED18BASE = 10 ** 18; 7 | 8 | function add(Fixed18 a, Fixed18 b) pure returns (Fixed18) { 9 | return Fixed18.wrap(Fixed18.unwrap(a) + Fixed18.unwrap(b)); 10 | } 11 | 12 | function sub(Fixed18 a, Fixed18 b) pure returns (Fixed18) { 13 | return Fixed18.wrap(Fixed18.unwrap(a) - Fixed18.unwrap(b)); 14 | } 15 | 16 | function mul(Fixed18 a, Fixed18 b) pure returns (Fixed18) { 17 | return Fixed18.wrap((Fixed18.unwrap(a) * Fixed18.unwrap(b)) / FIXED18BASE); 18 | } 19 | 20 | function div(Fixed18 a, Fixed18 b) pure returns (Fixed18) { 21 | return Fixed18.wrap((Fixed18.unwrap(a) * FIXED18BASE) / Fixed18.unwrap(b)); 22 | } 23 | 24 | function distance(Fixed18 a, Fixed18 b) pure returns (Fixed18) { 25 | uint256 _a = Fixed18.unwrap(a); 26 | uint256 _b = Fixed18.unwrap(b); 27 | unchecked { 28 | if (_a < _b) { 29 | return Fixed18.wrap(_b - _a); 30 | } else { 31 | return Fixed18.wrap(_a - _b); 32 | } 33 | } 34 | } 35 | 36 | function lt(Fixed18 a, Fixed18 b) pure returns (bool) { 37 | return Fixed18.unwrap(a) < Fixed18.unwrap(b); 38 | } 39 | 40 | function le(Fixed18 a, Fixed18 b) pure returns (bool) { 41 | return Fixed18.unwrap(a) <= Fixed18.unwrap(b); 42 | } 43 | 44 | function gt(Fixed18 a, Fixed18 b) pure returns (bool) { 45 | return Fixed18.unwrap(a) > Fixed18.unwrap(b); 46 | } 47 | 48 | function bit_and(Fixed18 a, Fixed18 b) pure returns (Fixed18) { 49 | return Fixed18.wrap(Fixed18.unwrap(a) & Fixed18.unwrap(b)); 50 | } 51 | 52 | function bit_xor(Fixed18 a, Fixed18 b) pure returns (Fixed18) { 53 | return Fixed18.wrap(Fixed18.unwrap(a) ^ Fixed18.unwrap(b)); 54 | } 55 | 56 | using { add, sub, mul, div, distance, lt, le, gt, bit_and, bit_xor } for Fixed18 global; 57 | -------------------------------------------------------------------------------- /script/MySolution.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "forge-std/Script.sol"; 5 | import { ISqrt } from "src/SqrtChallenge.sol"; 6 | import { IOptimizor, OPTIMIZOR_MAINNET } from "src/IOptimizor.sol"; 7 | import { computeKey } from "src/CommitHash.sol"; 8 | 9 | // Change accordingly. 10 | uint256 constant SQRT_ID = 1; 11 | // Change accordingly. 12 | address constant SUBMITTER = address(0xfd8e46f80ec8A2f05732B58744921Fa8234c4A6b); 13 | // Change accordingly. 14 | address constant RECEIVER = address(0x7b36949624eFeA27dD9F0925b25c309A09774784); 15 | // Change accordingly. 16 | uint256 constant SALT = 0; 17 | // Deploy solution contract. 18 | address constant DEPLOYED_SOLUTION = address(0xbf09fc36972D1B3d565681a2fDe48dfeE7E26982); 19 | 20 | contract MySolutionDeployAndCommit is Script { 21 | function run() public { 22 | vm.startBroadcast(); 23 | 24 | // Compile Solution contract 25 | string[] memory cmds = new string[](3); 26 | cmds[0] = "huffc"; 27 | cmds[1] = string("src/Solution.huff"); 28 | cmds[2] = "-b"; 29 | bytes memory code = vm.ffi(cmds); 30 | 31 | // Deploy solution contract. 32 | address sqrt; 33 | assembly { 34 | sqrt := create(0, add(code, 0x20), code) 35 | } 36 | 37 | // Commit solution key. 38 | OPTIMIZOR_MAINNET.commit(computeKey(SUBMITTER, address(sqrt).codehash, SALT)); 39 | 40 | // vm.roll(block.number + 65); 41 | // 42 | // OPTIMIZOR_MAINNET.challenge(SQRT_ID, address(sqrt), RECEIVER, SALT); 43 | 44 | vm.stopBroadcast(); 45 | } 46 | } 47 | 48 | contract MySolutionChallenge is Script { 49 | function run() public { 50 | vm.broadcast(); 51 | 52 | // Submit the challenge solution. 53 | OPTIMIZOR_MAINNET.challenge(SQRT_ID, address(DEPLOYED_SOLUTION), RECEIVER, SALT); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/MySolution.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import "forge-std/Test.sol"; 5 | import { ISqrt, SqrtChallenge, INPUT_SIZE } from "src/SqrtChallenge.sol"; 6 | import { Fixed18 } from "src/Fixed18.sol"; 7 | import { IOptimizor, OPTIMIZOR_MAINNET } from "src/IOptimizor.sol"; 8 | import { computeKey } from "src/CommitHash.sol"; 9 | import { HuffDeployer } from "foundry-huff/HuffDeployer.sol"; 10 | 11 | contract MySolutionTest is Test { 12 | uint256 constant SQRT_ID = 1; 13 | SqrtChallenge challenge; 14 | ISqrt sqrt; 15 | 16 | // Change accordingly. 17 | uint256 constant currentLeaderGas = 1000000; 18 | // Change accordingly. 19 | uint256 constant SEED = 0; 20 | // Change accordingly. 21 | uint256 constant SALT = 0; 22 | // Change accordingly. 23 | address constant MY_ADDRESS = address(0xcafe); 24 | 25 | function setUp() public { 26 | // Fork mainnet 27 | vm.createSelectFork(vm.rpcUrl("flashbots")); 28 | 29 | // Deploy our solution & a mock challenge 30 | sqrt = ISqrt(HuffDeployer.config().deploy("Solution")); 31 | challenge = new SqrtChallenge(); 32 | } 33 | 34 | function testSpecificSeed() public { 35 | testWithSeed(SEED); 36 | } 37 | 38 | // function testFuzzSeed(uint256 seed) public { 39 | // testWithSeed(seed); 40 | // } 41 | 42 | function testEndToEnd() public { 43 | // Don't run this test in the workflow 44 | if (!vm.envBool("LOCAL")) return; 45 | 46 | // Commit our solution 47 | OPTIMIZOR_MAINNET.commit(computeKey(MY_ADDRESS, address(sqrt).codehash, SALT)); 48 | 49 | // Fast forward 65 blocks 50 | vm.roll(block.number + 65); 51 | 52 | // Call the challenge contract's `challenge` func as `MY_ADDRESS` 53 | vm.prank(MY_ADDRESS); 54 | OPTIMIZOR_MAINNET.challenge(SQRT_ID, address(sqrt), MY_ADDRESS, SALT); 55 | } 56 | 57 | function testWithSeed(uint256 seed) internal { 58 | Fixed18[INPUT_SIZE] memory input; 59 | for (uint256 i = 0; i < INPUT_SIZE; ++i) { 60 | input[i] = Fixed18.wrap(i); 61 | } 62 | 63 | uint256 gasSpent = challenge.run(address(sqrt), seed); 64 | assertTrue(gasSpent < currentLeaderGas); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /assets/sqrt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/SqrtChallenge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import { IChallenge } from "src/IChallenge.sol"; 5 | 6 | import { Fixed18 } from "src/Fixed18.sol"; 7 | 8 | uint256 constant INPUT_SIZE = 5; 9 | 10 | // Expecting 5 decimal places of precision. 11 | Fixed18 constant EPSILON = Fixed18.wrap(0.0001 * 10 ** 18); 12 | 13 | interface ISqrt { 14 | function sqrt(Fixed18[INPUT_SIZE] calldata) external view returns (Fixed18[INPUT_SIZE] memory); 15 | } 16 | 17 | function random_fixed18(uint256 seed) pure returns (Fixed18) { 18 | return Fixed18.wrap(uint256(random_uint64(seed))); 19 | } 20 | 21 | function random_uint64(uint256 seed) pure returns (uint64) { 22 | return uint64(uint256(keccak256(abi.encodePacked(seed)))); 23 | } 24 | 25 | contract SqrtChallenge is IChallenge { 26 | error DoesNotSatisfyTolerance(uint256 input, uint256 output); 27 | 28 | function run(address target, uint256 seed) external view override returns (uint32) { 29 | // Generate inputs. 30 | Fixed18[INPUT_SIZE] memory inputs; 31 | unchecked { 32 | for (uint256 i = 0; i < INPUT_SIZE; ++i) { 33 | inputs[i] = random_fixed18(seed); 34 | seed = Fixed18.unwrap(inputs[i]); 35 | } 36 | } 37 | 38 | uint256 preGas = gasleft(); 39 | Fixed18[INPUT_SIZE] memory outputs = ISqrt(target).sqrt(inputs); 40 | uint256 usedGas; 41 | unchecked { 42 | usedGas = preGas - gasleft(); 43 | } 44 | 45 | verify(inputs, outputs); 46 | 47 | return uint32(usedGas); 48 | } 49 | 50 | // Reverts if invalid 51 | function verify(Fixed18[INPUT_SIZE] memory inputs, Fixed18[INPUT_SIZE] memory outputs) internal pure { 52 | unchecked { 53 | for (uint256 i = 0; i < INPUT_SIZE; ++i) { 54 | verify(inputs[i], outputs[i]); 55 | } 56 | } 57 | } 58 | 59 | // Reverts if invalid 60 | function verify(Fixed18 input, Fixed18 output) internal pure { 61 | // Checks 62 | // | output * output - input | 63 | // -------------------------- < EPSILON 64 | // | output | 65 | if (!output.mul(output).distance(input).div(output).lt(EPSILON)) { 66 | revert DoesNotSatisfyTolerance(Fixed18.unwrap(input), Fixed18.unwrap(output)); 67 | } 68 | } 69 | 70 | function name() external pure override returns (string memory) { 71 | return "SQRT"; 72 | } 73 | 74 | function description() external pure override returns (string memory) { 75 | return "Calculating the square root of an array of Fixed18 numbers"; 76 | } 77 | 78 | function svg(uint256 tokenId) external pure returns (string memory art) { 79 | uint32 level = uint32(tokenId); 80 | if (level == 0) { 81 | return ''; 82 | } 83 | if (level == 1) { 84 | return 85 | ''; 86 | } 87 | if (level == 2) { 88 | return 89 | ''; 90 | } 91 | if (level == 3) { 92 | return 93 | ''; 94 | } 95 | if (level == 4) { 96 | return 97 | ''; 98 | } 99 | if (level == 5) { 100 | return 101 | ''; 102 | } 103 | if (level == 6) { 104 | return 105 | ''; 106 | } 107 | if (level == 7) { 108 | return 109 | ''; 110 | } 111 | if (level == 8) { 112 | return 113 | ''; 114 | } 115 | if (level == 9) { 116 | return 117 | ''; 118 | } 119 | if (level == 10) { 120 | return 121 | ''; 122 | } 123 | if (level == 11) { 124 | return 125 | ''; 126 | } 127 | if (level == 12) { 128 | return 129 | ''; 130 | } else { 131 | return 132 | ''; 133 | } 134 | } 135 | } 136 | --------------------------------------------------------------------------------