# Author: Simon-Pierre Boucher # Mail: contact@spboucher.ai """Hand-rolled ERC-20 event decoding — no ABI library. An ERC-20 transfer is a contract call emitting: Transfer(address indexed from, address indexed to, uint256 value) In the log: topics[0] = keccak256("Transfer(address,address,uint256)") topics[1] = from, left-padded to 32 bytes (indexed params live in topics) topics[2] = to, left-padded to 32 bytes data = value as one 32-byte big-endian word (non-indexed) """ from decimal import Decimal TRANSFER_TOPIC = ( "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ) def topic_address(topic): """A 32-byte topic holding an address: the address is the last 20 bytes.""" return "0x" + topic[-40:].lower() def word_uint(hexdata): if hexdata in (None, "0x", ""): return 0 return int(hexdata, 16) def format_amount(raw, decimals): """Human-readable amount from raw integer units (raw may be str or int).""" q = Decimal(int(raw)) / (Decimal(10) ** decimals) return format(q.normalize(), "f") def decode_transfer(chain, log, token_meta, timestamp): """Normalize one raw Transfer log into the canonical cross-chain event.""" return { "chain": chain, "block": int(log["blockNumber"], 16), "block_hash": (log.get("blockHash") or "").lower() or None, "tx_hash": log["transactionHash"].lower(), "log_index": int(log["logIndex"], 16), "timestamp": timestamp, "token": log["address"].lower(), "symbol": token_meta["symbol"], "from": topic_address(log["topics"][1]), "to": topic_address(log["topics"][2]), # uint256 overflows SQLite's int64 — store raw units as TEXT "amount": str(word_uint(log["data"])), "decimals": token_meta["decimals"], }