Magic Number

To solve this level, you only need to provide the Ethernaut with a Solver, a contract that responds to whatIsTheMeaningOfLife() with the right number.

Easy right? Well… there’s a catch.

The solver’s code needs to be really tiny. Really reaaaaaallly tiny. Like freakin’ really really itty-bitty tiny: 10 opcodes at most.

Hint: Perhaps its time to leave the comfort of the Solidity compiler momentarily, and build this one by hand O_o. That’s right: Raw EVM bytecode.

Good luck!

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MagicNum {

  address public solver;

  constructor() {}

  function setSolver(address _solver) public {
    solver = _solver;



I recommand using this playground to debug what your are doing.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "forge-std/console.sol";

interface Solver {
  function whatIsTheMeaningOfLife() external returns(uint);

interface MagicNum {
  function solver() external returns(address);
  function setSolver(address _solver) external;

contract POC is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        address addr = vm.envAddress("INSTANCE_18");


        MagicNum magicNum = MagicNum(addr);

        address deployedAddress;

        assembly {
          let ptr := mload(0x40)
          let paddedBytes := shl(mul(8, 13), 0x69602a60005260206000f3600052600a6016f3)
          mstore(ptr, paddedBytes)
          deployedAddress := create(0, ptr, 19)




There are two parts in this byte code : 0x69{602a60005260206000f3}600052600a6016f3 . The part inside the curly braces {} correspond to the part that will be deployed by the contract constructor (which is the part outside the curly braces).

Each two digit byte corresponnd to an op code + its following parameters if expected.


Then if we call the return value: