Do you wanna play a game?

This small game is intended as a challenge that you have to solve to earn a ticket for an application at MaibornWolff.

Whether you're here to apply for a position or just for fun, this page will tell you how to play.
If you know the Ethernaut by OpenZeppelin you will see that we were heavily inspired by this wonderful game.
So it's very similiar in the way it is played.

Requirements

You will need to install Metamask to play this game.
Get it at: https://metamask.io/.

Metamask only runs on either Chrome or Firefox, so use either of those browsers to play.

Setup

Connect Metamask to the Ropsten testnet and get some testether.
You can get some at this faucet: faucet.ropsten.be:3001/ or this one: https://ipfs.io/ipfs/QmVAwVKys271P5EQyEfVSxm7BJDKWt42A2gHvNmxLjZMps/.

Browser console

Almost all of the game is played via the browser console. Open it up by either right clicking and selecting "Show Source" or under View -> Developer -> Toggle Developer Tools

Most variables needed can be access through ethernautilus or just en for short. Try it out now and have a look at the possible options.

Goals

Each level will have different goals. Make sure to fulfill them all.
If you plan to apply at MaibornWolff make sure to complete all levels with the same account.

Interacting with the levels

First you will have to request an instance of the level with the button below.

Congrats

You finished the first level. Well, level 0 actually.
Now you're ready to start for real.

You can try it now.
The Ethernautilus contract will then deploy a small Intro level which we will use to show you around.

If you already know what you are doing and are not trying to apply, you won't need to complete this part. ;)

The deployed contract looks like this:

pragma solidity ^0.4.18; contract Intro { address owner; function() public payable { owner = msg.sender; } function Intro() public { owner = msg.sender; } function withdraw() public { owner.transfer(this.balance); } }

The abi will already be loaded into the en.contract variable.
Type it into the console to see what it looks like. The contract address will be in the en.level variable.

In the en.player variable you will find your own address. Magic. You can use it, for example to get your own balance. Just type web3.eth.getBalance(en.player) into the console and you will receive a promise with your balance.
Use await before the call to receive a cleaner display of the value.

Try out some commands. For example:

  1. await en.contract.methods.owner().call()
  2. await web3.eth.getBalance(en.level)
  3. web3.eth.sendTransaction({from:en.player,to:en.level,value:100000})
  4. await web3.eth.getBalance(en.level)
  5. await en.contract.methods.owner().call()
  6. en.contract.methods.withdraw().send({from:en.player})
  7. await web3.eth.getBalance(en.level)

After you have taken Ownership of the contract and emptied its balance, you can submit the level with the Send Instance button above.

Now that you know what to do, start for real and have some fun.
You can continue with NumberGames from the tabs above.

Other useful tools

Burning money might be useful.

This contract allows you to get rid of unneeded ether.
It even prevents you from accidentally sending some.

Goals

  • Become owner
  • revert would have been easier

Congrats!

This contract demonstrates two important aspects.

  1. Constructors are important
  2. It's not always a reentrancy

That's it for now.

pragma solidity ^0.4.18; //Simple contract for buring ether or anything really contract Burn { address public owner; function burn() public payable{ owner = msg.sender; } //call this to burn any amount of ether function burnEther() public payable { } //Fallback function for unintended sends function () public payable { msg.sender.call.value(msg.value)(); } }

A simple onchain wallet.

This wallet is used to store ether for multiple accounts.
It would be really useful, if not for this simple flaw.

Goals

  • Set the balance of the contract to 0
  • Integers have a MAX_SIZE

Congrats!

This contract demonstrates two important aspects.

  1. Integers can under and overflow
  2. checking user supplied variables is important

The next challenge is already waiting.

pragma solidity ^0.4.18; import "./imports/zeppelin-solidity/Ownable.sol"; //Simple onchain Wallet contract NumberGames is Ownable { //Mapping for multiple wallets in on Contract mapping (address => uint256) public wallet; //Constructor function NumberGames() public payable { wallet[msg.sender] = msg.value; } //Add funds to personal Wallet function addFunds(uint256 value) public payable { require(value <= msg.value); wallet[msg.sender] += value; } //Withdraw Funds from personal Wallet function withdrawFunds(uint256 value) public { uint256 newBalance = wallet[msg.sender] - value; //Check if substraction was correct require(wallet[msg.sender] == newBalance + value); //Prevents certain overflows, but you have to withdraw atleast half of your wallet require(newBalance <= value); wallet[msg.sender] = newBalance; msg.sender.transfer(value); } }

Contracts can be bought.

This contract is buyable, you just need to tell it which token you will be buying it with.
But it's not cheap.

Goals

  • Buy the ownership of the contract
  • Use an ERC20
  • Just build your own

Congrats!

This contract demonstrates two important aspects.

  1. It's not all about exploiting
  2. Building tokens is important

The next challenge is already waiting.

pragma solidity ^0.4.18; import "./imports/zeppelin-solidity/Ownable.sol"; import "./imports/zeppelin-solidity/ERC20Basic.sol"; contract BuyMe is Ownable { address public tokenAddress; uint256 public price = 10000000000000000; function BuyMe() public { } function setTokenContract(address _tokenAddress) public { tokenAddress = _tokenAddress; } function buyOwnership() public { ERC20Basic instance = ERC20Basic(tokenAddress); require(instance.balanceOf(this) >= price); require(instance.totalSupply() >= price); owner = msg.sender; } }