Micro slots - researching how to store multiple values in a single uint256 slot

#1

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

((_value % (10 ** _position)) - (_value % (10 ** (_position - _size)))) / (10 ** (_position - _size));

Solidity
This has been implemented in Solidity as a pure function

contract uintTool {
    function getInt(uint256 _value, uint256 _position, uint256 _size) public pure returns(uint256){
        uint256 a = ((_value % (10 ** _position)) - (_value % (10 ** (_position - _size)))) / (10 ** (_position - _size));
        return a;
    }
}

Implementation
It has also been deployed on the Ropsten test network at the following contract address if you would like to interact with it.

0x33539788196abf0155e74b80f4d0916e020226f7

Usage
The above getInt function will return a value of 1234567 if passed the following arguments

_value 99999991234567999999999999999999999999999999999999999999999999999999999999999
_position 70
_size 7

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.

For more information, and many more examples, please see this GitHub repository

#2

We used this approach in our ERC-1155 implementation if you are interested in looking into the details. I wrote a blog post about it sometime ago as well: https://medium.com/horizongames/going-beyond-erc20-and-erc721-9acebd4ff6ef

1 Like
#3

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.

Again, thank you so much for your response!

#4

I have a way to zero-out (flush) each micro-slot, as well as, individually update each micro-slot’s value.

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.).

Many thanks
Tim

#5

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