1 of 59

2 of 59

�Smart Contract Team Learn Ups�EVM deep dive with Huff

Smart Contract Engineering��r4bbit (@0x_r4bbit)�Smart Contract Engineer at Vac

_

3 of 59

EVM & Opcodes

_

4 of 59

�The EVM is a stack-based computer, responsible for executing smart contract instructions. ��These instructions are known as opcodes.

EVM & Opcodes

_

5 of 59

What is a vault anyway?

_

6 of 59

What is a vault anyway?

_

7 of 59

What is a vault anyway?

_

8 of 59

9 of 59

10 of 59

11 of 59

12 of 59

13 of 59

14 of 59

15 of 59

16 of 59

TODO: huff website picture

17 of 59

Huff Basics

_

18 of 59

Huff Basics

_

#define function addTwo(uint256, uint256) view returns (uint256)��

19 of 59

Huff Basics

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {������}

20 of 59

Huff Basics

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack�����}

21 of 59

Huff Basics

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0x20 calldataload // load second 32 bytes onto stack����}

22 of 59

Huff Basics

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0x20 calldataload // load second 32 bytes onto stack� add // add number 1 and 2 and put result on stack���}

23 of 59

Huff Basics

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0x20 calldataload // load second 32 bytes onto stack� add // add number 1 and 2 and put result on stack� 0x00 mstore // place result in memory��}

24 of 59

Huff Basics

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0x20 calldataload // load second 32 bytes onto stack� add // add number 1 and 2 and put result on stack� 0x00 mstore // place result in memory� 0x20 0x00 return // return the result}

25 of 59

Huff Basics

_

�� huffc src/addTwo.huff -b� → 600c8060093d393df35f35602035015f5260205ff3������Try on evm.codes

26 of 59

Contract Creation

_

27 of 59

Contract creation

_

PUSH1 0c // push value 12 onto stack (runtime code length)�DUP1 // duplicate value�PUSH1 09 // push value 9 onto stack (offset of runtime code)�RETURNDATASIZE // push value 0 onto stack�CODECOPY // copy runtime code to memory�RETURNDATASIZE // push value 0 onto stack�RETURN // return runtime code��

28 of 59

Contract creation

_

Returned runtime code:�5f35602035015f5260205ff3PUSH0 // push value 0 onto stack�CALLDATALOAD // load 32 bytes of calldata�PUSH1 20 // push value 32 onto stack�CALLDATALOAD // load 32 bytes of calldata (with offset)�ADD // add two values from stack�PUSH0 // push value 0 onto stack�MSTORE // store result in memory�PUSH1 20 // push value 32 onto stack�PUSH0 // push value 0 onto stack�RETURN // return result��Try on evm.codes

29 of 59

Function Dispatching

_

30 of 59

Function Dispatching

_

Function selectors

  1. Calls to smart contracts are ABI encoded�
  2. Function selector is the first 4 bytes of the keccak256(fnSignature)�
  3. Following bytes are function arguments

31 of 59

“Dead” shares

_

32 of 59

Function Dispatching

_

cast sig “addTwo(uint256,uint256)(uint256)”�0x0f52d66e��cast calldata “addTwo(uint256,uint256)(uint256)” 10 10�0x0f52d66e�000000000000000000000000000000000000000000000000000000000000000a�000000000000000000000000000000000000000000000000000000000000000a

33 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)#define macro MAIN() = takes(0) returns(0) {��������� }��

34 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack�������� }��

35 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0xe0 shr // shift 28 bytes to the right������� }��

36 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0xe0 shr // shift 28 bytes to the right� 0f52d66e // push function selector onto stack������ }��

37 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0xe0 shr // shift 28 bytes to the right� 0f52d66e // push function selector onto stack� eq // compare loaded selector with function selector����� }��

38 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0xe0 shr // shift 28 bytes to the right� 0f52d66e // push function selector onto stack� eq // compare loaded selector with function selector� addTwoJump // add program counter for jump definition� jumpi // jump to program location if fn selector matches�� addTwoJump:� ADD_TWO_NUMBERS(); }��#define macro ADD_TWO_NUMBERS() = takes(0) returns(0) {...}

