Skip to content

Instantly share code, notes, and snippets.

@miohtama
Created Mar 26, 2021
Embed
What would you like to do?
Solidity and Ethereum int256 for Python, SQLAlchemy and SQL Databases, efficiently as 32 bytes blobs
class LiquidityChanged(TransactionEvent):
"""A sampled liquidity at any moment."""
__tablename__ = "liquidity"
delta0 = sa.Column(Int257, nullable=False, index=False)
from utils import Int257
def test_max_positive():
max_positive = int(2**256 - 1)
int257 = Int257()
bin = int257.process_bind_param(max_positive, None)
assert bin == b'\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
i = int257.process_result_value(bin, None)
assert i == max_positive
def test_max_negative():
max_negative = -int(2**256 - 1)
int257 = Int257()
bin = int257.process_bind_param(max_negative, None)
assert bin == b'\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
i = int257.process_result_value(bin, None)
assert i == max_negative
from sqlalchemy import types
class Int257(types.TypeDecorator):
"""SQLAlchemy class to store Ethereum Virtual Machine (EVM) uint256s in an SQL database.
Unlike Solidity's native uin256, this is one allows to store the sign and negative numbers,
making it more useful for accounting data.
Should be a bit more efficient than storing stringified decimals in a database.
"""
# uint256 is 32 bytes
impl = types.BINARY(33)
MAX_UINT256 = 2**256
BYTE_ORDER = "big"
def load_dialect_impl(self, dialect):
return dialect.type_descriptor(types.BINARY(33))
def process_bind_param(self, val: int, dialect):
assert type(val) == int
assert val < Int257.MAX_UINT256
# Separate sign and 256-bit int body
# The sign is stored as the highest 33th byte
b = abs(val).to_bytes(32, byteorder=Int257.BYTE_ORDER)
sign = b"\0" if val >= 0 else b"\1"
return sign + b
def process_result_value(self, val: bytes, dialect):
assert type(val) == bytes
sign = -1 if val[0] else 1
lower = val[1:]
number = int.from_bytes(lower, byteorder=Int257.BYTE_ORDER)
return sign * number
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment