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# 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