EVM Puzzles solutions
Intro
Source code with installation instructions and how to play can be found here.
I’ll use the evm.code website to debug the bytecode and for documentation references.
Puzzle 1
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 34 CALLVALUE
01 56 JUMP
02 FD REVERT
03 FD REVERT
04 FD REVERT
05 FD REVERT
06 FD REVERT
07 FD REVERT
08 5B JUMPDEST
09 00 STOP
Bytecode analysis:
CALLVALUE
gets the transaction value (in wei) sent to this contract and pushes it on top of the stackJUMP
jumps to the instruction at the offset value stored on top of the stack
Our goal is to send a transaction value (in wei) such that we reach the JUMPDEST
opcode and not revert the execution.
The JUMP
instruction will jump at a location equal to the transaction value sent (CALLVALUE
).
Since the instruction JUMPDEST
is at PC 08
, the value that must be stored on top of the stack when executing the JUMP
instruction is 8
.
In other words, we need to send 8
wei to solve this challenge.
Solution
|
|
evm.code bytecode solution challenge: link.
Puzzle 2
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 34 CALLVALUE
01 38 CODESIZE
02 03 SUB
03 56 JUMP
04 FD REVERT
05 FD REVERT
06 5B JUMPDEST
07 00 STOP
08 FD REVERT
09 FD REVERT
Bytecode analysis (opcodes unseen from previous challenges):
CODESIZE
pushes the size of the contract code in bytes on top of the stackSUB
pops the first two values from the stack, subtracts the second from the first one, and pushes the result
This puzzle has 10
instructions (each instruction is 1
byte), so the CODESIZE
will push 0a
on top of the stack when executed. SUB
will compute the difference between 0a
and the msg.value
(output of CALLVALUE
).
The JUMP
instruction will jump at the location returned by SUB
. Since the JUMPDEST
instruction is at location 6
, we need to solve the following:
where $x$ is the amount (in wei) sent to this contract (i.e. msg.value
).
If we convert the hex values to decimal, this translates to:
To reach the JUMPDEST
instruction, we need to send 4
wei.
Solution
|
|
evm.code bytecode solution challenge: link.
Puzzle 3
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 36 CALLDATASIZE
01 56 JUMP
02 FD REVERT
03 FD REVERT
04 5B JUMPDEST
05 00 STOP
Bytecode analysis (opcodes unseen from previous challenges):
CALLDATASIZE
pushes the size of thecalldata
on top of the stack
calladata
?The evm.codes website provide a simple and clear explanation (link):
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.
This puzzle is similar to Puzzle 1. The only difference is that the value pushed on top of the stack is not the msg.value
but the size of the calldata
. The JUMPDEST
instruction is at PC 04
, so we need to send a calldata
of size 4
bytes, for example, 0x01010101
.
Solution
|
|
evm.code bytecode solution challenge: link
Puzzle 4
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 34 CALLVALUE
01 38 CODESIZE
02 18 XOR
03 56 JUMP
04 FD REVERT
05 FD REVERT
06 FD REVERT
07 FD REVERT
08 FD REVERT
09 FD REVERT
0A 5B JUMPDEST
0B 00 STOP
Bytecode analysis (opcodes unseen from previous challenges):
XOR
pops the first two values from the stack, computes their bitwise XOR, and pushes the result
x y x^y
------------
0 0 0
0 1 1
1 0 1
1 1 0
CALLVALUE
pushes the msg.value
on top of the stack. The next instruction, CODESIZE
pushes the code size on top of the stack. We know this value will be 0c
(12
in decimal) since our puzzle code has 12
instructions. JUMP
will use the result from the XOR
instruction to jump to the next instruction. We want to reach the location of JUMPDEST
at PC 0a
(10
in decimal). It means the result of the XOR
instruction must be 0a
.
The only unknown is the CALLVALUE
(i.e. the msg.value
). The formula we want to compute,
where $x$ is the value returned by CALLVALUE
, is the following:
The amount of wei we need to send is 6
.
Solution
|
|
evm.code bytecode solution challenge: link
Puzzle 5
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 34 CALLVALUE
01 80 DUP1
02 02 MUL
03 610100 PUSH2 0100
06 14 EQ
07 600C PUSH1 0C
09 57 JUMPI
0A FD REVERT
0B FD REVERT
0C 5B JUMPDEST
0D 00 STOP
0E FD REVERT
0F FD REVERT
Bytecode analysis (opcodes unseen from previous challenges):
DUP1
duplicates the value on top of the stack and pushes it on top of the stackMUL
pops the first two values from the stack and pushes the result of the multiplication of these two numbersPUSH2
pushes two bytes on top of the stackEQ
pops the first two values from the stack and pushes1
if the values are equal,0
otherwisePUSH1
pushes one byte on top of the stackJUMPI
jumps to the instruction on top of the stack if the second value is different from0
The location of JUMPDEST
at PC 0c
(12
in decimal). This value is pushed on top of the stack by PUSH1 0C
. JUMPI
will jump at that location if the second value on top of the stack differs from 0
. It means the EQ
instruction must return 1
, which is possible only if the output of the MUL
instruction and PUSH2 0100
are equal. The output of MUL
is, in turn, computed by multiplying the value of msg.value
by himself (because of the DUP
instruction).
The formula we want to compute to obtain $1$ from EQ
instruction,
where $x$ is the value returned by CALLVALUE
, is the following:
The amount of wei we need to send is 16
(10
in hexadecimal).
Solution
|
|
evm.code bytecode solution challenge: link
Puzzle 6
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 6000 PUSH1 00
02 35 CALLDATALOAD
03 56 JUMP
04 FD REVERT
05 FD REVERT
06 FD REVERT
07 FD REVERT
08 FD REVERT
09 FD REVERT
0A 5B JUMPDEST
0B 00 STOP
Bytecode analysis (opcodes unseen from previous challenges):
PUSH1 00
pushes00
on top of the stackCALLDATALOAD
pops the offset of thecalldata
to read from the stack and pushes this data on top of the stack
The location of JUMPDEST
at PC 0a
. PUSH1 00
sets the offset that the CALLDATALOAD
instruction will use. Since we need to jump at location 0a
and the calldata
is read at offset 0
, we need to send a calldata
of 32
bytes with all 0
except for the right-most byte that will be the exact value of JUMPDEST
location, that is 0a
.
The calldata
we need to send is: 0x000000000000000000000000000000000000000000000000000000000000000A
.
Solution
|
|
evm.code bytecode solution challenge: link
Puzzle 7
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 36 CALLDATASIZE
01 6000 PUSH1 00
03 80 DUP1
04 37 CALLDATACOPY
05 36 CALLDATASIZE
06 6000 PUSH1 00
08 6000 PUSH1 00
0A F0 CREATE
0B 3B EXTCODESIZE
0C 6001 PUSH1 01
0E 14 EQ
0F 6013 PUSH1 13
11 57 JUMPI
12 FD REVERT
13 5B JUMPDEST
14 00 STOP
Bytecode analysis (opcodes unseen from previous challenges):
CALLDATACOPY
copies transaction data (calldata
) intomemory
. It uses3
values from the stack:destOffset
is the bytes offset inmemory
where thecalldata
data will be copiedoffset
is the bytes offset of thecalldata
from where we want to start copying the datasize
is the byte size of thecalldata
to be copied inmemory
CREATE
is used to create a new contract. LikeCALLDATACOPY
, it uses3
values from the stack:value
is the amount of wei we want to send to the contactoffset
is the bytes offset in thememory
where to start coping contract codesize
is the size of the initialization code (i.e. the bytes we want to copy frommemory
starting from theoffset
). This instruction pushes on top of the stack the address of the contract deployed or0
if it fails
EXTCODESIZE
pops a 20-bytes address from the stack; as a result, it pushes the byte size of the contract code
JUMPDEST
is at PC 13
(value pushed on the stack by PUSH1 13
). JUMPI
is used to jump to the JUMPDEST
location if the value of the second element on the stack is different from 0
. This value is the result of the following OP codes:
PC OPCODE NAME
-----------------------
0B 3B EXTCODESIZE
0C 6001 PUSH1 01
0E 14 EQ
0F 6013 PUSH1 13
EQ
compares 01
and the output of EXTCODESIZE
and returns 1
if they are equal, 0
otherwise. We want this value to be different from 0
, so the output of EXTCODESIZE
must be 1
.
EXTCODESIZE
reads the 20 bytes address returned by CREATE
and returns the byte size of that contract code. So we need to create a contract that, when deployed, returns only 1
instruction (1
bytecode size).
We can use the following code that uses the RETURN
instruction to return only 1
instruction: 0x60016000F3
:
OPCODE NAME
-----------------------
6001 PUSH 01 (size of the data we want to copy)
6000 PUSH 00 (where we want to start copying the data from memory)
F3 RETURN
RETURN
pops two values from the stack:
- bytes
offset
where we want to start copying the data from memory - bytes
size
of the data we want to copy
Since the memory
is initialized to 0
by default (docs), in the above code, the RETURN
instruction will return 1
byte from memory (starting at offset 0
), that is 00
that corresponds to the STOP
instruction.
Solution
|
|
evm.code bytecode solution challenge: link
Puzzle 8
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 36 CALLDATASIZE
01 6000 PUSH1 00
03 80 DUP1
04 37 CALLDATACOPY
05 36 CALLDATASIZE
06 6000 PUSH1 00
08 6000 PUSH1 00
0A F0 CREATE
0B 6000 PUSH1 00
0D 80 DUP1
0E 80 DUP1
0F 80 DUP1
10 80 DUP1
11 94 SWAP5
12 5A GAS
13 F1 CALL
14 6000 PUSH1 00
16 14 EQ
17 601B PUSH1 1B
19 57 JUMPI
1A FD REVERT
1B 5B JUMPDEST
1C 00 STOP
Bytecode analysis (opcodes unseen from previous challenges):
SWAP5
swaps the value at the top of the stack with the value at position5
(i.e.1 0 0 0 0 0 2
->2 0 0 0 0 0 1
)GAS
pushes the gas remaining after this instructionCALL
calls a contract at a given address. It returns0
if the call succeeds,0
otherwise. It accepts different values:gas
is the gas to send during the calladdress
is the address where the call will execute the call.value
is the amount of wei sentargsOffset
is the byte offset in the memory in bytesargsSize
is the byte size to copyretOffset
is the byte offset in the memory in bytes, where to store the return dataretSize
is the byte size to copy
Let’s do the same reasoning as the previous challenge. JUMPDEST
is at PC 1B
(value pushed on the stack by PUSH1 1B
). JUMPI
is used to jump to the JUMPDEST
location if the value of the second element on the stack is different from 0
. This value is the result of the following OP codes:
PC OPCODE NAME
-----------------------
13 F1 CALL
14 6000 PUSH1 00
16 14 EQ
17 601B PUSH1 1B
EQ
compares 00
and the output of CALL
and returns 1
if they are equal, 0
otherwise. We want this value to be different from 0
, so the output of CALL
must be 00
. It means we want the call to our contract to revert (CALL
must return 0
).
To create a contract that returns the REVERT
instruction, we can use the following bytecode (similar to the previous challenge):
OPCODE NAME
-----------------------
60FD PUSH FD (REVERT instruction)
6000 PUSH 00 (offset in memory)
53 MSTORE8 (stores REVERT instruction starting at offset `00`)
6001 PUSH 01 (size of the data we want to copy)
6000 PUSH 00 (where we want to start copying the data from memory)
F3 RETURN
The first three instructions above push the FD
instruction and load it into memory. The REVERT
instruction will be the code of our contract. When the contract is called, it will revert; thus, the return value from the CALL
instruction will be 0
.
Solution
|
|
evm.code bytecode solution challenge: link
Puzzle 9
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 36 CALLDATASIZE
01 6003 PUSH1 03
03 10 LT
04 6009 PUSH1 09
06 57 JUMPI
07 FD REVERT
08 FD REVERT
09 5B JUMPDEST
0A 34 CALLVALUE
0B 36 CALLDATASIZE
0C 02 MUL
0D 6008 PUSH1 08
0F 14 EQ
10 6014 PUSH1 14
12 57 JUMPI
13 FD REVERT
14 5B JUMPDEST
15 00 STOP
Bytecode analysis (opcodes unseen from previous challenges):
LT
pops two values from the stack and returns1
ifa < b
,0
otherwise, wherea
is the element at the top of the stack
First, we need to reach the JUMPDEST
at PC 09
. PUSH1 09
pushes this location on top of the stack, and JUMPI
is used to jump to that instruction. To succeed, CALLDATASIZE
must be greater than 03
, so LT
will return 1
.
The first condition we need to satisfy is:
where $x$ is the CALLDATASIZE
.
Then, we need to reach theJUMPDEST
at PC 14
. This value is pushed by PUSH1 14
. For JUMPI
instruction to jump to that instruction, EQ
must return 1
. EQ
compares 08
with the product between CALLVALUE
and CALLDATASIZE
.
The second condition we need to satisfy is:
where $x$ is the CALLDATASISE
, and $y$ is the CALLVALUE
.
To summarize, we need to solve the following:
The values that satisfy the above are:
We need to send 2
wei and a calldata
of 4
bytes (for example, 01010101
).
Solution
|
|
evm.code bytecode solution challenge: link
Puzzle 10
evm.code bytecode challenge: link.
PC OPCODE NAME
-----------------------
00 38 CODESIZE
01 34 CALLVALUE
02 90 SWAP1
03 11 GT
04 6008 PUSH1 08
06 57 JUMPI
07 FD REVERT
08 5B JUMPDEST
09 36 CALLDATASIZE
0A 610003 PUSH2 0003
0D 90 SWAP1
0E 06 MOD
0F 15 ISZERO
10 34 CALLVALUE
11 600A PUSH1 0A
13 01 ADD
14 57 JUMPI
15 FD REVERT
16 FD REVERT
17 FD REVERT
18 FD REVERT
19 5B JUMPDEST
1A 00 STOP
Bytecode analysis (opcodes unseen from previous challenges):
GT
pops two values from the stack and returns1
ifa > b
,0
otherwise, wherea
is the element at the top of the stackSWAP1
swaps the first two values from the stackMOD
computes the integer moduloa % b
. If the denominator is0
, the result will be0
.ISZERO
pops the value from the stack and pushes1
if the value is0
,0
otherwise
First, we need to reach the JUMPDEST
at PC 08
. PUSH1 08
pushes this location on top of the stack, and JUMPI
is used to jump to that instruction. To succeed, GT
must return 1
, which is possible if CODESIZE > CALLVALUE
. We know that the CODESIZE
is 1b
(there are 27
instructions).
The first condition we need to satisfy is:
where $x$ is the CALLVALUE
.
Then, we need to reach the JUMPDEST
at PC 19
. The result of ISZERO
must be different from 0
(this value is used by JUMPI
), and this is possible if CALLDATASIZE % 3 = 0
. Then, 0A
is pushed on top of the stack ad added to the CALLVALUE
. We want this sum to be 19
, so CALLVALUE + A0 = 19
.
Here are the conditions we need to satisfy:
where $y$ is the CALLDATASIZE
.
To summarize, we need to solve the following:
The values that satisfy the above are:
The CALLDATASIZE
can have multiple values as long the result from CALLDATASIZE mod 3
is zero (0 mod 3 = 0
, 3 mod 3 = 0
, 6 mod 3 = 0
, etcc).
To solve this challenge, we can send 15
wei and a calldata
of 3
bytes (for example, 010101
), or we can omit the calldata
(this way, 0 mod 3
will still return 0
).
Solution
15
wei andcalldata
of3
bytes
|
|
evm.code bytecode solution challenge: link
15
wei and nocalldata
|
|
evm.code bytecode solution challenge: link