39 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) { 0x00 calldataload // load first 32 bytes onto stack� 0xe0 shr // shift 28 bytes to the right� 0f52d66e // push function selector onto stack eq // compare loaded selector with function selector� addTwoJump // add program counter for jump definition� jumpi // jump to program location if fn selector matches�� addTwoJump:� ADD_TWO_NUMBERS(); }��#define macro ADD_TWO_NUMBERS() = takes(0) returns(0) {...}

40 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) { 0x00 calldataload // load first 32 bytes onto stack� 0xe0 shr // shift 28 bytes to the right� __FUNC_SIG(addTwo) // push function selector onto stack eq // compare loaded selector with function selector� addTwoJump // add program counter for jump definition� jumpi // jump to program location if fn selector matches�� addTwoJump:� ADD_TWO_NUMBERS(); }��#define macro ADD_TWO_NUMBERS() = takes(0) returns(0) {...}

41 of 59

Function Dispatching

_

#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload // load first 32 bytes onto stack� 0xe0 shr // shift 28 bytes to the right� __FUNC_SIG(addTwo) // push function selector onto stack� eq // compare loaded selector with function selector� addTwoJump // add program counter for jump definition� jumpi // jump to program location if fn selector matches�� addTwoJump:� ADD_TWO_NUMBERS(); }��#define macro ADD_TWO_NUMBERS() = takes(0) returns(0) {...}

42 of 59

Function Dispatching

_

����#define macro ADD_TWO_NUMBERS() = takes(0) returns(0) {� 0x04 calldataload // load first 32 bytes onto stack� 0x24 calldataload // load second 32 bytes onto stack� add // add number 1 and 2 and put result on stack� 0x00 mstore // place result in memory� 0x20 0x00 return // return the result }

Try on evm.codes

43 of 59

Working with storage

_

44 of 59

Working with storage

_

contract SimpleStorage {� uint256 value;�� function setValue(uint256 _value) public {� value = _value;� }�� function readValue() public view returns (uint256) {� return value;� }�}��

45 of 59

Working with storage

_

#define function setValue(uint256) nonpayable returns ()�#define function readValue() returns (uint256)���

46 of 59

Working with storage

_

#define function setValue(uint256) nonpayable returns ()�#define function readValue() returns (uint256)�#define constant VALUE = FREE_STORAGE_POINTER();���

47 of 59

Working with storage

_

#define function setValue(uint256) nonpayable returns ()�#define function readValue() returns (uint256)�#define constant VALUE = FREE_STORAGE_POINTER();��#define macro MAIN() = takes(0) returns(0) {�����������}��

48 of 59

Working with storage

_

#define function setValue(uint256) nonpayable returns ()�#define function readValue() returns (uint256)�#define constant VALUE = FREE_STORAGE_POINTER();��#define macro MAIN() = takes(0) returns(0) {� 0x00 calldataload 0xe0 shr // get function selector� dup1 __FUNC_SIG(setValue) eq setValue jumpi // selector comparison� dup1 __FUNC_SIG(readValue) eq readValue jumpi // selector comparison�� setValue: // function dispatch� SET_VALUE()� readValue:� READ_VALUE()�� 0x00 0x00 revert // revert with error�}��

49 of 59

Working with storage

_

��#define macro SET_VALUE() = takes(0) returns(0) {��������}��

50 of 59

Working with storage

_

��#define macro SET_VALUE() = takes(0) returns(0) {� // read calldata 0x04 // [0x04]� calldataload // [value]�����}��

51 of 59

Working with storage

_

��#define macro SET_VALUE() = takes(0) returns(0) {� // read calldata 0x04 // [0x04]� calldataload // [value]�� // get storage pointer and store� [VALUE] // [value_ptr, value]� sstore // []�}��

52 of 59

Working with storage

_

#define macro READ_VALUE() = takes(0) returns(0) {�������������}��

53 of 59

Working with storage

_

#define macro READ_VALUE() = takes(0) returns(0) {� // read value from storage� [VALUE] // [value_ptr]� sload // [value]����������}��

54 of 59

Working with storage

_

#define macro READ_VALUE() = takes(0) returns(0) {� // read value from storage� [VALUE] // [value_ptr]� sload // [value]�� // store return value in memory� 0x00 // [0x00, value]� mstore // []������}��

55 of 59

Working with storage

_

#define macro READ_VALUE() = takes(0) returns(0) {� // read value from storage� [VALUE] // [value_ptr]� sload // [value]�� // store return value in memory� 0x00 // [0x00, value]� mstore // []�� // return first 32 bytes from memory� 0x20 // [0x20]� 0x00 // [0x00]� return // [] Try on evm.codes�}��

56 of 59

TODO: huff website picture

57 of 59

Outro

_

�Resources

58 of 59

�Thank you.�_

Smart Contract Engineering ��r4bbit (@0x_r4bbit)�Smart Contract Engineer at Vac

_

59 of 59