CLI tool
OpenZeppelin/ethernaut avatar
OpenZeppelin/ethernaut

Ethernaut, the Solidity wargame where every level is built to be hacked

Web3/Solidity based wargame

2,340 stars788 forksSolidityAGPL-3.0

At a glance

What is it?
OpenZeppelin's ethernaut repository is a Web3/Solidity based wargame played inside the Ethereum Virtual Machine, where each level is a smart contract that has to be hacked. Its toolchain is Foundry, while its setup text still describes ganache.
Who is it for?
Adopt ethernaut if you want Solidity practice on a chain you control, since `yarn network` gives you a local node and `yarn compile:contracts` builds the level contracts straight from the repo. Do not treat it as an audit checklist, because the README never enumerates the levels or names the bug behind each one.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Solidity, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • OpenZeppelin/ethernaut2,340 ★ · +1
Star history
Repository2026-10-132026-10-21
OpenZeppelin/ethernaut2.3k2.3k

1 Stars in the last 8 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Every level is a contract built to be hacked

Ethernaut is filed as a Web3/Solidity based wargame, inspired by overthewire and played inside the Ethereum Virtual Machine. Each level is a smart contract that needs to be hacked, and the same corpus doubles as a catalogue of historical hacks, so a level pairs a technique with the incident that made the technique famous. Levels do not have to be taken in any particular order, and the design leaves room for an unlimited number of them.

Who it is for is anyone learning Ethereum, where the barrier is a working Solidity toolchain rather than a reading list. The official version runs at ethernaut.openzeppelin.com. The repository declares no homepage field, so that address comes from the README text alone and is the only hosted instance named anywhere in the project. The last push was on 2026-09-23, and the repository is not archived.

The install needs a chain, a deployment and a client

A local run is split into three components: a testnet running on your machine, such as ganache, hardhat network or geth; a contract deployment that puts the levels onto that chain; and a React client reached on localhost:3000. The top level layout holds more than those three items, including client/, contracts/, deps/, netlify.toml, .env-sample, .nvmrc, renovate.json, .gitmodules and yarn.lock. Root package.json sets `private` to true and declares yarn workspaces covering client and deps/hljs, which makes deps/ a build input rather than a folder of copied files.

Two build paths exist and they differ in what they touch. `yarn build:ethernaut` compiles the contracts and then builds the client, so a production bundle needs a working Solidity compiler. `build:netlify` builds the client alone, which is the path that pairs with netlify.toml for a hosted frontend.

Cloning the repo, starting the chain, funding the wallet

Order matters here, because the wallet key comes out of the node output and not out of the repository. Step 0 asks you to pin Node first. Running `nvm use` at the root reads .nvmrc and selects the version the toolchain expects, which is the reason that file sits at the top level instead of inside client/.

Then clone and install:

bash
git clone [email protected]:OpenZeppelin/ethernaut.git
cd ethernaut
yarn install

Start the node next. `yarn network` runs anvil with a one second block time and impersonation enabled, and it prints the private keys that step 3 imports into Metamask.

bash
yarn network

Compilation is a separate step, and the first real checkpoint for a working Solidity setup.

bash
yarn compile:contracts

From there, set `client/src/constants.js` `ACTIVE_NETWORK` to `NETWORKS.LOCAL`, run `yarn deploy:contracts`, and start the client with `yarn start:ethernaut`. Each deployed level address lands in deploy.local.json, and the node answers on localhost:8545.

One constant decides between local JSON and Sepolia

The client does not discover a chain by itself. `client/src/constants.js` carries an `ACTIVE_NETWORK` key that you edit by hand, and the README gives two values: `NETWORKS.LOCAL` for the anvil chain and `NETWORKS.SEPOLIA` for the public testnet. Picking Sepolia changes what you skip, because the same steps apply except 2, 3 and 6, which drops the local node, the Metamask key import and the deployment.

Deployment state lives in two files. A local run writes deploy.local.json with one address per level, so the level addresses are readable without leaving the terminal. Sepolia deployments are hand edited in deploy.sepolia.json, a file the README calls a history of all level and contract instances, and a new instance is requested by adding an "x" entry to the array:

json
{
  "0": "x",
  "1": "0x4b1d5eb6cd2849c7890bcacd63a6855d1c0e79d5",
  "2": "0xdf51a9e8ce57e7787e4a27dd19880fd7106b9a5c",
  ...
},

Then `yarn deploy:contracts` runs. The prose calls that structure an array while the example keys its entries by level number, and the script reads the file through an experimental JSON module flag rather than a plain JSON parse, so the loader is part of what you need to understand.

Yarn script names hide a Foundry toolchain

Every developer facing command is a yarn script, but three of them are Foundry commands with the tool name moved one directory down:

json
"test:contracts": "cd contracts && forge test",
"compile:contracts": "cd contracts && forge build",
"network": "cd contracts && anvil --block-time 1 --auto-impersonate",

