Initial commit
This commit is contained in:
214
skills/hyperindex-development/references/wildcard-indexing.md
Normal file
214
skills/hyperindex-development/references/wildcard-indexing.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user