BCDV 1013 - Advanced Smart Contract
Data location in solidity assembly
Dhruvin Parikh , July 2021
Topics
- Stack
- Memory
- Storage
- Calldata
EVM
- The Ethereum Virtual Machine
- The runtime environment for smart contracts
- A sandboxed, isolated environment
- Code running inside the EVM has no access to network, filesystem or other processes
Stack
- EVM is a stack machine
- All instructions are run on a stack
- Only the top part of a stack can be accessed
- Stack max size is 1024 elements
- Each stack element is 256 bits wide
- Stack operations: PUSH, POP, SWAP, DUP
PUSH
and POP
PUSH1
: add 1 byte item on stack
PUSH2
: add 2 bytes item on stack
PUSH3-32
: add 3 to 32 bytes item on stack
POP
: remove an item from the stack
SWAP
and DUP
SWAP1
: exchange 1st and 2nd stack items
SWAP2
: exchange 1st and 3rd stack items
SWAP3-16
: exchange 1st and 4th (17th) stack items
DUP1
: Duplicate 1st stack item
DUP2-16
: Duplicate 2nd-16th stack item
Opcodes not available in Yul
Since Yul manages local variables and control-flow, opcodes that interfere with these features are not available. This includes the dup
, swap
, jump
, labels
, push
instructions
EVM Opcodes
Memory
- An expandable byte-array used to store data
- Expanding memory costs gas
- Memory is expanded by 256-bit
- Volatile, only available during execution
Memory Access Functions
MLOAD(o)
: load word from memory onto stack
MSTORE(o,v)
: store 256-bit word into memory
- o: memory offset
- v: value from stack
MSTORE8(o,v)
: store a byte into memory
Solidity Memory Layout
- Solidity reserves memory slots as follow:
- 0x00 - 0x3f (64 bytes): scratch space, for short term usage
- 0x40 - 0x5f (32 bytes): free memory pointer
- 0x60 - 0x7f (32 bytes): zero for empty dynamic memory arrays
- 0x80: initial value of the free memory pointer
- Solidity Documentation on Memory Layout
Memory Example
| function getData(uint value) public view returns (bytes32 output) { |
| assembly { |
| function allocate(length) -> pos { |
| let freePointer := 0x40 |
| pos := mload(freePointer) |
| mstore(freePointer, add(pos,length)) |
| } |
| let dataSize := 0x20 |
| let offset := allocate(dataSize) |
| mstore(offset, value) |
| return(offset, dataSize) |
| } |
| } |
CallData
- Read only byte-array containing msg.data
- Data is ABI encoded
- The first 4 bytes is the function selector
- Arguments are padded to multiples of 32 bytes
- Layout of CallData
CallData Access Functions
CALLDATACOPY(m, c, n)
:
- load n bytes of call data from offset c into memory offset m
CALLDATALOAD(m)
: load 32 bytes calldata onto stack
CALLDATASIZE()
: load call data size onto stack
Return
- End execution, return output data
return(m,l)
:
- m: memory offset
- l: length of data in byte
Calldata demo
| |
| function getData(uint input) public view returns (bytes memory output) { |
| assembly { |
| let base := mload(0x40) |
| mstore(add(base, 0x00), 0x20) |
| mstore(add(base, 0x20), 36) |
| calldatacopy(add(base, 0x40), 0, 36) |
| return(base, 0x80) |
| } |
| } |
Storage
- State variables stored here
- Key-value store
- maps 256-bit words to 256-bit words
- Persistent storage in an account
State Variable Access
.slot
: storage slot for the variable
.offset
: byte offset for the variable
SSTORE/SLOAD
sstore(p,v)
: save a word to storage
- p: storage slot
- v: value to store
sload(p)
: load a word from storage to stack
State Variable Storage Layout
- Statically-sized variables stored from position 0
- Variables less than 32 bytes are packed in a single slot
- Refer to other rules in Solidity Documentation
Storage demo
| uint8 data1 = 1; |
| uint8 data2 = 2; |
| uint8 data3 = 3; |
| uint8 data4 = 4; |
| |
| // get data3 and return it as ouput |
| function getData() public view returns(bytes32){ |
| assembly { |
| let data := sload(data3_slot) |
| let result := and(shr(shl(3,data3.offset), data), 0xff) |
| mstore(0, result) |
| return(0,32) |
| } |
| } |
BCDV 1013 - Advanced Smart Contract Data location in solidity assembly Dhruvin Parikh , July 2021