> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bloklake.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Decoding logs

> Turn topics and data into event arguments with an ABI, in Python and TypeScript.

Bloklake returns logs as they are on chain: `address`, `topic0`–`topic3` and `data`. Decoding
them into named arguments needs the event's ABI:

* `topic0` is the event selector: `keccak256` of the signature, e.g.
  `keccak256("Swap(address,uint256,uint256,uint256,uint256,address)")`.
* **Indexed** arguments are in `topic1`–`topic3`, one per topic, each 32 bytes.
* **Non-indexed** arguments are ABI-encoded together in `data`.

Select `topic0` only if you query several events at once and need to tell them apart.

## Python (eth-abi)

`pip install bloklake eth-abi "eth-hash[pycryptodome]" eth-utils`

```python theme={null}
from eth_abi import decode
from eth_utils import event_abi_to_log_topic, to_checksum_address
from bloklake import Bloklake

bl = Bloklake(chain="eth")
SWAP_V2 = {"type": "event", "name": "Swap", "inputs": [
    {"name": "sender", "type": "address", "indexed": True},
    {"name": "amount0In", "type": "uint256", "indexed": False},
    {"name": "amount1In", "type": "uint256", "indexed": False},
    {"name": "amount0Out", "type": "uint256", "indexed": False},
    {"name": "amount1Out", "type": "uint256", "indexed": False},
    {"name": "to", "type": "address", "indexed": True},
]}
topic0 = "0x" + event_abi_to_log_topic(SWAP_V2).hex()

page = bl.query({
    "from_block": 19426000, "to_block": 19426099,
    "logs": [{"topic0": [topic0]}],
    "fields": {"log": ["block_number", "address", "topic1", "topic2", "data"]},
})
for log in page.logs[:2]:
    a0in, a1in, a0out, a1out = decode(["uint256"] * 4, bytes.fromhex(log["data"][2:]))
    sender = to_checksum_address("0x" + log["topic1"][-40:])
    print(log["block_number"], to_checksum_address(log["address"]), "sender", sender, "in", a0in, a1in, "out", a0out, a1out)
```

```text theme={null}
19426000 0x55D5c232D921B9eAA6b37b5845E439aCD04b4DBa sender 0x6b75d8AF000000e20B7a7DDf000Ba900b4009A80 in 0 568824768602570752 out 105201708239710 0
```

The [client](https://docs.bloklake.com/clients.md) returns hex strings: decode `bytes.fromhex(log["data"][2:])`, and an
indexed address is the last 40 hex characters of its topic. (Reading Arrow yourself, you get
`bytes` directly: `row["data"]` and `row["topic1"][12:]`.)

## TypeScript (viem)

`npm install @bloklake/client viem`

```typescript theme={null}
import { decodeEventLog, parseAbiItem, toEventSelector, type Hex } from "viem";
import { Bloklake } from "@bloklake/client";

const bl = new Bloklake({ chain: "rh" });
const transfer = parseAbiItem("event Transfer(address indexed from, address indexed to, uint256 value)");

const page = await bl.query({
  fromBlock: 61000000,
  toBlock: 61000009,
  logs: [{ topic0: [toEventSelector(transfer)] }],
  fields: { log: ["blockNumber", "address", "topic0", "topic1", "topic2", "data"] },
});
for (const log of page.logs.slice(0, 2)) {
  // rows are hex strings already: no conversion before decoding
  const { args } = decodeEventLog({
    abi: [transfer],
    topics: [log.topic0, log.topic1, log.topic2] as [Hex, Hex, Hex],
    data: log.data as Hex,
  });
  console.log(log.blockNumber, log.address, args.from, args.to, args.value);
}
```

```text theme={null}
61000000 0xecef091068f26d19174b3ad49e5e809d977e4e63 0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e 0x267444D099b10fB5Ed7c3Cc7B7c767AdcA574952 81632653061224488171595169n
```

The rows are already hex strings, which is what viem takes. `decodeEventLog` wants `topic0` too, so it's selected here. Leave out topics the event
doesn't use: `topic3` is null for a two-argument `Transfer`.

## Pitfalls

* **Same selector, different events.** ERC-20 and ERC-721 `Transfer` have the same `topic0`.
  ERC-721 indexes the token id too, so it has a `topic3` and empty `data`. Check before decoding.
* **Proxies.** For a proxied contract, the events come from the proxy's address, but you need
  the implementation's ABI.
* **Anonymous events** have no selector in `topic0`; their first indexed argument is there
  instead. Filter them by `address` and decode by position.
* **Indexed dynamic types** (`string`, `bytes`, arrays) are stored as the `keccak256` of the
  value, so the original can't be recovered from the topic.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.