-
Notifications
You must be signed in to change notification settings - Fork 32
/
SSTORE2Map.sol
124 lines (108 loc) · 5 KB
/
SSTORE2Map.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@0xsequence/create3/contracts/Create3.sol";
import "./utils/Bytecode.sol";
/**
@title A write-once key-value storage for storing chunks of data with a lower write & read cost.
@author Agustin Aguilar <aa@horizon.io>
Readme: https://github.com/0xsequence/sstore2#readme
*/
library SSTORE2Map {
error WriteError();
// keccak256(bytes('@0xSequence.SSTORE2Map.slot'))
bytes32 private constant SLOT_KEY_PREFIX = 0xd351a9253491dfef66f53115e9e3afda3b5fdef08a1de6937da91188ec553be5;
function internalKey(bytes32 _key) internal pure returns (bytes32) {
// Mutate the key so it doesn't collide
// if the contract is also using CREATE3 for other things
return keccak256(abi.encode(SLOT_KEY_PREFIX, _key));
}
/**
@notice Stores `_data` and returns `pointer` as key for later retrieval
@dev The pointer is a contract address with `_data` as code
@param _data To be written
@param _key unique string key for accessing the written data (can only be used once)
@return pointer Pointer to the written `_data`
*/
function write(string memory _key, bytes memory _data) internal returns (address pointer) {
return write(keccak256(bytes(_key)), _data);
}
/**
@notice Stores `_data` and returns `pointer` as key for later retrieval
@dev The pointer is a contract address with `_data` as code
@param _data to be written
@param _key unique bytes32 key for accessing the written data (can only be used once)
@return pointer Pointer to the written `_data`
*/
function write(bytes32 _key, bytes memory _data) internal returns (address pointer) {
// Append 00 to _data so contract can't be called
// Build init code
bytes memory code = Bytecode.creationCodeFor(
abi.encodePacked(
hex'00',
_data
)
);
// Deploy contract using create3
pointer = Create3.create3(internalKey(_key), code);
}
/**
@notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
@dev The function is intended for reading pointers first written by `write`
@param _key string key that constains the data
@return data read from contract associated with `_key`
*/
function read(string memory _key) internal view returns (bytes memory) {
return read(keccak256(bytes(_key)));
}
/**
@notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
@dev The function is intended for reading pointers first written by `write`
@param _key string key that constains the data
@param _start number of bytes to skip
@return data read from contract associated with `_key`
*/
function read(string memory _key, uint256 _start) internal view returns (bytes memory) {
return read(keccak256(bytes(_key)), _start);
}
/**
@notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
@dev The function is intended for reading pointers first written by `write`
@param _key string key that constains the data
@param _start number of bytes to skip
@param _end index before which to end extraction
@return data read from contract associated with `_key`
*/
function read(string memory _key, uint256 _start, uint256 _end) internal view returns (bytes memory) {
return read(keccak256(bytes(_key)), _start, _end);
}
/**
@notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
@dev The function is intended for reading pointers first written by `write`
@param _key bytes32 key that constains the data
@return data read from contract associated with `_key`
*/
function read(bytes32 _key) internal view returns (bytes memory) {
return Bytecode.codeAt(Create3.addressOf(internalKey(_key)), 1, type(uint256).max);
}
/**
@notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
@dev The function is intended for reading pointers first written by `write`
@param _key bytes32 key that constains the data
@param _start number of bytes to skip
@return data read from contract associated with `_key`
*/
function read(bytes32 _key, uint256 _start) internal view returns (bytes memory) {
return Bytecode.codeAt(Create3.addressOf(internalKey(_key)), _start + 1, type(uint256).max);
}
/**
@notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
@dev The function is intended for reading pointers first written by `write`
@param _key bytes32 key that constains the data
@param _start number of bytes to skip
@param _end index before which to end extraction
@return data read from contract associated with `_key`
*/
function read(bytes32 _key, uint256 _start, uint256 _end) internal view returns (bytes memory) {
return Bytecode.codeAt(Create3.addressOf(internalKey(_key)), _start + 1, _end + 1);
}
}