Ethernaut is an (awesome) browser game that let’s you practice Web3 and Solidity whilte solving different tasks. The game consists of 23 levels (as of August 2021) and each level is focused on some Solidity feature or bug, smart contract design flaw, or unexpected behaviour of a contract. Your goals in each level is to hack it: to find a way of becoming a contract owner, to transfer all tokens to your own address, or abuse a feature of Solidity that wasn’t taken into consideration by a smart contract developer.
This is game a rare opportunity to get some real practical skills if you already know some basics of Web3 and Solidity. This is also a good opportunity to deepen your knowledge of Solidity because some levels are very hardcore – you won’t solve them without diving into the internals of Solidity and EVM.
In this blog post, I’ll tell you about my experience solving the game and will give you some hints for each of the levels. If want to see complete solutions, you can find them on GitHub. Just remember: there’s more than one way of solving the levels, finding your own solutions might be more benefitial to you than simply solving the game.
To play Ethernaut, you’ll have to have some basic skils, like:
awaitor how to get results from Promises.
- Solidity. You need to know Solditiy syntax and have experience writing simple contracts. A good way to get the basics is CryptoZombies and the official documentation.
Nevertheless, don’t set the bar too high! Give it a try and learn while playing! This is what the game was created for.
Even though Ethernaut is a browser game, you’ll want to reproduce levels on your computer to better understand them, test ideas, and quickly iterate with solutions. That’s why you’ll need a tool to make smart contracts development as easy and comfortable as possible.
Hardhat is the best Solidity development tool at the moment. It comes with a template to set up a Solidity project and frees you from the need to run and manage a local Ethereum network. It just works! Also, Hardhat has a configured and transparent testing environment, which allows you to iterate super fast compared to other development environments.
One extremely useful feature of Hardhat is
hardhat/console.sol contract that allows you print to the console from
smart contracts! It provides
console.log() function in your Solditiy code, as well as other functions to print
different data types (e.g.
console.logBytes32()). With this function, it’ll be much easier for you to understand
what happens in a contract and find out if something goes wrong.
Remix IDE is a browser IDE for writing and deploying smart contracts. The fact that it runs in browser and that it’s connected to MetaMask allows you to deploy contracts in a couple of clicks. Everyone uses Remix. Spend some time to get comfortable with it, this will pay back.
Whenever you see Solidity code:
- Read it.
- Ensure that you understand every line.
- Re-read it.
- Try to see the whole picture.
- Try to see if there’s something wrong.
0. Hello Ethernaut
This is an introductory level, it’s goal is to teach you how to use MetaMask to connect to a test network and how to interact with Ethernaut contracts using your browser’s developer console.
My solution: follow the instructions.
The smart contract has a logical flaw that allows anyone to become an owner and withdraw all funds. Find it.
- Learn about the
- While this is not relevant to this level, learn about the
fallback()function as well. This will help in the future.
- Learn how to call contracts with Web3.js and how to send ether allon with contract calls.
My solution: call
contribute() and send some ethers to become a contributor, send some ethers to the contract
to become an owner, call
withdraw() to withdraw all funds.
There’s a typo in the code, find it and use it to become an owner.
My solution: a single transaction sent via Web3.js.
3. Coin Flip
This is where things are getting nastier. 😈
- Read the code.
- Learn about the maximum value of
- Try to understand the idea behind using that big number.
- Realize that you always know current block number and hash.
My solution: I first tried soliving it via Web3.js but that was dull because there’s no guarantee that my tx gets into the next block. Then I’ve deployed it as a Solidity contract (there’s no code, sorry) and it was also dull but more reliable.
Learn the difference between
Learn about integer overflow and underflow and see if the contract is vulnerable.
Learn how contracts are called from other contracts and the difference between
Also, remember that
delegatecall is always a red flag.
If you’re doing this via Web3.js, you’ll need to know how to use custom ABI to call contract methods that are not defined in the contract’s ABI. Another way of achieving the same goals is by using the low level call function, but it’ll be harder.
My solution: Web3.js
You need to send ethers to a contract that doesn’t allow to do that. Here’s a hint: learn about selfdestruct.
Everything on blockchain is public even if it’s marked as
private in Solidity. Ethereum JSON-RPC and Web3.js provide
a way to read any value stored in a contract: getStorageAt.
My solution: a single call to
getStorageAt via Web3.js.
This level requires deploying a contract. The goal is to break the contract so it cannot be used after you. You’ve probably already learned about the fallback function and what happenes when you send ethers to a contract that doesn’t implement it.
Re-entrancy attacks are probably the most common ones. Here are some useful links to learn about them:
- Re-entrancy from the official Solidity documentation.
- Re-entrancy by example
- Checks-effects-interactions, a pattern to prevent re-entrancy attacks.
After studying these resources you’ll be able to spot the vulnerability.
You need to implement a contract that returns different results when its function is called with the same argument. The easiest way of achieving that is via some internel state.
This is an advanced version of the
Vault level where you need to read an element of a private array that’s not the
first state variable of a contract.
These links will help you:
My solution: a single call to
getStorageAt via Web3.js.
13. Gatekeeper One
To solve this level you need to solve three sub-tasks:
gateOnerequires you to use a contract to solve this level.
gateTworequires the amount of gas left in the transaction to satisfy a condition. This is the trickiest part. To solve it, learn about the way of limiting gas in contract calls.
gateThreerequires a key that satisfies three conditions. This is not hard as soon as you learn about type conversions.
Notice that my solution will work only in the Hardhat environment because the gate key is derived from the address you’re sending the transaction from.
14. Gatekeeper Two
This levels is the same as the previous one but it’s a little bit trickier.
Similarly to Gatekeeper One,
gateOne requires the call to be made from a contract.
gateTwo now requires the caller to have no code, which kind of contradicts to
You need to find out if and when Solidity
smart contract has no code.
15. Naught Coin
This is a simple level where you need to transfer tokens to a different address when
transfer method is not available.
To solve it, you need to read through the ERC-20 EIP and see if transferring is
This is my favorite level and it’s mind blowing. I highly recommend you solving this level by yourself. For that, you’ll need to learn about:
The correct way of solving this level is to learn how contract address is generated when contract is deployed , use Etherscan to find the nonce, and to reconstruct the address.
My solution: This is the only level where I cheated. I used Etherscan to find the transaction where the level was deployed and the token contract was created. I then extracted the token contract address from that transaction. 🤷♂️
This is one of the most interesting levels because you have to go beyond Solidity to solve it. While level description says you need to write EVM bytecode, there’s in fact an easier way – Yul.
- Refer to the ERC-20 example to figure out the minimal structure of the contract and notice that you’ll need the deploying part in your tiny contract.
- Use solc-select to install and switch between different versions of Solidity compiler.
- I used this to compile my contract:
$ solc --strict-assembly --optimize contracts/MagicNumber.yul
- Use sendTransaction to deploy bytecode.
19. Alien Codex
In my opinion, this is the most controversial level because you need to abuse a bug in an older Solidity version. This level is a pain in the ass. It really hurts. But it’s worth it.
In an older version of Solidity (this level uses Solidity 0.5.0) there was a bug that allowed to change the length
of an array without adding or removing elements. It literary looked liked
The idea of this level is to abuse this bug to overwrite a private state variable (
owner) that has no public setter.
This levels teaches you how to break some functionality of a contract that makes external calls. You need to build a contract that, when called, causes a transaction to fail.
This is a tricker version of Elevator. The difference is that the external function that’s called has
which doesn’t allow your contract to modify internal state. Also, this levels sets a tight limit of gas available to
However, the solution is much more straightforward.
The goal is to hack a decentralized exchange and withdraw its liquidity to your address. It’ll help a lot to learn about the basics of DEXes, for example by studying how Uniswap works. After that, it won’t be a problem to spot an implementation flaw in the contract.
And that’s it! I hope this writeup was helpful and you learned a lot from this game!