Events
Every contract module DappQL generates ships typed event helpers. You can decode logs you already have, compute topic hashes, and parse receipts, all with the ABI embedded, no hand-rolled topic0 math.
Generated surface
For a contract with any events in its ABI, the generated module exports:
import { Token } from './src/contracts'
Token.events // typed object: { Transfer, Approval, ... }
Token.getEventTopic('Transfer')
// → '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
Token.parseEvents('Transfer', logs)
// → ParsedEvent<'Transfer'>[], typed argsThese are thin wrappers over viem's encodeEventTopics and parseEventLogs, but with the ABI baked in so you never pass it manually.
Typical flow: filter by topic, parse to typed args
import { createPublicClient, http } from 'viem'
import { base } from 'viem/chains'
import { Token } from './src/contracts'
const client = createPublicClient({ chain: base, transport: http() })
// Filter logs by topic, topic hash comes from the generated helper
const logs = await client.getLogs({
address: Token.deployAddress,
topics: [Token.getEventTopic('Transfer')],
fromBlock: 44_000_000n,
toBlock: 'latest',
})
// Decode into typed args
const parsed = Token.parseEvents('Transfer', logs)
for (const { event, parsed: [decoded] } of parsed) {
console.log(decoded.eventName) // 'Transfer'
console.log(decoded.args.from, decoded.args.to, decoded.args.value)
console.log(event.blockNumber) // raw log data available too
}Watching live
Wagmi's useWatchContractEvent is the React way to subscribe. DappQL doesn't wrap it, use wagmi directly for live event subscriptions:
import { useWatchContractEvent } from 'wagmi'
import { Token } from './src/contracts'
useWatchContractEvent({
address: Token.deployAddress,
abi: Token.abi,
eventName: 'Transfer',
onLogs: (logs) => {
const parsed = Token.parseEvents('Transfer', logs)
// ...
},
})Pass the generated Token.abi straight in, it's already typed.
Via @dappql/mcp: getEvents tool
For scripts, debugging, or agent workflows, @dappql/mcp exposes events via a tool:
{
"tool": "getEvents",
"arguments": {
"contract": "Token",
"event": "Transfer",
"fromBlock": "44000000",
"toBlock": "latest",
"args": { "from": "0xabc..." },
"limit": 100
}
}Returns decoded events with { blockNumber, txHash, logIndex, args }. No topic hashing, no eth_getLogs gymnastics. See Tools reference.
Decoding an unknown log
If you have a log but don't know which contract emitted it, @dappql/mcp's getTransaction tool auto-decodes every log in a receipt against every project contract's ABI, first match wins. Useful for "what did this tx do":
{
"tool": "getTransaction",
"arguments": { "hash": "0x..." }
}Response includes logs: [{ decoded: { contract, eventName, args } | null, ... }].
Event arg types
Indexed args come from topics; non-indexed args come from the log's data field. parseEvents gives you the union correctly typed:
// ABI: event Transfer(address indexed from, address indexed to, uint256 value)
decoded.args.from // `0x${string}`, indexed
decoded.args.to // `0x${string}`, indexed
decoded.args.value // bigint, non-indexedGotchas
uint256event args arebigint. Don't compare withNumber().- Topic filtering only works for indexed args.
args: { value: 1000n }on the MCPgetEventstool does nothing ifvalueis non-indexed. - Large block ranges time out. RPCs cap
eth_getLogsto ~10k block ranges. For bigger windows, chunk queries or use an indexer. The MCP tool returnstruncated: truewhentotal > limit. - Template contracts need an explicit address.
Token.getEventTopic('Transfer')works because topic is ABI-only, butgetLogsneeds the address passed in.
Related
getEventsMCP tool, agent-callable event reader.getTransactionMCP tool, full tx + auto-decoded logs.- Templates, event patterns across many instances.