Skip to content

Instantly share code, notes, and snippets.

@Agusx1211
Last active July 28, 2022 06:08
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Agusx1211/e2f6743d8886c784843de5e95b99da78 to your computer and use it in GitHub Desktop.
Save Agusx1211/e2f6743d8886c784843de5e95b99da78 to your computer and use it in GitHub Desktop.
pragma solidity ^0.5.11;
/*
* @author Agustin Aguilar <@agusx1211>
*/
library ChunkStorage {
struct Storage {
mapping(bytes32 => address) slots;
}
function write(
Storage storage _space,
bytes32 _key,
bytes memory _data
) internal {
address slot;
if (_data.length == 0) {
delete _space.slots[_key];
return;
}
assembly {
// Load data to store, size and offset
let data_size := mload(_data)
let data_offset := add(_data, 0x20)
// Calculate expected data-contract size
let known_size
// If the contract is going to need PUSH2
// we need to account for that during the size estimation
let one_byte_push_only := lt(data_size, 289)
if one_byte_push_only {
// Calculate number of words required for storing
// the data
let words := div(data_size, 0x20)
// divRound number of words, if we need to store half word
// it's the same as storing a full one
if iszero(iszero(mod(data_size, 0x20))){
words := add(words, 0x01)
}
// Each word takes 0x24 bytes to store (PUSH 32 + 32 bytes + PUSH1 + 1 byte + MSTORE)
// and we need 0x06 extra bytes for the "return" opcodes
known_size := add(0x06, mul(words, 0x24))
}
if iszero(one_byte_push_only) {
// We know that 0x120 bytes of the data are stored using
// PUSH1, and thus that size is fixed, we need to calculate
// the remaining size
let tmp_data_size := sub(data_size, 0x120)
let words := div(tmp_data_size, 0x20)
// divRound number of words, if we need to store half word
// it's the same as storing a full one
if iszero(iszero(mod(tmp_data_size, 0x20))){
words := add(words, 0x01)
}
// Each word takes 0x25 bytes to store (PUSH 32 + 32 bytes + PUSH2 + 2 bytes + MSTORE)
// the 0x06 extra bytes for the "return" opcodes and
// we need to add the 0x145 bytes of the PUSH1 data
known_size := add(0x145, add(0x06, mul(words, 0x25)))
// We add the 0x09 words of the PUSH1 data
words := add(0x09, words)
}
// Get free memory to store the generated bytecode
let bytecode_offset := mload(0x40)
// Contract constructor, adding the calculated size of the contract
mstore(bytecode_offset, or(shl(232, known_size), 0x610000600081600B8239F3000000000000000000000000000000000000000000))
// Create the bytecode pointer to start writting the contract
let bytecode_pointer := add(0x0b, bytecode_offset)
// Iteare the data array, using 32 bytes words
for { let data_c := 0 } lt(data_c, data_size) { } {
// Write PUSH32 opcode and update pointer
mstore(bytecode_pointer, 0x7f00000000000000000000000000000000000000000000000000000000000000)
bytecode_pointer := add(bytecode_pointer, 0x01)
// Copy data to bytecode, 32 bytes
mstore(bytecode_pointer, mload(add(data_offset, data_c)))
bytecode_pointer := add(bytecode_pointer, 0x20)
// Check if the MSTORE opcode uses a PUSH1 or a PUSH2
let one_byte_opcode := lt(data_c, 0x0100)
if one_byte_opcode {
// Write PUSH1 [data_c] MSTORE and update pointer
mstore(bytecode_pointer, or(shl(0xf0, data_c), 0x6000520000000000000000000000000000000000000000000000000000000000))
bytecode_pointer := add(bytecode_pointer, 0x03)
}
if iszero(one_byte_opcode) {
// Write PUSH2 [data_c] MSTORE and update pointer
mstore(bytecode_pointer, or(shl(0xe8, data_c), 0x6100005200000000000000000000000000000000000000000000000000000000))
bytecode_pointer := add(bytecode_pointer, 0x04)
}
// Move the data pointer 32 bytes
data_c := add(data_c, 0x20)
}
// Write return function for the data-contract code and update the pointer
mstore(bytecode_pointer, or(shl(232, data_size), 0x6100006000f30000000000000000000000000000000000000000000000000000))
bytecode_pointer := add(bytecode_pointer, 0x06)
// Execute create using the generated bytecode
slot := create(0, bytecode_offset, sub(bytecode_pointer, bytecode_offset))
}
_space.slots[_key] = slot;
}
function read(Storage storage _space, bytes32 _key) internal view returns (bytes memory _data) {
address slot = _space.slots[_key];
if (slot == address(0)) {
_data = bytes("");
} else {
(, _data) = slot.staticcall("");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment