�Smart Contract Team Learn Ups�EVM deep dive with Huff
Smart Contract Engineering��r4bbit (@0x_r4bbit)�Smart Contract Engineer at Vac
_
EVM & Opcodes
_
�The EVM is a stack-based computer, responsible for executing smart contract instructions. ��These instructions are known as opcodes.
EVM & Opcodes
_
What is a vault anyway?
_
What is a vault anyway?
_
What is a vault anyway?
_
TODO: huff website picture
Huff Basics
_
Huff Basics
_
�#define function addTwo(uint256, uint256) view returns (uint256)��
Huff Basics
_
�#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {������}
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�����}
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����}
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���}
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��}
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�}
Huff Basics
_
�� huffc src/addTwo.huff -b� → 600c8060093d393df35f35602035015f5260205ff3������Try on evm.codes
Contract Creation
_
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���
Contract creation
_
Returned runtime code:�5f35602035015f5260205ff3��PUSH0 // 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
Function Dispatching
_
Function Dispatching
_
Function selectors
“Dead” shares
_
Function Dispatching
_
�cast sig “addTwo(uint256,uint256)(uint256)”�0x0f52d66e��cast calldata “addTwo(uint256,uint256)(uint256)” 10 10�0x0f52d66e�000000000000000000000000000000000000000000000000000000000000000a�000000000000000000000000000000000000000000000000000000000000000a
Function Dispatching
_
#define function addTwo(uint256, uint256) view returns (uint256)��#define macro MAIN() = takes(0) returns(0) {��������� �}��
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�������� �}��
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������� �}��
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������ �}��
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����� �}��
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) {...}
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) {...}
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) {...}
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) {...}
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
Working with storage
_
Working with storage
_
��contract SimpleStorage {� uint256 value;�� function setValue(uint256 _value) public {� value = _value;� }�� function readValue() public view returns (uint256) {� return value;� }�}��
Working with storage
_
�#define function setValue(uint256) nonpayable returns ()�#define function readValue() returns (uint256)���
Working with storage
_
�#define function setValue(uint256) nonpayable returns ()�#define function readValue() returns (uint256)�#define constant VALUE = FREE_STORAGE_POINTER();���
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) {�����������}��
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�}��
Working with storage
_
���#define macro SET_VALUE() = takes(0) returns(0) {��������}��
Working with storage
_
���#define macro SET_VALUE() = takes(0) returns(0) {� // read calldata� 0x04 // [0x04]� calldataload // [value]�����}��
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 // []�}��
Working with storage
_
��#define macro READ_VALUE() = takes(0) returns(0) {�������������}��
Working with storage
_
��#define macro READ_VALUE() = takes(0) returns(0) {� // read value from storage� [VALUE] // [value_ptr]� sload // [value]����������}��
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 // []������}��
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�}��
TODO: huff website picture
Outro
_
�Resources
�Thank you.�_
Smart Contract Engineering ��r4bbit (@0x_r4bbit)�Smart Contract Engineer at Vac
_