Skip to content

Instantly share code, notes, and snippets.

@zemse

zemse/cint.sol Secret

Last active October 1, 2021 23:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zemse/0ea19dd9b4922cd68f096fc2eb4abf93 to your computer and use it in GitHub Desktop.
Save zemse/0ea19dd9b4922cd68f096fc2eb4abf93 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
/**
Compressed Integers 64
What?
Helps conversion between uint256 values and uint64 values.
Why?
Storage is costly, each storage slot costs almost $0.8 to initialize and $0.2 to update (20 gwei, 2000 ETHUSD).
Usually we store amounts in uint256 which takes up one entire slot.
If it's DAI value, the range we work with mostly is 0.001 DAI to 1T DAI (or 10**12).
If it's ETH value, the range we work with mostly is 0.000001 ETH to 1B ETH.
Similarly any token of any scale has a resonable range of 10**15 amounts that we care / work with.
However, uint256 type allows us to represent 10**(-18) to 10**58, and most of it is a waste.
Number of bits required to represent 10^15 values = log_base_2(10**15) = 50 bits.
How?
uint64 => uint56 mostSignificantBits,uint8 shift
We only use the first 56 bits of uint256 from where we find the first "on" bit as mostSignificantBits.
We use the shift to shiftLeft the 56 bit value to uncompress it back to uint256.
This is a lossy compression and some information is definately lost,
it amounts to $0.001 of error in the WORST CASE, which is 200x less than storage costs.
**/
contract Example {
using CompressedMath64 for uint64;
using CompressedMath64 for uint256;
struct Slot {
uint32 blockNumber;
address payable receiver;
uint64 amount;
}
Slot public slot;
function deposit(uint32 blockNumber, address payable receiver) external payable {
Slot memory _slot = slot; // SLOAD
require(_slot.amount == 0, 'already initialized');
_slot.blockNumber = blockNumber;
_slot.receiver = receiver;
_slot.amount = msg.value.compress(); // compress with 56 most significant bits
slot = _slot; // SSTORE
}
function withdraw() external {
Slot memory _slot = slot; // SLOAD
require(msg.sender == address(_slot.receiver), 'not authorised');
require(block.number > _slot.blockNumber, 'not yet');
_slot.receiver.transfer(slot.amount.uncompress());
}
}
library CompressedMath64 {
/**
* CInt Math
*/
function cadd(uint64 a, uint64 b) public pure returns (uint64 cint) {
cint = compress(uncompress(a) + uncompress(b));
}
function cadd(uint64 a, uint256 b) public pure returns (uint64 cint) {
cint = compress(uncompress(a) + b);
}
function csub(uint64 a, uint64 b) public pure returns (uint64 cint) {
cint = compress(uncompress(a) - uncompress(b));
}
function csub(uint64 a, uint256 b) public pure returns (uint64 cint) {
cint = compress(uncompress(a) - b);
}
function cmul(uint64 a, uint256 b) public pure returns (uint64 cint) {
cint = compress(uncompress(a) * b);
}
function cdiv(uint64 a, uint256 b) public pure returns (uint64 cint) {
cint = compress(uncompress(a) / b);
}
function cmuldiv(
uint64 a,
uint64 b,
uint64 c
) public pure returns (uint64 cint) {
cint = compress((uncompress(a) * uncompress(b)) / uncompress(c));
}
/**
* CInt Core
*/
function compress(uint256 full) public pure returns (uint64 cint) {
uint8 bits = mostSignificantBit(full);
if (bits <= 55) {
cint = uint64(full) << 8;
} else {
bits -= 55;
cint = (uint64(full >> bits) << 8) + bits;
}
}
function uncompress(uint64 cint) public pure returns (uint256 full) {
uint8 bits = uint8(cint % (1 << 9));
full = uint256(cint >> 8) << bits;
}
function mostSignificantBit(uint256 val) public pure returns (uint8 bit) {
if (val >= 0x100000000000000000000000000000000) {
val >>= 128;
bit += 128;
}
if (val >= 0x10000000000000000) {
val >>= 64;
bit += 64;
}
if (val >= 0x100000000) {
val >>= 32;
bit += 32;
}
if (val >= 0x10000) {
val >>= 16;
bit += 16;
}
if (val >= 0x100) {
val >>= 8;
bit += 8;
}
if (val >= 0x10) {
val >>= 4;
bit += 4;
}
if (val >= 0x4) {
val >>= 2;
bit += 2;
}
if (val >= 0x2) bit += 1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment