Custom SQLite functions for exact uint256 arithmetic over big-endian BLOBs, plus display helpers that render wei amounts as ether/gwei/USD strings.
EVM amount values are 256-bit integers, but SQLite has no native 256-bit type. Storing them as 32-byte big-endian BLOBs keeps byte order equal to numeric order, so MAX/MIN/COUNT/GROUP BY/threshold comparisons work natively. Arithmetic does not - a plain SUM coerces the BLOB to a float and returns garbage. This crate registers functions that do real 256-bit math and render the results as readable decimals.
Most functions accept each operand either as a non-negative INTEGER or as a big-endian BLOB of at most 32 bytes (shorter blobs are left-padded). Unless noted otherwise, a NULL operand propagates to NULL, and a blob longer than 32 bytes (not a valid uint256) or a negative integer raises an error.
u256_sum(x)- aggregate. Sums uint256 operands and returns a 32-byte big-endian BLOB. SkipsNULL. ReturnsNULLover an empty set. Raises if the total overflowsU256::MAX.u256_mul(a, b)- scalar. Multiplies two uint256 operands and returns a 32-byte big-endian BLOB. Needed because a product such asgas_used * effective_gas_priceoverflows SQLite's signed 64-bitINTEGER. Raises on overflow pastU256::MAX.u256_add(a, b)- scalar. Adds two uint256 operands and returns a 32-byte big-endian BLOB. ANULLoperand yieldsNULL, so an optional addend (e.g. an absent coinbase transfer) nulls the result. Raises on overflow pastU256::MAX.
u256_to_dec(x)- scalar. Decodes a uint256 to its full-precision decimal string (e.g."2014847014830705"), since a u256 overflows SQLite's signed 64-bitINTEGER.format_ether(x)- scalar. Renders a wei amount as ether with 6 decimals, e.g."0.000141 ETH".format_gwei(x)- scalar. Renders a wei amount as gwei with 2 decimals, e.g."30.00 gwei".convert_usd(x, price)- scalar. Returns the USD value of a wei amount as aREAL, computed asether(x) * price.priceis a real (or integer) USD price per token; aNULLamount orNULLprice yieldsNULL. Approximate: it goes throughf64, since the price is itself a float.format_usd(x)- scalar. Renders a USD value (a real or integer) as"$"plus a 2-decimal value with US-style thousands commas, e.g."$1,234,567.89". This is a pure formatter and does not convert from wei; compose it withconvert_usd, e.g.format_usd(convert_usd(wei, price)). ANULLvalue yieldsNULL.erc20_to_real(amount, decimals)- scalar. Divides a uint256 token amount by10^decimalsand returns aREAL, so numeric SQL works directly on it, e.g.ROUND(erc20_to_real(u256_sum(amount), 6), 2)for a USDC total.decimalsmust be anINTEGERin0..=77; aNULLamount orNULLdecimals yieldsNULL. Approximate (f64, ~15-16 significant digits) - useu256_to_decwhen the exact value matters.
use rusqlite::Connection;
let conn = Connection::open_in_memory()?;
evm_sqlite::register_functions(&conn)?;
// Wrap the aggregate in `u256_to_dec` to get a readable decimal string
// instead of a raw 32-byte BLOB.
let total: Option<String> = conn.query_row(
"SELECT u256_to_dec(u256_sum(amount)) FROM transfers",
[],
|r| r.get(0),
)?;
// Compose arithmetic and rendering: gas cost in ether and in USD.
let (eth, usd): (String, String) = conn.query_row(
"SELECT format_ether(u256_mul(gas_used, effective_gas_price)), \
format_usd(convert_usd(u256_mul(gas_used, effective_gas_price), 2500.5)) \
FROM transactions WHERE tx_hash = ?1",
[tx_hash],
|r| Ok((r.get(0)?, r.get(1)?)),
)?;Functions are per-connection, so call register_functions on every connection that needs them.