### Solidity Assembly (Part 3) #### Advanced Smart Contract
Yuet Loo Wong
### Topics - Assembly - Memory Array - Storage Array - Mappings - Contract Code
## Memory Array -
memory
keyword - ``` function Hello(uint[] memory input) public pure returns(uint[] memory output) { bool[] memory boolArray; return input; } ```
## Array Memory Layout - Elements in arrays occupy multiples of 32 bytes - except for
string
and
bytes
- Dynamic array - the length is stored in the first slot - elements are stored in the following slots
### Dynamic Memory Array - length stored in the first slot ``` function getLength(uint[] memory input) public pure returns(bytes32 result) { assembly { result := mload(input) } } ```
### Fixed Length Memory Array - elements start in the first slot - subsequent slots will be filled if first slot is full - ``` function getFirstElement( byte[3] memory data ) public pure returns(bytes32) { assembly { mstore(0, mload(data)) return(0,32) } } ```
### String and Bytes in Memory - length in first slot - elements start in second slot - elements are tightly packed - each element do not occupy 32 bytes - ``` function getFirstElement( bytes memory data ) public pure returns(bytes32) { assembly { mstore(0, mload(add(data,32))) return(0,32) } } ```
## Multi-dimensional Memory Array - length is stored in the first slot - subsequent slots store pointers to memory arrays - ``` function get2DArrayLength( ) public pure returns(bytes32) { uint size = 3; uint8[][] memory data = new uint8[][](size); for(uint i = 0; i < size; i++) { data[i] = new uint8[](4); data[i][0] = 88; } assembly { mstore(0, mload(data)) return(0, 32) } } ```
## Array in Storage - array state variables - ``` contract Sample { uint[] array; // storage slot 0 bool[1] boolArray; // storage slot 1 } ```
### Dynamic Array Storage Layout - the array length is in the storage slot 'p' - the nth-element is in slot: keccak256(p) + n
### Dynamic Array Storage Example -
Github Source
``` contract DynamicArrayStorage { struct Store { uint8 x; uint8 y; } Store[] data; constructor() public { data.push(Store(0x22, 0x33)); } function getData(uint slot) public view returns (bytes32 result) { assembly { mstore(0, data_slot) result := sload(add(keccak256(0,0x20),slot)) } } } ```
### Dynamic Array Vunerability - Doug Hoyte's
Underhanded Solidity Contest
Winning Submission - ``` contract MerdeToken { address public owner; uint[] public bonusCodes; constructor() public { owner = msg.sender; bonusCodes.length--; } function modifyBonusCode(uint index, uint update) public { require(index < bonusCodes.length); bonusCodes[index] = update; } } ```
### Array Index Exercise - How to change the
owner
value in previous example? - Hints: - use modifyBonusCode() - for an array at storage slot p, its data is stored at: - x = keccak256(p) - maximum storage index = n = 2^256 - 1 - last array index i = n - x - array index j wraps around and end up at slot k - j = n - x + k
### Solidity v0.6 -
Breaking changes
- Array length is read only:
issue 3515
- array.length-- is not allowed - Use pop() to remove array element - Fix issue related to array length manipulation
Mapping
mapping (address=>uint) balances;
### Mapping Storage Layout - the value for a mapping key k is located at: - keccak256(k . p) - where "." is concatenation - p is the storage slot of the mapping
### Mapping Example ``` contract Account { mapping (address=>uint) balances; constructor() public { balances[0xe90f4F8aeBa3AdE774CaC94245792085a451bC8E] = 0x45; balances[0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c] = 0x67; } function GetBalance(address addr) public view returns(bytes32 output) { assembly { mstore(0, addr) mstore(0x20, balances_slot) output := sload(keccak256(0, 0x40)) } } } ```
### Bytes and String in Storage - short (data less than 32 bytes) - data is in the same slot as the length - lowest-order byte stores length * 2 - long (data more than 31 bytes) - first slot stores length * 2 + 1 - last bit is set if data is longer than 31 bytes
### Bytes in Storage Example ``` bytes bytesData = "aabbcc"; function getBytes() public view returns(bytes32) { assembly { mstore(0, sload(bytesData_slot)) return(0, 32) } } ```
## Lab Following the instructions on the readme file of
assembly-array