EVM Puzzle 7 & 8: Advanced Memory Operations
Introduction
In this post, we'll tackle Puzzles 7 and 8, which introduce more advanced EVM concepts including memory operations and complex data manipulation. These puzzles will test your understanding of how the EVM handles data storage and retrieval.
Puzzle 7
Copy and Paste → 36600080373660006000F03B600114601357FD5B00 into the EVM Playground. Then click RUN.
# Puzzle 7 # Goal: # Make it to the JUMPDEST at position 13 # # The contract's code is: # # [00] CALLDATASIZE # Get size of calldata # [01] PUSH1 00 # Push 0 onto stack # [03] DUP1 # Duplicate 0 # [04] CALLDATACOPY # Copy calldata to memory # [05] CALLDATASIZE # Get size of calldata again # [06] PUSH1 00 # Push 0 onto stack # [08] PUSH1 00 # Push 0 onto stack # [0A] CREATE # Create new contract # [0B] EXTCODESIZE # Get size of created contract # [0C] PUSH1 01 # Push 1 onto stack # [0E] EQ # Check if sizes are equal # [0F] PUSH1 13 # Push 19 (0x13) onto stack # [11] JUMPI # Conditional jump # [12] REVERT # Revert if jump fails # [13] JUMPDEST # Success destination # [14] STOP # Halt execution # # Bytecode: 0x36600080373660006000F03B600114601357FD5B00
Understanding the Puzzle
This puzzle introduces contract creation and code size comparison. Let's break down what's happening:
- The code first gets the size of our calldata
- It copies our calldata to memory starting at position 0
- It then tries to create a new contract using that calldata as the contract's code
- Finally, it checks if the new contract's code size equals 1
| Location | Opcode | Meaning | Stack After |
|---|---|---|---|
| 00 | CALLDATASIZE | Get size of calldata | [size] |
| 01 | PUSH1 00 | Push 0 onto stack | [0, size] |
| 03 | DUP1 | Duplicate top stack item | [0, 0, size] |
| 04 | CALLDATACOPY | Copy calldata to memory | [] |
| 05 | CALLDATASIZE | Get size of calldata | [size] |
| 06 | PUSH1 00 | Push 0 onto stack | [0, size] |
| 08 | PUSH1 00 | Push 0 onto stack | [0, 0, size] |
| 0A | CREATE | Create new contract | [addr] |
| 0B | EXTCODESIZE | Get contract size | [size] |
| 0C | PUSH1 01 | Push 1 onto stack | [1, size] |
| 0E | EQ | Check equality | [result] |
| 0F | PUSH1 13 | Push jump dest | [0x13, result] |
| 11 | JUMPI | Conditional jump | [] |
Puzzle 8:
CREATE and CALL
Puzzle 8 combines contract creation with contract interaction, requiring us to create a contract and then call it successfully.
Copy and Paste → 36600080373660006000F0600080808080945AF1600014601B57FD5B00 into the EVM Playground. Then click RUN.
# Puzzle 8 # Goal: # Create a contract and call it successfully 00 36 CALLDATASIZE 01 60 00 PUSH1 00 03 80 DUP1 04 37 CALLDATACOPY 05 36 CALLDATASIZE 06 60 00 PUSH1 00 08 60 00 PUSH1 00 0A F0 CREATE 0B 60 00 PUSH1 00 0D 80 DUP1 0E 80 DUP1 0F 80 DUP1 10 80 DUP1 11 86 SWAP5 12 5A GAS 13 F1 CALL 14 60 00 PUSH1 00 16 14 EQ 17 60 1B PUSH1 1B 19 57 JUMPI 1A FD REVERT 1B 5B JUMPDEST 1C 00 STOP # Bytecode: 36600080373660006000F0600080808080945AF1600014601B57FD5B00
Understanding the Puzzle
This puzzle introduces two new opcodes: CREATE and CALL. Let's break down what each does:
| Location | Opcode | Meaning | Operation | Stack After |
|---|---|---|---|---|
| 00 | CALLDATASIZE | Get size of calldata | Push size of calldata | [size] |
| 01 | PUSH1 00 | Push 0 onto stack | Push 0 | [0, size] |
| 03 | DUP1 | Duplicate the top stack item | Dup size | [size, size] |
| 04 | CALLDATACOPY | Copy calldata to memory | Copy size bytes from calldata to memory | [] |
| 05 | CALLDATASIZE | Get size of calldata | Push size | [size] |
| 06 | PUSH1 00 | Push 0 onto stack | Push 0 | [0, size] |
| 08 | PUSH1 00 | Push 0 onto stack | Push 0 | [0, 0, size] |
| 0A | CREATE | Create new contract | Create contract with size bytes from memory | [address] |
| 0B | PUSH1 00 | Push 0 onto stack | Push 0 | [0, address] |
| 0D | DUP1 | Duplicate top stack item | Dup 0 | [0, 0, address] |
| 0E | DUP1 | Duplicate top stack item | Dup 0 | [0, 0, 0, address] |
| 0F | DUP1 | Duplicate top stack item | Dup 0 | [0, 0, 0, 0, address] |
| 10 | DUP1 | Duplicate top stack item | Dup 0 | [0, 0, 0, 0, 0, address] |
| 11 | SWAP5 | Swap top with 6th item | Swap address to top | [address, 0, 0, 0, 0, 0] |
| 12 | GAS | Get remaining gas | Push gas | [gas, address, 0, 0, 0, 0] |
| 13 | CALL | Call contract | Call contract with 0 value | [success] |
| 14 | PUSH1 00 | Push 0 onto stack | Push 0 | [0, success] |
| 16 | EQ | Check if equal | Check if success = 0 | [result] |
| 17 | PUSH1 1B | Push 27 onto stack | Push 27 | [27, result] |
| 19 | JUMPI | Conditional jump | Jump if result is 1 | [] |
| 1A | REVERT | Revert transaction | FAIL | [] |
| 1B | JUMPDEST | Valid jump destination | SUCCESS! | [] |
| 1C | STOP | Stop execution | - | [] |
Key Takeaways
- Contract creation can be done dynamically using calldata
- The CREATE opcode uses memory contents as the new contract's code
- Contract calls can be used to test contract behavior
- Return values from contract calls can be checked
Common Pitfalls
- Forgetting that created contract code must be valid EVM bytecode
- Not accounting for gas costs in contract creation and calls
- Creating contracts that revert instead of returning successfully
- Misunderstanding how the CALL opcode affects the stack
Practice Exercises
- Can you create a contract that returns a specific non-zero value?
- Try creating a contract that performs a calculation before returning
- Experiment with different ways to make contract calls fail or succeed