{"id":21107191,"url":"https://github.com/luloxi/ethernaut-with-hardhat","last_synced_at":"2025-10-07T04:19:28.193Z","repository":{"id":148436642,"uuid":"568293147","full_name":"luloxi/Ethernaut-with-Hardhat","owner":"luloxi","description":"Suite for solving Ethernaut CTF challenges","archived":false,"fork":false,"pushed_at":"2023-10-31T16:09:12.000Z","size":1851,"stargazers_count":2,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-04T06:33:14.006Z","etag":null,"topics":["audit","ctf","ctf-challenges","solidity","solidity-contracts","solidity-language"],"latest_commit_sha":null,"homepage":"https://ethernaut.openzeppelin.com/","language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luloxi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-20T03:44:28.000Z","updated_at":"2024-10-25T20:52:24.000Z","dependencies_parsed_at":"2024-11-20T00:46:51.231Z","dependency_job_id":null,"html_url":"https://github.com/luloxi/Ethernaut-with-Hardhat","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/luloxi/Ethernaut-with-Hardhat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luloxi%2FEthernaut-with-Hardhat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luloxi%2FEthernaut-with-Hardhat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luloxi%2FEthernaut-with-Hardhat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luloxi%2FEthernaut-with-Hardhat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luloxi","download_url":"https://codeload.github.com/luloxi/Ethernaut-with-Hardhat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luloxi%2FEthernaut-with-Hardhat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278718263,"owners_count":26033726,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-07T02:00:06.786Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audit","ctf","ctf-challenges","solidity","solidity-contracts","solidity-language"],"created_at":"2024-11-20T00:36:41.699Z","updated_at":"2025-10-07T04:19:28.166Z","avatar_url":"https://github.com/luloxi.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"# About this repo\n\nSuite to solve [Ethernaut](https://ethernaut.openzeppelin.com/) challenges.\n\n**Prior to using this Ethernaut solving suite:**\n\n1. Create and fill a **.env** file with the data specified in **.env.example** on the root folder\n2. Run `yarn` on this folder.\n\n- If you wanna skip Etherscan verifications, don't fill out the ETHERSCAN_API_KEY variable.\n\n\u003cimg src=\"./img/birdy.gif\" /\u003e\n\n## File locations\n\n- **contracts/ folder**: Original and attacker contracts are inside the same file\n- **deploy/ folder**: Deploy scripts for attacks that require deploying another contract\n- **scripts/ folder**: Scripts that run the entire attack from start to finish (pwn)\n- **.env**: **Env**ironment variables. Located on the root folder of the project.\n\nFor any troubles, visit the [Troubleshooting](#troubleshooting) section.\n\n# Index\n\n1. [Fallback](#01---fallback)\n2. [Fallout](#02---fallout)\n3. [CoinFlip](#01---coinflip)\n4. [Telephone](#04---telephone)\n5. [Token](#05---token)\n6. [Delegation](#06---delegation)\n7. [Force](#07---force)\n8. [Vault](#08---vault)\n9. [King](#09---king)\n10. [Reentrance](#10---reentrance)\n11. [Elevator](#11---elevator)\n12. [Privacy](#12---privacy)\n13. [Gatekeeper One](#13---gatekeeper-one)\n14. [Gatekeeper Two](#14---gatekeeper-two)\n15. [Naught Coin](#15---naught-coin)\n16. [Preservation](#16---preservation)\n17. [Recovery](#17---recovery)\n18. [Magic Number](#18---magic-number)\n19. [Alien Codex](#19---alien-codex)\n20. [Denial](#20---denial)\n21. [Shop](#21---shop)\n22. [Dex](#22---dex)\n23. [Dex Two](#23---dex-two)\n24. [Puzzle Wallet](#24---puzzle-wallet)\n25. [Motorbike](#25---motorbike)\n26. [DoubleEntryPoint](#26---double-entry-point)\n27. [Good Samaritan](#27---good-samaritan)\n\n# 01 - Fallback\n\nLesson on calling the **[receive function](https://docs.soliditylang.org/en/v0.8.17/contracts.html?highlight=receive#receive-ether-function)**.\n\n1. Change the address in **scripts/fallback.js** to your instance address\n2. Run `yarn hardhat run scripts/fallback.js`\n\n# 02 - Fallout\n\nLesson on... dangers of not using **constructor keyword**? Paying attention to **typos**?\n\n3. Change the address in **scripts/fallout.js** to your instance address\n4. Run `yarn hardhat run scripts/fallout.js`\n\n# 03 - Coinflip\n\nLesson on **[exploiting blockhash \"randomness\"](https://docs.soliditylang.org/en/latest/units-and-global-variables.html#block-and-transaction-properties)**.\n\n1. Change the address in **contracts/CoinFlip.sol** to your instance address\n2. Run `yarn hardhat run scripts/coinflip.js`\n\nNote: If one of the \"guesses\" fails, comment out the \"deploy\" section of the script and uncomment the \"use existing contract\" one, then populate it with your attacker contract's address. Or just go to Etherscan and call the \"attack\" write function every some seconds, your call.\n\n# 04 - Telephone\n\nLesson on **using a contract to attack** another contract.\n\n1. Change the address in **contracts/Telephone.sol** to your instance address\n2. Run `yarn hardhat run scripts/telephone.js`\n\n# 05 - Token\n\nLesson on **[underflow and overflow](https://docs.soliditylang.org/en/v0.8.17/control-structures.html?highlight=underflow#checked-or-unchecked-arithmetic)**.\n\n1. Change the address in **scripts/token.js** to your instance address\n2. Run `yarn hardhat run scripts/token.js`\n\n# 06 - Delegation\n\nLesson on **[delegatecall](https://docs.soliditylang.org/en/v0.8.17/introduction-to-smart-contracts.html?highlight=delegatecall#delegatecall-and-libraries)** and calling through **[fallback function](https://docs.soliditylang.org/en/v0.8.17/contracts.html?highlight=fallback#fallback-function)**\n\n1. Change the address in **scripts/delegation.js** to your instance address\n2. Run `yarn hardhat run scripts/delegation.js`\n\n# 07 - Force\n\nLesson on **[self-destruct](https://docs.soliditylang.org/en/v0.8.17/introduction-to-smart-contracts.html?highlight=self%20destruct#deactivate-and-self-destruct)**.\n\n1. Change the address in **contracts/Force.sol** to your instance address\n2. Run `yarn hardhat run scripts/force.js`\n\n# 08 - Vault\n\nLesson on **[private variables](https://docs.soliditylang.org/en/v0.8.17/security-considerations.html?highlight=private#private-information-and-randomness)** and **[storage slots](https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html?highlight=storage%20slots)**.\n\n1. Change the address in **scripts/vault.js** to your instance address\n2. Run `yarn hardhat run scripts/vault.js`\n\n# 09 - King\n\nLesson on **payable** keyword in **constructor** and **disabling receive function**\n\n3. Change the address in **deploy/09-deploy-king.js** to your instance address\n4. Run `yarn hardhat run scripts/king.js`\n\n# 10 - Reentrance\n\nLesson on **[reentrancy attacks](https://docs.soliditylang.org/en/v0.8.17/security-considerations.html#re-entrancy)**.\n\n1. Change the address in **contracts/Reentrance.sol** to your instance address\n2. Run `yarn hardhat run scripts/reentrance.js`\n\n# 11 - Elevator\n\nLesson on **interfaces** (and them not having the view keyword...) and using a **boolean toggle**.\n\n1. Change the address in **contracts/Elevator.sol** to your instance address\n2. Run `yarn hardhat run scripts/elevator.js`\n\n# 12 - Privacy\n\nLesson on size and ordering of **[storage slots](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#layout-of-state-variables-in-storage)**.\n\n1. Change the address in **scripts/privacy.js** to your instance address\n2. Run `yarn hardhat run scripts/privacy.js`\n\n# 13 - Gatekeeper One\n\nLesson on **typecasting**, **data types**, and **gas calculation**\n\n1. Change the address in **contracts/GatekeeperOne.sol** to your instance address\n2. Run `yarn hardhat run scripts/gatekeeperOne.js`\n\n## About\n\nFirst condition is just calling from another contract. Done.\n\n## Getting the right gas\n\nCopied solution from [this guy](https://www.youtube.com/watch?v=AUQxXJiqLF4) that doesn't explain very well how it's done. My interpretation is that it just multiplies 3 times 8191 to get enough gas in any network conditions, then adds up from 150 up to 270 to cover the whole range the transaction could cost.\n\n## About getting the right bytes8 key\n\n### Condition 1:\n\n- Hexadecimal uses 4 bits per digit (0-9+a-f)\n- And it uses two digits per byte\n- If 1 byte consists in 2 digits, bytes8 has 16 digits\n\n0xXXXXXXXXXXXXXXXX\n\n- 1 byte = 8 bits\n- 8 bytes = 64 bits\n\n**bytes8** has the same amount of digits as **uint64**\n\nFirst requirement converts one argument from uint64 to uint32 -\u003e uint32(uin64(\\_gateKey)),\n\nThat means it takes this part of the password:\n0x XXXXXXXX **\"XXXXXXXX\"**\n\nand it has to equal the same argument from uint64 to uint16 on the other side, -\u003e uint16(uint64(\\_gateKey))\n\n0x XXXXXXXX XXXX **\"XXXX\"**\n\nWhen comparing to a uint32, the uint16 will fill the bytes it doesn't have with zeros, that's why:\n\n0x XXXXXXXX **0000** XXXX\n\nWe already have 4 digits of the password!\n\nPassword: 0xXXXXXXXX**0000**XXXX\n\n### Condition 2:\n\nLast 4 bytes (8 digits, or uint32) must not be equal to uint64 of key\nuint32(uint64(\\_gateKey)) != uint64(\\_gateKey)\n\n0x **\"00000000\"** 0000XXXX != 0x **XXXXXXXX** XXXXXXXX\n\nThis means that first 4 bytes (8 digits) must have at least one different digit than 0\n0x **\"XXXXXXXX\"** 0000XXXX\n\nPassword: 0x**XXXXXXXX0000**XXXX\n\n### Condition 3:\n\nFirst requirement converts one argument from uint64 to uint32 | uint32(uin64(\\_gateKey)),\nand must equal first two bytes (4 digits, or uint16) of tx.origin (our address!) || uint16(tx.origin)\n\n0x XXXXXXXX 0000 \"XXXX\"\n\nThat last \"XXXX\" MUST be the last 4 digits of our wallet address.\n\nPassword: 0x**XXXXXXXX0000XXXX**\n\nAttacker contract is using the **Masking bits off** function with the \u0026 operator on your wallet address.\n\n- **1001**0101 \u003c- This\n- **1111**0000 \u003c- AND this\n- **1001**0000 \u003c- Equals this (only both 1 result in 1)\n\n- X value with 0 equals 0\n- X value with 1 equals X.\n\n# 14 - Gatekeeper Two\n\nLesson on **contract size before initializing** and the **xor operator**.\n\n1. Change the address in **deploy/14-deploy-gatekeepertwo.js** to your instance address\n2. Run `yarn hardhat run scripts/gatekeeperTwo.js`\n\n## About\n\n### Condition 2:\n\nTo call from a contract of size 0, we must call the enter function from the constructor, because the contract's size will be 0 until it is initialized.\n\nHere's the reference inside the code\n\nassembly {\nx := **extcodesize**(caller())\n}\n\n### Condition 3:\n\n^ stands for xor bitwise operator, this is an exclusive or operator.\n\nThis means that if: a ^ b == c, then a ^ c == b\n\n- \u0026 : and (x, y) bitwise **and** of x and y; where 1010 \u0026 1111 == 1010 (true false true false)\n- | : or (x, y) bitwise **or** of x and y; where 1010 | 1111 == 1111 (true true true true)\n- ^ : xor (x, y) bitwise **xor** of x and y; where 1010 ^ 1010 == 0101 (false true false true)\n- ~ : not (x) bitwise **not** of x; where ~1010 == 0101 (false true false true)\n\n# 15 - Naught Coin\n\nLesson on **ERC20** standard, and approving to **transferFrom** any address (including our own).\n\n1. Change the address in **scripts/naughtCoin.js** to your instance address\n2. Run `yarn hardhat run scripts/naughtCoin.js`\n\n# 16 - Preservation\n\nLesson combining knowledge of **Privacy** and **Delegation** challenges.\n\n1. Change the address in **scripts/preservation.js** to your instance address\n2. Run `yarn hardhat run scripts/preservation.js`\n\n# 17 - Recovery\n\nLesson on **contract creation addresses**.\n\n1. Change the address in **scripts/recovery.js** to your instance address\n2. Run `yarn hardhat run scripts/recovery.js`\n\n## Getting contract creation address\n\nTo figure out a contract address, it takes the rightmost 20 bytes (160 bits) of the keccak hash of the RLP encoding of the structure containing only the sender of the contract creation transaction and the nonce of that transaction.[(page 10 of Ethereum Yelowpaper)](https://ethereum.github.io/yellowpaper/paper.pdf)\n\n`address = rightmost_20_bytes(keccak(RLP(sender address, nonce)))`\n\nRLP = Recursive Length Prefix\n\nIn the attack script, we're using [ethers.utils.SolidityKeccak256](https://docs.ethers.io/v5/api/utils/hashing/#utils-solidityKeccak256)\n\n`ethers.utils.solidityKeccak256( types , values ) ⇒ string\u003c DataHexString\u003c 32 \u003e \u003e source`\nReturns the KECCAK256 of the non-standard encoded values packed according to their respective type in types.\n\nAs a reference: **abi.encodePacked** in Solidity gives the same output as **ethers.utils.solidityPack**\nHere we encode and hash directly into keccak256 with a single command with **solidityKeccak256**.\n\nThe RLP encoding of a 20-byte address is: 0xd6, 0x94 .\nAnd for all integers less than 0x7f, its encoding is just its own byte value.\nSo the RLP of 1 is 0x01.\n\nThat's how we end with this line:\n\n```shell\nconst simpleTokenAdd = await ethers.utils.solidityKeccak256(\n    [\"bytes1\", \"bytes1\", \"address\", \"bytes1\"],\n    [\"0xd6\", \"0x94\", recoveryInstanceAddress, \"0x01\"]\n  )\n```\n\nAnd then just take the last 40 digits (20 bytes) -because each byte is 2 digits-, and append them to \"0x\" to form the contract's address:\n\n```shell\nconst simpleTokenAddress =\n\"0x\" + simpleTokenAdd.substring(simpleTokenAdd.length - 40, simpleTokenAdd.length)\n```\n\n# 18 - Magic Number\n\nLesson on Opcode, Bytecode, Contract Creation vs. Runtime.\n\n1. Change the address in **scripts/magicNumber.js** to your instance address\n2. Run `yarn hardhat run scripts/magicNumber.js`\n\n## About\n\n[Creation vs. Runtime](https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-ii-creation-vs-runtime-6b9d60ecb44c/)\n\nGoal is to run two sets of OPCODES. One for **creation** (initialization) and another for **runtime** (10 OPCODES or less).\nOnce initialized, runtime is stored for future computations.\n\nSolidity Code (high level) \u003e Opcodes \u003e Byte code (EVM language)\n\n### Runtime code\n\nTo RETURN 42, we need:\n\n1. First we need to PUSH the value (v)\n2. Then we need to store it in memory with MSTORE, which needs position (p) first and then size (s)\n3. Then we RETURN the value (v)\n\n[Hex to Decimal converter](https://www.rapidtables.com/convert/number/hex-to-decimal.html)\n\nUsing the converter, we can see that 42 value in Hex is **0x2a**\n\n[EVM Opcodes](https://github.com/crytic/evm-opcodes)\n\nPUSH is 0x60\nMSTORE is 0x52\nRETURN is 0xf3\n\nBytecode - Description\n\n1. **602a** - PUSH the value (v) to MSTORE. In this case, we push 42 (0x2a).\n2. **6050** - PUSH the position (p) (0x50) to MSTORE\n3. **52** - Store value (v=0x2a) at position (p=0x50) in memory.\n4. **6020** - PUSH the size (s) of v to the stack. In this case, we push 32 (0x20) for 32 bytes.\n5. **6050** - PUSH the position (p) of the size (s) (slot in which value was stored). In this case, position was 0x50.\n6. **f3** - RETURN value, v=0x24 (42) of size s=0x20 (32 bytes)\n\nConcatenated, it reads: `602a60505260206050f3`\n\nThis is **10 bytes**, as it has 20 digits when concatenated.\n\n[Solidity Bytecode and Opcode Basics](https://medium.com/@blockchain101/solidity-bytecode-and-opcode-basics-672e9b1a88c2)\n\n\"In the EVM, there are 3 places to store data. Firstly, in the stack. We’ve just used the “PUSH” opcode to store data there as per the example above. Secondly in the memory (RAM) where we use the “MSTORE” opcode and lastly, in the disk storage where we use “SSTORE” to store the data. The gas required to store data to storage is the most expensive and storing data to stack is the cheapest.\"\n\n### Creation (initialization) code\n\nThe CODECOPY opcode can be used to copy the runtime opcodes. It takes three arguments: the **destination** position of copied code in memory, **current position** of runtime opcode in the bytecode and **size** of the code in bytes.\n\nValue 10 (size of our code in bytes) in hex is **0x0a**\n\nWe don't know the position of runtime opcode in the final bytecode (since initialization opcode comes before runtime opcode), so\nwe omit by setting position as unknown with --.\n\nAfter CODECOPY, we need to specify the values for RETURN.\n\n1. **600a** - PUSH size of runtime opcode to stack. Size (s) param for COPYCODE.\n2. **60--** - PUSH -- (unknown) to the stack. Position (p) param for COPYCODE.\n3. **6000** - PUSH 0x00 (chosen destination in memory) in stack. Destination (d) param for COPYCODE.\n4. **39** - CODECOPY of size (s) at position (p) to destination (d) in memory.\n5. **600a** - PUSH 0x0a (size of our runtime opcode) in stack. Size (s) param for RETURN.\n6. **6000** - PUSH 0x00 (location of value in memory) in stack. Position (p) param for RETURN.\n7. **f3** - RETURN value (v) of size (s) at position (p)\n\nSo the initialization opcode is `600460--600039600a6000f3` which is 12 bytes in total.\n\nWhich means that runtime opcodes start at index 12 or position **0x0c**\n\nTherefore, initialization opcode must be `6004600c600039600a6000f3`\n\n### Final opcode\n\nConcatenate initialization opcode with runtime opcode:\n\n```shell\n600a600c600039600a6000f3 + 602a60505260206050f3\n600a600c600039600a6000f3602a60505260206050f3\n```\n\nAnd now we can create the contract by sending a transaction to the zero address (0x0) with some data as it will be interpreted as Contract Creation by the EVM.\n\nOn scripts/magicnumber.js, I appendedd **0x** at the beginning of the final opcode to make it readable as hex to be able to send the transaction.\n\nMore in-depth guides: [Watch how to solve it](https://www.youtube.com/watch?v=FsPWuKK8mWI\u0026list=PLiAoBT74VLnmRIPZGg4F36fH3BjQ5fLnz\u0026index=19) - [Read how to solve it](https://medium.com/coinmonks/ethernaut-lvl-19-magicnumber-walkthrough-how-to-deploy-contracts-using-raw-assembly-opcodes-c50edb0f71a2)\n\n# 19 - Alien Codex\n\nLesson on **Check-Effect-Interact** and **layout of dynamically-sized arrays in storage**\n\n1. Change the address in **scripts/alienCodex.js** to your instance address\n2. Run `yarn hardhat run scripts/alienCodex.js`\n\n## About\n\nFirst step is to make contact. Then we retract, so we substract 1 from position 0. Then we gotta figure out where is the owner. Then we gotta figure out the format to enter our address.\n\n### Check-Effect-Interact\n\nRetract function doesn't respect the [Check-Effect-Interact pattern](https://docs.soliditylang.org/en/v0.8.17/security-considerations.html#use-the-checks-effects-interactions-pattern), it does the effect right away. If something doesn't respect the CEI pattern, there's a chance **there's a security vulnerability there.**\n\n**codex.length--** is gonna remove the last element from the array.\n\nWe're doing this Effect without doing first a Check. This allows the opportunity to do an **underflow** on the array. If we substract 1 from the position 0, we're going to go to the last element of the array, and we're gonna start from there, which will allow to put anything we want wherever we want in the storage of the contract.\n\n### Layout of State Variables in Storage\n\nState variables of contracts are stored in storage in a compact way such that **multiple values sometimes use the same storage slot**. **Except for dynamically-sized arrays** and mappings (see below), data is stored contiguously item after item starting with the first state variable, which is stored in slot 0. [Read more in SolidityLang.org](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html)\n\nDynamically sized arrays use keccak-256 to find the starting position (always full stack slot - 32 bytes).\n\n**Mapping**\n\n- p = position (storing the array length)\n- k = value (value corresponding to the mapping)\n\n- keccak256(k.p)\n\nTo find this information, we hash them together by concatenating then hashing them\n\n1. **p = web3.utils.keccak256(web3.abi.encodeParameters([\"uint256\"], [1]))**\n\nPosition: We're gonna hash with keccak256 the parameters ([\"uint256\"], [1])\nThis is gonna be slot 1 inside AlienCodex.\nRemember slot 0 has the owner address and contact boolean set to true.\n\n2. i = BigInt(2 \\*\\* 256) - BigInt(p)\n\nWe substact to go to the slot 0, not inside the contract storage, but **inside the array storage**, because that's where the owner is.\nConverting hashed value to BigInt, we're able to substact back to Slot 0 of the codex.\n\n3. Content = \"0x\" + \"0\".repeat(24) + playeraddress.slice(2)\n\nplayeraddress.slice(2) \u003c- This takes out the 0x at the beginning of playeraddress\n\nPadding our address with 0's to meet the expected 32 bytes length.\n\nExploiting a flaw in the ABI specs. Doesn't validate that the length of the array matches the length of the payload. (e.g 0'd out.)\n\n4. contract.revise(i, content)\n\nPlace our **content** into the owner array **index** location\n\n[Watch how to solve it](https://www.youtube.com/watch?v=oGx-EvSsQbE)\n\n# 20 - Denial\n\nLesson on [different ways of sending value](https://solidity-by-example.org/sending-ether/) and **assert method on ^0.6.0** (doesn't revert the transaction)\n\n1. Change the address in **scripts/denial.js** to your instance address\n2. Run `yarn hardhat run scripts/denial.js`\n\n## About sending value\n\n**send and transfer**:\n\n- Require an address to be marked as **payable** to send them ETH.\n- They cost 2300 gas to prevent reentrancy vulnerability.\n\n**call**\n\n- Doesn't require an address to be marked as payable to send them ETH.\n- Can be manually specified how much gas to use.\n\n```shell\nfunction depositUsingTransfer(address payable _to) public payable {\n    _to.transfer(msg.value); // 2300 gas, throws error\n}\n\nfunction depositUsingSend(address payable _to) public payable {\n    bool sent = _to.send(msg.value); // 2300 gas, returns bool that can be used to throw error\n    require(sent, \"Error! Ether not sent!\");\n}\n```\n\n**Call** returns bool and the return (if any) of calling the specified function.\nGas can also be specified: `_to.call{gas: 20000, value: ...}`\n\n```shell\nfunction depositUsingCall(address _to) public payable {\n\n    (bool sent, /* bytes memory data */) = _to.call{value: msg.value}(\"\");\n    require(sent, \"Error! Ether not sent!\");\n}\n```\n\n## About assert\n\nThere are 3 ways of throwing an error: **assert, revert and require**\n\nFor this version of the solidity compiler (^0.6.0), **revert and require** undo any state changes that have occurred until that error was reached. **assert did not** refund or return any of the gas that was sent to it.\n\nrequire(condition, \"Error explanation\")\nrevert(\"an error has occurred\") \u003c- Usually nested inside some logic\nassert(condition)\n\n# 21 - Shop\n\nLesson on **interfaces** and using **ternary operators** to bypass repeated check with a **boolean toggle** in between\n\n1. Change the address in **scripts/shop.js** to your instance address\n2. Run `yarn hardhat run scripts/shop.js`\n\n# 22 - Dex\n\nLesson on **DEX mechanisms** and Solidity's **inhability to comprehend floating point numbers**\n\n1. Change the address in **scripts/shop.js** to your instance address\n2. Run `yarn hardhat run scripts/shop.js`\n\n## About\n\nIn Solidity, 3 / 2 = 1, because it can't comprehend floating point numbers such as 1.5.\n\nWe start like this:\n\n\u003cimg src=\"./img/22-dex-1.png\" /\u003e\n\nThen we swap all of our Token 1 for Token 2\n\n\u003cimg src=\"./img/22-dex-2.png\" /\u003e\n\nAt this point the exchange rate changed. Now **20 token2** would be **20 \\* 110 / 90 = 24,444**\n\nSince **result is an integer**, we get 24 token2. Swap again to get another price readjustment.\n\n\u003cimg src=\"./img/22-dex-3.png\" /\u003e\n\nEach swap gets us more of token1 or token2 than we had before, because of the **inaccuracy of price calculation** in the get_swap_price function. Keep swapping:\n\n\u003cimg src=\"./img/22-dex-4.png\" /\u003e\n\nNow we got enough tokens to drain the 110 token1! 65 tokens would get us 158!\n**65 \\* 110 / 45 = 158**\n\nBut as there aren't enough tokens, we need to calculate that number down.\n**110 \\* 45 / 110 = 45**\n\nWe just need to swap 45 tokens in that last transaction :)\n\n# 23 - Dex Two\n\nLesson on **ERC20 standard**, **DEX oracle manipulation**, and using **transferFrom** to bypass restrictions.\n\n1. Change the address in **scripts/dexTwo.js** to your instance address\n2. Run `yarn hardhat run scripts/dexTwo.js`\n\n## About\n\n[Read how to solve it](https://daltyboy11.github.io/every-ethernaut-challenge-explained/#dex-two)\n\n# 24 - Puzzle Wallet\n\nLesson on **delegatecall** and **proxy contracts**.\n\n## Reccomended reads:\n\n- [EIP-1967: Proxy Storage Slots](https://eips.ethereum.org/EIPS/eip-1967)\n- [Proxies (OpenZeppelin docs)](https://docs.openzeppelin.com/contracts/4.x/api/proxy)\n\n## About\n\nWhen an EOA (Externally Owned Account) does a call to a contract that does a **regular call** to another contract, the contract is taken as sender and owner of value for the second contract.\n\n\u003cimg src=\"./img/24-puzzlewallet-1.png\" /\u003e\n\nBut with **delegatecall**, EOA is taken as sender and owner of value for the second contract. (i.e: Logic contract (2nd) for a Proxy contract (1st))\n\n\u003cimg src=\"./img/24-puzzlewallet-2.png\" /\u003e\n\nFor the attack, we're using Multicall to Multicall twice the deposit function.\n\n\u003cimg src=\"./img/24-puzzlewallet-3.png\" /\u003e\n\n[Watch how to solve it](https://www.youtube.com/watch?v=toVc-iX-XAA) - [Shorter video](https://www.youtube.com/watch?v=3JcS-04cAj0)\n\n# 25 - Motorbike\n\nLesson on **proxy contracts**, **Initializable contracts**, **getStorageAt**, **delegatecall** and **selfdestruct**.\n\n1. Change the address in **scripts/motorbike.js** to your instance address\n2. Run `yarn hardhat run scripts/motorbike.js`\n\n## Useful reads:\n\n- [EVM Dialect](https://docs.soliditylang.org/en/v0.8.17/yul.html?highlight=evm%20dialect#evm-dialect)\n- [UUPS Proxies](https://forum.openzeppelin.com/t/uups-proxies-tutorial-solidity-javascript/7786)\n\n## About\n\n1. Gain access to Engine (Address is inside the IMPLEMENTATION_SLOT location)\n2. \"Initialize\" to make ourselves the \"upgrader\"\n3. Deploy a attacker contract with self destruct\n4. Call \"upgradeToAndCall\" to attacker contract\n5. Self destruct attacker contract\n\n[Watch how to solve it](https://www.youtube.com/watch?v=D7IfmkINYJ0) - [Another video](https://www.youtube.com/watch?v=WdiCzB3zjy0) - [Read how to solve it](https://dev.to/nvn/ethernaut-hacks-level-25-motorbike-397g)\n\n# 26 - Double Entry Point\n\n**You can read about it below**\n\n[Watch how to solve it](https://www.youtube.com/watch?v=aGnC_917YOY) - [Read how to solve it](https://dev.to/nvn/ethernaut-hacks-level-26-double-entry-point-1774) - [Another read](https://daltyboy11.github.io/every-ethernaut-challenge-explained/#doubleentrypoint) - [A GitHub repo!](https://github.com/maAPPsDEV/double-entry-point-attack)\n\n# 27 - Good Samaritan\n\nRead how to do it:\n\n- [Link 1](https://dev.to/erhant/ethernaut-27-good-samaritan-1cp6)\n- [Link 2](https://hackernoon.com/good-samaritan-the-new-ethernaut-ctf-challenge)\n- [Link 3](https://blog.dixitaditya.com/ethernaut-level-27-good-samaritan)\n- [Link 4](https://coinsbench.com/27-good-samaritan-ethernaut-6de5de92d3e3)\n- [Link 5](https://blog.blockmagnates.com/ethernaut-lvl-27-good-samaritan-walkthrough-custom-errors-in-solidity-17c7e20fb58a)\n\n# Troubleshooting\n\n## General errors\n\n- Check level indications to see if you **set the parameters right** for your instance.\n- **Did you save** after changing address to your instance?\n- Delete **\"artifacts\"** and **\"cache\"** folder and **run the command again**.\n\n## \"nonce too low\" / Pending transaction stuck:\n\nIf you get this error when submitting level instance, it's because you used some nonces to send the transactions that attack the level. In your MetaMask, go to **Settings \u003e Advanced \u003e Reset Account**\n\n## Don't have Goerli ETH\n\nGet some here: [Chainlink Faucet](https://faucets.chain.link/) - [Alchemy Faucet](https://goerlifaucet.com/)\n\n## Don't have yarn\n\n- Just enter `npm install --global yarn` on your console.\n- Don't have NPM either? [Go get the LTS champ!](https://nodejs.org/en/download/)\n- Don't know how to use it? Search how to :)\n\n## Don't know how to fill .env file\n\n- GOERLI_RPC URL: [Alchemy (step 1 and 2)](https://www.alchemy.com/overviews/private-rpc-endpoint)\n- PRIVATE_KEY: [Metamask](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key)\n- (optional) ETHERSCAN_API_KEY: [Etherscan](https://info.etherscan.com/api-keys/)\n\n## (optional) Use different network than Goerli:\n\n1. Add your network (i.e: **mumbai**) as **DefaultNetwork** on hardhat.config.js\n2. **Add it's parameters** in network section same as goerli is added.\n\n### (optional) Want to use Mumbai\n\n- MUMBAI_RPC_URL: [Alchemy (step 1 and 2)](https://www.alchemy.com/overviews/private-rpc-endpoint)\n- PRIVATE_KEY is the same\n- (optional) POLYGONSCAN_API_KEY: [Register and get one](https://polygonscan.com/apis)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluloxi%2Fethernaut-with-hardhat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluloxi%2Fethernaut-with-hardhat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluloxi%2Fethernaut-with-hardhat/lists"}