215 lines
5.0 KiB
Markdown
215 lines
5.0 KiB
Markdown
# Wildcard Indexing & Topic Filtering
|
|
|
|
Index events by signature without specifying contract addresses.
|
|
|
|
## Basic Wildcard Indexing
|
|
|
|
Index all events matching a signature across ALL contracts:
|
|
|
|
**config.yaml:**
|
|
```yaml
|
|
networks:
|
|
- id: 1
|
|
start_block: 0
|
|
contracts:
|
|
- name: ERC20
|
|
handler: ./src/EventHandlers.ts
|
|
events:
|
|
- event: Transfer(address indexed from, address indexed to, uint256 value)
|
|
# No address = wildcard indexing
|
|
```
|
|
|
|
**Handler:**
|
|
```typescript
|
|
import { ERC20 } from "generated";
|
|
|
|
ERC20.Transfer.handler(
|
|
async ({ event, context }) => {
|
|
context.Transfer.set({
|
|
id: `${event.chainId}_${event.block.number}_${event.logIndex}`,
|
|
from: event.params.from,
|
|
to: event.params.to,
|
|
token: event.srcAddress, // The actual contract address
|
|
});
|
|
},
|
|
{ wildcard: true } // Enable wildcard
|
|
);
|
|
```
|
|
|
|
## Topic Filtering
|
|
|
|
Filter wildcard events by indexed parameters:
|
|
|
|
### Single Filter
|
|
|
|
Only index mints (from = zero address):
|
|
|
|
```typescript
|
|
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
|
|
ERC20.Transfer.handler(
|
|
async ({ event, context }) => {
|
|
// Handle mint event...
|
|
},
|
|
{ wildcard: true, eventFilters: { from: ZERO_ADDRESS } }
|
|
);
|
|
```
|
|
|
|
### Multiple Filters
|
|
|
|
Index both mints AND burns:
|
|
|
|
```typescript
|
|
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
const WHITELISTED = [
|
|
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
|
|
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
|
|
];
|
|
|
|
ERC20.Transfer.handler(
|
|
async ({ event, context }) => {
|
|
// Handle mint or burn...
|
|
},
|
|
{
|
|
wildcard: true,
|
|
eventFilters: [
|
|
{ from: ZERO_ADDRESS, to: WHITELISTED }, // Mints to whitelisted
|
|
{ from: WHITELISTED, to: ZERO_ADDRESS }, // Burns from whitelisted
|
|
],
|
|
}
|
|
);
|
|
```
|
|
|
|
### Per-Network Filters
|
|
|
|
Different filters for different chains:
|
|
|
|
```typescript
|
|
const WHITELISTED = {
|
|
1: ["0xEthereumAddress1"],
|
|
137: ["0xPolygonAddress1", "0xPolygonAddress2"],
|
|
};
|
|
|
|
ERC20.Transfer.handler(
|
|
async ({ event, context }) => {
|
|
// Handle transfer...
|
|
},
|
|
{
|
|
wildcard: true,
|
|
eventFilters: ({ chainId }) => [
|
|
{ from: ZERO_ADDRESS, to: WHITELISTED[chainId] },
|
|
{ from: WHITELISTED[chainId], to: ZERO_ADDRESS },
|
|
],
|
|
}
|
|
);
|
|
```
|
|
|
|
## Wildcard with Dynamic Contracts
|
|
|
|
Track ERC20 transfers to/from dynamically registered contracts:
|
|
|
|
**config.yaml:**
|
|
```yaml
|
|
networks:
|
|
- id: 1
|
|
contracts:
|
|
- name: SafeRegistry
|
|
address: 0xRegistryAddress
|
|
handler: ./src/EventHandlers.ts
|
|
events:
|
|
- event: NewSafe(address safe)
|
|
|
|
- name: Safe
|
|
handler: ./src/EventHandlers.ts
|
|
events:
|
|
- event: Transfer(address indexed from, address indexed to, uint256 value)
|
|
# No address - dynamically registered
|
|
```
|
|
|
|
**Handler:**
|
|
```typescript
|
|
// Register Safe addresses dynamically
|
|
SafeRegistry.NewSafe.contractRegister(async ({ event, context }) => {
|
|
context.addSafe(event.params.safe);
|
|
});
|
|
|
|
// Track transfers to/from registered Safes
|
|
Safe.Transfer.handler(
|
|
async ({ event, context }) => {
|
|
context.Transfer.set({
|
|
id: `${event.chainId}_${event.block.number}_${event.logIndex}`,
|
|
from: event.params.from,
|
|
to: event.params.to,
|
|
});
|
|
},
|
|
{
|
|
wildcard: true,
|
|
eventFilters: ({ addresses }) => [
|
|
{ from: addresses }, // Transfers FROM Safe addresses
|
|
{ to: addresses }, // Transfers TO Safe addresses
|
|
],
|
|
}
|
|
);
|
|
```
|
|
|
|
## Filter in Handler
|
|
|
|
Additional filtering inside handler:
|
|
|
|
```typescript
|
|
const USDC = {
|
|
1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
137: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
|
|
};
|
|
|
|
Safe.Transfer.handler(
|
|
async ({ event, context }) => {
|
|
// Only process USDC transfers
|
|
if (event.srcAddress !== USDC[event.chainId]) {
|
|
return;
|
|
}
|
|
|
|
context.USDCTransfer.set({
|
|
id: `${event.chainId}_${event.block.number}_${event.logIndex}`,
|
|
from: event.params.from,
|
|
to: event.params.to,
|
|
amount: event.params.value,
|
|
});
|
|
},
|
|
{
|
|
wildcard: true,
|
|
eventFilters: ({ addresses }) => [{ from: addresses }, { to: addresses }],
|
|
}
|
|
);
|
|
```
|
|
|
|
## Contract Register with Filters
|
|
|
|
Filter factory events when registering contracts:
|
|
|
|
```typescript
|
|
const DAI = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
|
|
|
|
// Only register pools containing DAI
|
|
UniV3Factory.PoolCreated.contractRegister(
|
|
async ({ event, context }) => {
|
|
context.addUniV3Pool(event.params.pool);
|
|
},
|
|
{ eventFilters: [{ token0: DAI }, { token1: DAI }] }
|
|
);
|
|
```
|
|
|
|
## Use Cases
|
|
|
|
- **Index all ERC20 transfers** - Track any token transfer
|
|
- **Index all NFT mints** - Track mints across collections
|
|
- **Track protocol interactions** - Monitor transfers to/from your contracts
|
|
- **Cross-contract analysis** - Analyze patterns across all contracts
|
|
- **Factory-created contracts** - Index contracts created by factories
|
|
|
|
## Limitations
|
|
|
|
- Only one wildcard per event signature per network
|
|
- Either `contractRegister` OR `handler` can have eventFilters, not both
|
|
- RPC data source supports only single wildcard event with topic filtering
|