The README never uses the word Foundry, and that gap creates a concrete contradiction in the setup steps. Its component list offers ganache, hardhat network or geth as the local testnet, and step 3 tells you to import a private key from the ganache-cli output, while the script you actually run starts anvil. Follow the text literally on a machine with only ganache installed and you end up with no node answering on localhost:8545 and no key to paste into Metamask. The `--block-time 1` flag also asks the node to mine on a timer, so blocks arrive without a transaction driving them, which changes how a chain behaves while you are reading contract source.

Upgrade and supersede scripts the setup never mentions

package.json carries operations the README does not document: `upgrade:proxy` and `supersede:level` for the deployed levels, plus three leaderboard entry points named `leaderboard:oldCrawler`, `leaderboard:newCrawler` and `leaderboard:triggerNextCrawl`. Two crawl scripts with separate names, one for historical data and one for new data, indicate that leaderboard records were migrated at some point. Proxy upgrades and level supersession existing as commands tells you the published level set changes after deployment rather than staying frozen.

All of the deployment scripts share one shape. Each recompiles first and each hands node an experimental JSON modules flag:

json
"upgrade:proxy": "yarn compile:contracts && node --experimental-json-modules client/scripts/upgrade_proxy.mjs",
"supersede:level": "yarn compile:contracts && node --experimental-json-modules client/scripts/supersede_level.mjs",

That flag is the reason step 0 pins a Node version, and it is also a failure point. An interpreter without JSON module support rejects the import before any contract is touched, so the .nvmrc pin protects the first command of the run, not only the last one.

A patched highlight.js and an unfetched submodule

Syntax highlighting comes from a local build instead of the public registry. The root package.json depends on `highlight.js` at ^10.7.2 and adds a resolutions block that rewrites every copy in the tree to `10.7.2-local`, a version string that only exists if you build the `deps/hljs` workspace yourself. The same file lists `react-loader-spinner` at ^5.3.4, and yarn.lock is committed, so what you install is pinned to a recorded resolution rather than to the newest matching version.

`yarn install` covers the workspaces and nothing else. `.gitmodules` sits at the top level, which means at least one dependency arrives as a git submodule, and the README numbered list never mentions a submodule step. A fresh clone therefore leaves that dependency empty until you fetch it yourself. renovate.json is present as well, so pinned versions do get update proposals, but the lock file and the resolutions pin still decide what actually runs on your machine.

What the repository leaves to the reader

Several things a practitioner would look for are missing. The README does not enumerate the levels or name the bug behind each one, so you learn a level by reading its contract source in contracts/. It does not document a rollback for a level you already completed either: the "x" entry in deploy.sepolia.json is the nearest thing to a revocation, and no procedure is given for unmarking it. Nothing explains how the leaderboard crawlers authenticate or what they collect.

Licensing is equally short. The repository is licensed AGPL-3.0, and no other terms for level content are stated anywhere, so read LICENSE before redistributing a fork of the game. On approach, Ethernaut keeps the wargame shape the README credits to overthewire but moves it inside the Ethereum Virtual Machine, where the state under attack is contract storage and the level order is free. A hosted run at ethernaut.openzeppelin.com gives you fixed instances maintained by someone else; a local run gives you a chain you control and the deployed level addresses in deploy.local.json.

Editorial conclusion

Adopt ethernaut if you want Solidity practice on a chain you control, since `yarn network` gives you a local node and `yarn compile:contracts` builds the level contracts straight from the repo. Do not treat it as an audit checklist, because the README never enumerates the levels or names the bug behind each one. Before the first level, confirm that `nvm use` resolves a Node version your Forge install accepts, initialise the submodules that `yarn install` leaves alone, and check that `client/src/constants.js` points at the network your wallet keys can actually reach.

Frequently asked questions

What is Ethernaut and what do I need before playing it?

It is a Web3/Solidity based wargame played in the Ethereum Virtual Machine, where every level is a smart contract that needs to be hacked. Running it locally needs three pieces: a testnet on your machine, the contracts deployed onto it, and the React client served on localhost:3000.

Why does the Ethernaut setup mention ganache when the local node is anvil?

The README lists ganache, hardhat network and geth as options for the local testnet and says to import a private key from the ganache-cli output, while the `yarn network` script in package.json runs anvil with a one second block time. The README does not explain the difference or mention Foundry anywhere.

How do I run Ethernaut on Sepolia instead of a local chain?

Set `ACTIVE_NETWORK` to `NETWORKS.SEPOLIA` in client/src/constants.js. The README says the Sepolia path is the same as the local path except for steps 2, 3 and 6, which are the local node, the private key import and the deployment.

How do I add a new Sepolia instance of a level?

Add an "x" entry to the array in deploy.sepolia.json and then run `yarn deploy:contracts`. That file keeps the address history for every level and contract instance already deployed.

Is the ethernaut repository still worked on, and under which licence?

The last push was on 2026-09-23 and the repository is not archived. It is licensed AGPL-3.0 and has no GitHub releases.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. OpenZeppelin/ethernaut on GitHub
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/openzeppelin-ethernaut.svg)](https://hysenlabs.com/projects/openzeppelin-ethernaut)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/openzeppelin-ethernaut.svg)](https://hysenlabs.com/projects/openzeppelin-ethernaut)

Was this useful?