Please find some design patterns (below) which use modulo arithmetic & exponentiation to access and store multiple values inside a single uint256 slot.
Pseudo code 1 - read one or more digits from a uint256 variable
Thank you
Thank you for having a super quick chat with me about this at EDCON2019 @vbuterin, I went ahead and wrote this getInt function in Vyper straight after our quick chat. I appreciate that this is something that you have already thought of/tried; I would like to refine the general idea and formalize it as an Informational EIP.
Hi @PhABC
Thank you for your article and code URLs. Very thought provoking!
May I suggest the use of convention for assigning micro slots inside the uint256 data type. For example there are 77 (0-9) digits available. The factors of 77 are 1, 7, 11 and 77. Therefore the following options will explicitly segment the uint256 and ensure that as much of the uint256 is being used.
1, 77 digit integer with an individual value between 0 and 99999999999999999999999999999999999999999999999999999999999999999999999999999
77, 1 digit integers with an individual value between 0 and 9
7, 11 digit integers with an individual value between 0 and 99999999999
11, 7 digit integers with an individual value between 0 and 9999999
I have provided some Solidity code below which enforces the last bullet point from above; provides 11 unique slots, each of which can contain a maximum of 7 digits (total value of between 0 and 9999999).
pragma solidity ^0.5.0;
// The following contract uses a fixed convention which provides 11 unique micro-slots, each with 7 digits between 0 and 9.
// Each of the functions are designed to explicitly return the appropriate slot.
// Warning: This is a prototype for research purposes and it is not to be used to transact real value yet.
contract MicroSlots{
// This variable must be set to 77 digits between 0 and 9
uint256 private value = 76543210000000000000000000000000000000000055555554444444333333322222221111111;
// TODO This contract needs modifiers which will only allow numbers between 1 and 9999999 to be set for each position
// Returns 1111111
function getPosition1() public view returns(uint256){
return ((value % (10 ** 7)) - (value % (10 ** (7 - 7)))) / (10 ** (7 - 7));
}
// Returns 2222222
function getPosition2() public view returns(uint256){
return ((value % (10 ** 14)) - (value % (10 ** (14 - 7)))) / (10 ** (14 - 7));
}
// Returns 3333333
function getPosition3() public view returns(uint256){
return ((value % (10 ** 21)) - (value % (10 ** (21 - 7)))) / (10 ** (21 - 7));
}
//Returns 4444444
function getPosition4() public view returns(uint256){
return ((value % (10 ** 28)) - (value % (10 ** (28 - 7)))) / (10 ** (28 - 7));
}
// Returns 5555555
function getPosition5() public view returns(uint256){
return ((value % (10 ** 35)) - (value % (10 ** (35 - 7)))) / (10 ** (35 - 7));
}
// Returns 0000000
function getPosition6() public view returns(uint256){
return ((value % (10 ** 42)) - (value % (10 ** (42 - 7)))) / (10 ** (42 - 7));
}
// Returns 0000000
function getPosition7() public view returns(uint256){
return ((value % (10 ** 49)) - (value % (10 ** (49 - 7)))) / (10 ** (49 - 7));
}
// Returns 0000000
function getPosition8() public view returns(uint256){
return ((value % (10 ** 56)) - (value % (10 ** (56 - 7)))) / (10 ** (56 - 7));
}
// Returns 0000000
function getPosition9() public view returns(uint256){
return ((value % (10 ** 63)) - (value % (10 ** (63 - 7)))) / (10 ** (63 - 7));
}
// Returns 0000000
function getPosition10() public view returns(uint256){
return ((value % (10 ** 70)) - (value % (10 ** (70 - 7)))) / (10 ** (70 - 7));
}
// Returns 7654321
function getPosition11() public view returns(uint256){
return ((value % (10 ** 77)) - (value % (10 ** (77 - 7)))) / (10 ** (77 - 7));
}
}
The above contract has been deployed on the Ropsten Testnet at the following address
0xc7b6ac40d8557de62a33f5bcec6c4dc443fbd0f3
The functions are all public view so please feel free to interact with it.
I can provide the Solidity code for this here also if anyone is interested. I feel that this could be a very powerful tool for certain applications. Please let me know if there are any specific use cases and I will go ahead and code up the contract and the modifiers (validate inputs outputs etc.).
Awesome work. Very helpful. Thanks. I am interested in the Solidity code for the Zero-out and update each micro-slot. Please do post it here at your convenience. Cheers
This is interesting from the perspective of the developer (and the user) because it reduces gas costs. That’s an important consideration, to be sure, but I write tools that try to access arbitrary data from any smart contract (permissionless accounting). Generally, we only have the ABI to guide us about what’s going on.
Would there be any way to programmatically understand the storage/input/event data of a smart contract through the ABI? Or would an ‘outsider’ (such as our software) have to know that the uint256 in the ABI interface is actually storing multiple values?
This is obviously a useful and interesting idea, but there may be considerations other than simply gas savings. Have you thought about how this type of storage mechanism might be communicated to tools?
Hi,
Thank you so much for your response and questions.
Programmatically understanding the data
I envisage that the developer would write a smart contract such as this prototype example which I have provided. You will notice that the actual uint256 variable is private, however you will also see some pre-written “get” and “set” functions which can access each of the slots.
Validation (overflow and underflow)
These pre-written functions could include custom validation etc. The main point is that the pre-written functions will be available in the ABI. Of course the developer might want to give the pre-written functions more meaningful names. I just programmatically churned the pre-written functions out using a for loop in Javascript.
Events
Also, I like your point about events. We could absolutely create some events with meaningful names and then emit those events as part of each “get” and “set” function. All in all the combination of the above would allow external tools to instantiate a web3 contract instance and harvest not only the current state (by calling all of the public/view functions) but also harvest and store the event logs for the life of the contract (and even create a watcher for real-time events).
Compiler support
I like this Micro slots approach because it can be implemented in the Vyper programming language by any developer. It does not require new opcodes.
It is my current understanding that (whilst alternatives to Micro slots such as Bitwise shifting may be as cheap or cheaper in terms of gas) Bitwise shifting is only supported in the Solidity compiler.
a) I am happy to be wrong about the Vyper support, please correct me if Vyper now supports the Bitwise shift opcodes.
b) I am not sure if Bitwise shifting is actually cheaper than Micro slots in terms of gas. If someone could test and measure this I would be ever so grateful.
EIP to formalize this design pattern
In a community spirit, and also to improve safety and prevent duplication of effort, I would like to see the community refine the idea in this informational EIP so that there is a safe and comprehensive resource (design pattern) for the community to use, if they so desire.
I hope this answers your questions.
Thanks again for the reply, much appreciated.
Hi,
Sorry, I just re-read this and realized that you specifically asked for the Solidity code to Zero-out and update each micro-slot (as apposed to just zeroing out and updating arbitrary positions in the uint256). Apologies for the delayed response. I have been meaning to write a full smart contract (building on the previous work here). I will get back to this and include getters, setters, events and more. I will just need some more time as I am currently very busy. Hold that thought