SPB Git

spb/coinexplorer Public MIT

Self-hosted, zero-API-key explorer for stablecoins and major crypto.

Python 60.3% HTML 23.6% JavaScript 8.1% CSS 6.8% SQL 1%
1.8 KB · 56 lines python
Raw Blame History
1# Author: Simon-Pierre Boucher2# Mail: contact@spboucher.ai3"""Hand-rolled ERC-20 event decoding — no ABI library.45An ERC-20 transfer is a contract call emitting:6    Transfer(address indexed from, address indexed to, uint256 value)78In the log:9    topics[0] = keccak256("Transfer(address,address,uint256)")10    topics[1] = from, left-padded to 32 bytes (indexed params live in topics)11    topics[2] = to,   left-padded to 32 bytes12    data      = value as one 32-byte big-endian word (non-indexed)13"""1415from decimal import Decimal1617TRANSFER_TOPIC = (18    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"19)202122def topic_address(topic):23    """A 32-byte topic holding an address: the address is the last 20 bytes."""24    return "0x" + topic[-40:].lower()252627def word_uint(hexdata):28    if hexdata in (None, "0x", ""):29        return 030    return int(hexdata, 16)313233def format_amount(raw, decimals):34    """Human-readable amount from raw integer units (raw may be str or int)."""35    q = Decimal(int(raw)) / (Decimal(10) ** decimals)36    return format(q.normalize(), "f")373839def decode_transfer(chain, log, token_meta, timestamp):40    """Normalize one raw Transfer log into the canonical cross-chain event."""41    return {42        "chain": chain,43        "block": int(log["blockNumber"], 16),44        "block_hash": (log.get("blockHash") or "").lower() or None,45        "tx_hash": log["transactionHash"].lower(),46        "log_index": int(log["logIndex"], 16),47        "timestamp": timestamp,48        "token": log["address"].lower(),49        "symbol": token_meta["symbol"],50        "from": topic_address(log["topics"][1]),51        "to": topic_address(log["topics"][2]),52        # uint256 overflows SQLite's int64 — store raw units as TEXT53        "amount": str(word_uint(log["data"])),54        "decimals": token_meta["decimals"],55    }56