This is Part 3 of the “Let’s play EVM Puzzles” series, where I will explain how to solve each puzzle challenge.
EVM Puzzles is a project developed by Franco Victorio (@fvictorio_nan) that is a perfect fit if you are in the process of learning how the Ethereum EVM works, and you want to apply some of the knowledge you have just acquired.
EVM Puzzle 3
00 36 CALLDATASIZE
01 56 JUMP
02 FD REVERT
03 FD REVERT
04 5B JUMPDEST
05 00 STOP
The problem is similar to the Puzzle 1 and Puzzle 2 challenges, where we need to find a way to have in the EVM Stack the correct value when the JUMP
opcode is executed. We need to have into the stack the value 4
to land in a valid JUMPDEST
opcode.
In this puzzle, we have only one opcode before the JUMP
- CALLDATASIZE push the byte size of the calldata
To solve this challenge, it’s important to understand what the calldata
is.
From Chapter 13 — The Ethereum Virtual Machine
The call data region is the data that is sent with a transaction. In the case of contract creation, it would be the constructor code. This region is immutable and can be read with the instructions CALLDATALOAD, CALLDATASIZE, and CALLDATACOPY.
Instead, from OpenZeppelin blog post “Deconstructing a Solidity Contract — Part III: The Function Selector” the calldata
is explained like:
As explained in Solidity’s documentation ABI specification, the calldata is an encoded chunk of hexadecimal numbers that contains information about what function of the contract we want to call, and it’s arguments or data. Simply put, it consists of a “function id”, which is generated by hashing the function’s signature (truncated to the first leading four bytes) followed by the packed arguments data.
If for example we want to interact with a Contract to withdraw 10 WETH
we would call the contract’s function that have this signature: withdraw(uint256)
. The calldata value for that call would be 0x2e1a7d4d0000000000000000000000000000000000000000000000008ac7230489e80000
which the 4 first bytes represents the function signature, the other 32 would represent the uint256 parameter value passed to it.
Just try to print this in your Solidity contract to reproduce it: abi.encodeWithSignature(“withdraw(uint256)”, 10 ether);
Solution
The solution in this challenge is pretty easy, we just need to pass 4 bytes input value to make the JUMP
op to jump to the JUMPDEST
destination.
For example, we could pass the 4 bytes that represent the signature of the function we have used in the example above. In this case, we would pass 0x2e1a7d4d
to solve the challenge.
Here’s the link to the solution of Puzzle 3 on EVM Codes website to simulate it.