@dilukangelo/web3-ai-skills 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: subgraph-indexing
|
|
3
|
-
description: Chain indexing with The Graph, Ponder, Envio, and custom indexers. Event-driven data, GraphQL APIs, entity relationships, and real-time sync.
|
|
3
|
+
description: Chain indexing with The Graph, Ponder, Subsquid, Envio, and custom indexers. Event-driven data, GraphQL APIs, entity relationships, and real-time sync.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Subgraph & Indexing — Blockchain Data Access
|
|
@@ -10,7 +10,7 @@ Expert knowledge for building efficient blockchain data indexing solutions.
|
|
|
10
10
|
## Use this skill when
|
|
11
11
|
|
|
12
12
|
- Building subgraphs for The Graph
|
|
13
|
-
- Setting up Ponder or Envio indexers
|
|
13
|
+
- Setting up Ponder, Subsquid, or Envio indexers
|
|
14
14
|
- Creating real-time data feeds from on-chain events
|
|
15
15
|
- Designing entity schemas for blockchain data
|
|
16
16
|
- Querying historical blockchain data efficiently
|
|
@@ -165,16 +165,215 @@ ponder.on('Token:Transfer', async ({ event, context }) => {
|
|
|
165
165
|
|
|
166
166
|
---
|
|
167
167
|
|
|
168
|
+
## Subsquid (SQD / Squid SDK)
|
|
169
|
+
|
|
170
|
+
### Overview
|
|
171
|
+
Subsquid is a batch-processing blockchain indexer optimized for high-throughput multi-chain data. Uses the **SQD Network** as a decentralized data lake.
|
|
172
|
+
|
|
173
|
+
### Project Structure
|
|
174
|
+
```
|
|
175
|
+
squid/
|
|
176
|
+
├── src/
|
|
177
|
+
│ ├── processor.ts # EvmBatchProcessor config
|
|
178
|
+
│ ├── main.ts # Processing logic
|
|
179
|
+
│ ├── model/ # TypeORM entities
|
|
180
|
+
│ └── abi/ # Generated type-safe ABI wrappers
|
|
181
|
+
├── db/
|
|
182
|
+
│ └── migrations/ # Database migrations
|
|
183
|
+
├── schema.graphql # Entity definitions (generates model/)
|
|
184
|
+
├── squid.yaml # Deployment manifest
|
|
185
|
+
├── commands.json # CLI commands
|
|
186
|
+
└── docker-compose.yml # Local Postgres + GraphQL
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Installation
|
|
190
|
+
```bash
|
|
191
|
+
# Install Squid CLI
|
|
192
|
+
npm i -g @subsquid/cli
|
|
193
|
+
|
|
194
|
+
# Create new squid from EVM template
|
|
195
|
+
sqd init my-squid -t evm
|
|
196
|
+
|
|
197
|
+
# Generate ABI types
|
|
198
|
+
npx squid-evm-typegen src/abi erc721.json
|
|
199
|
+
# or from Etherscan:
|
|
200
|
+
npx squid-evm-typegen src/abi 0x...contractAddress --etherscan-api https://api.etherscan.io/api
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Processor Configuration
|
|
204
|
+
```typescript
|
|
205
|
+
// src/processor.ts
|
|
206
|
+
import { EvmBatchProcessor } from '@subsquid/evm-processor'
|
|
207
|
+
import { lookupArchive } from '@subsquid/archive-registry'
|
|
208
|
+
|
|
209
|
+
export const processor = new EvmBatchProcessor()
|
|
210
|
+
// Use SQD Network for fast batch data access
|
|
211
|
+
.setGateway(lookupArchive('eth-mainnet'))
|
|
212
|
+
// Also connect to RPC for real-time blocks
|
|
213
|
+
.setRpcEndpoint(process.env.RPC_ENDPOINT)
|
|
214
|
+
.setFinalityConfirmation(75)
|
|
215
|
+
.setBlockRange({ from: 18_000_000 })
|
|
216
|
+
.addLog({
|
|
217
|
+
address: ['0x...'], // Contract address
|
|
218
|
+
topic0: [
|
|
219
|
+
// Transfer(address,address,uint256)
|
|
220
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
221
|
+
],
|
|
222
|
+
})
|
|
223
|
+
.addTransaction({
|
|
224
|
+
to: ['0x...'],
|
|
225
|
+
sighash: ['0xa9059cbb'], // transfer(address,uint256)
|
|
226
|
+
})
|
|
227
|
+
.setFields({
|
|
228
|
+
log: { transactionHash: true },
|
|
229
|
+
transaction: { from: true, value: true, hash: true },
|
|
230
|
+
})
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Entity Schema
|
|
234
|
+
```graphql
|
|
235
|
+
# schema.graphql — generates TypeORM entities
|
|
236
|
+
type Token @entity {
|
|
237
|
+
id: ID!
|
|
238
|
+
name: String!
|
|
239
|
+
symbol: String!
|
|
240
|
+
totalSupply: BigInt!
|
|
241
|
+
transfers: [Transfer!]! @derivedFrom(field: "token")
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
type Transfer @entity {
|
|
245
|
+
id: ID!
|
|
246
|
+
token: Token!
|
|
247
|
+
from: String! @index
|
|
248
|
+
to: String! @index
|
|
249
|
+
value: BigInt!
|
|
250
|
+
blockNumber: Int!
|
|
251
|
+
timestamp: DateTime!
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
```bash
|
|
255
|
+
# Generate TypeORM entities from schema
|
|
256
|
+
npx squid-typeorm-codegen
|
|
257
|
+
npx squid-typeorm-migration generate
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Batch Processing Logic
|
|
261
|
+
```typescript
|
|
262
|
+
// src/main.ts
|
|
263
|
+
import { TypeormDatabase } from '@subsquid/typeorm-store'
|
|
264
|
+
import { processor } from './processor'
|
|
265
|
+
import { Transfer } from './model'
|
|
266
|
+
import * as erc721 from './abi/erc721'
|
|
267
|
+
|
|
268
|
+
processor.run(new TypeormDatabase(), async (ctx) => {
|
|
269
|
+
const transfers: Transfer[] = []
|
|
270
|
+
|
|
271
|
+
for (const block of ctx.blocks) {
|
|
272
|
+
for (const log of block.logs) {
|
|
273
|
+
if (log.topics[0] === erc721.events.Transfer.topic) {
|
|
274
|
+
const { from, to, tokenId } = erc721.events.Transfer.decode(log)
|
|
275
|
+
|
|
276
|
+
transfers.push(new Transfer({
|
|
277
|
+
id: log.id,
|
|
278
|
+
from,
|
|
279
|
+
to,
|
|
280
|
+
tokenId: tokenId.toString(),
|
|
281
|
+
blockNumber: block.header.height,
|
|
282
|
+
timestamp: new Date(block.header.timestamp),
|
|
283
|
+
}))
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Batch insert — much faster than individual saves
|
|
289
|
+
await ctx.store.insert(transfers)
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Deployment
|
|
294
|
+
```bash
|
|
295
|
+
# Local development
|
|
296
|
+
docker compose up -d # Start Postgres
|
|
297
|
+
npx squid-typeorm-migration apply
|
|
298
|
+
node -r dotenv/config lib/main.js
|
|
299
|
+
|
|
300
|
+
# Deploy to SQD Cloud
|
|
301
|
+
sqd deploy .
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Key Advantages
|
|
305
|
+
| Feature | Detail |
|
|
306
|
+
|---------|--------|
|
|
307
|
+
| **Batch Processing** | Process 1000s of blocks per batch (faster than event-by-event) |
|
|
308
|
+
| **SQD Network** | Decentralized data lake, no RPC needed for historical data |
|
|
309
|
+
| **Multi-chain** | 100+ EVM and Substrate networks supported |
|
|
310
|
+
| **Type-safe** | ABI typegen for events, functions, and multicall |
|
|
311
|
+
| **Multicall Support** | Built-in `Multicall` facade for batch RPC reads |
|
|
312
|
+
| **Real-time** | Connect RPC endpoint for latest blocks |
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Envio (HyperSync)
|
|
317
|
+
|
|
318
|
+
### Setup
|
|
319
|
+
```bash
|
|
320
|
+
npx envio init
|
|
321
|
+
|
|
322
|
+
# envio.config.ts
|
|
323
|
+
export default {
|
|
324
|
+
networks: [{
|
|
325
|
+
id: 1,
|
|
326
|
+
rpc_url: process.env.ETH_RPC_URL,
|
|
327
|
+
contracts: [{
|
|
328
|
+
name: 'Token',
|
|
329
|
+
address: '0x...',
|
|
330
|
+
abi_file_path: './abis/Token.json',
|
|
331
|
+
events: ['Transfer'],
|
|
332
|
+
start_block: 18_000_000,
|
|
333
|
+
}],
|
|
334
|
+
}],
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Event Handlers (Envio)
|
|
339
|
+
```typescript
|
|
340
|
+
import { TokenContract } from '../generated/src/Handlers.gen'
|
|
341
|
+
|
|
342
|
+
TokenContract.Transfer.handler(async ({ event, context }) => {
|
|
343
|
+
const transfer = {
|
|
344
|
+
id: event.transactionHash + '-' + event.logIndex,
|
|
345
|
+
from: event.params.from,
|
|
346
|
+
to: event.params.to,
|
|
347
|
+
value: event.params.value,
|
|
348
|
+
}
|
|
349
|
+
context.Transfer.set(transfer)
|
|
350
|
+
})
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### HyperSync Advantage
|
|
354
|
+
- **100x faster** than RPC for historical data
|
|
355
|
+
- Raw data streamed from Envio's optimized indices
|
|
356
|
+
- No rate limits or throttling
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
168
360
|
## Indexer Selection Guide
|
|
169
361
|
|
|
170
|
-
| Tool | Best For | Language | Hosting |
|
|
171
|
-
|
|
172
|
-
| **The Graph** | Decentralized, production | AssemblyScript | Decentralized |
|
|
173
|
-
| **Ponder** | TypeScript-native, fast dev | TypeScript | Self-hosted |
|
|
174
|
-
| **
|
|
175
|
-
| **
|
|
176
|
-
| **
|
|
177
|
-
| **Custom** | Full control | Any | Self-hosted |
|
|
362
|
+
| Tool | Best For | Language | Speed | Hosting | Multi-chain |
|
|
363
|
+
|------|----------|----------|-------|---------|-------------|
|
|
364
|
+
| **The Graph** | Decentralized, production | AssemblyScript | Medium | Decentralized | ✅ |
|
|
365
|
+
| **Ponder** | TypeScript-native, fast dev | TypeScript | Fast | Self-hosted | ✅ |
|
|
366
|
+
| **Subsquid** | High-throughput, archive data | TypeScript | Very Fast | SQD Cloud | ✅ 100+ |
|
|
367
|
+
| **Envio** | Ultra-fast historical sync | TypeScript | Fastest | Managed | ✅ |
|
|
368
|
+
| **Goldsky** | Mirror + Subgraphs | GraphQL | Fast | Managed | ✅ |
|
|
369
|
+
| **Custom** | Full control | Any | Variable | Self-hosted | Manual |
|
|
370
|
+
|
|
371
|
+
### When to Use What
|
|
372
|
+
- **Decentralized & battle-tested?** → The Graph
|
|
373
|
+
- **TypeScript-native, fast iteration?** → Ponder
|
|
374
|
+
- **Multi-chain with batch processing?** → Subsquid
|
|
375
|
+
- **Fastest historical sync possible?** → Envio (HyperSync)
|
|
376
|
+
- **Need subgraphs + real-time streams?** → Goldsky
|
|
178
377
|
|
|
179
378
|
---
|
|
180
379
|
|
|
@@ -210,16 +409,33 @@ query TopHolders($token: Bytes!) {
|
|
|
210
409
|
transferCount
|
|
211
410
|
}
|
|
212
411
|
}
|
|
412
|
+
|
|
413
|
+
# Subsquid-style query with pagination
|
|
414
|
+
query NFTTransfers($owner: String!, $offset: Int) {
|
|
415
|
+
transfers(
|
|
416
|
+
where: { to_eq: $owner }
|
|
417
|
+
limit: 50
|
|
418
|
+
offset: $offset
|
|
419
|
+
orderBy: blockNumber_DESC
|
|
420
|
+
) {
|
|
421
|
+
tokenId
|
|
422
|
+
from
|
|
423
|
+
to
|
|
424
|
+
blockNumber
|
|
425
|
+
timestamp
|
|
426
|
+
}
|
|
427
|
+
}
|
|
213
428
|
```
|
|
214
429
|
|
|
215
430
|
---
|
|
216
431
|
|
|
217
432
|
## Best Practices
|
|
218
433
|
|
|
219
|
-
- Use `immutable: true`
|
|
434
|
+
- Use `immutable: true` (Graph) or batch inserts (Subsquid) for event entities
|
|
220
435
|
- Cache derived values instead of computing in queries
|
|
221
|
-
- Use `@derivedFrom` for
|
|
436
|
+
- Use `@derivedFrom` / `@index` for relationship lookups
|
|
222
437
|
- Index only the data you need — less storage, faster sync
|
|
223
|
-
- Handle chain reorgs gracefully
|
|
224
|
-
- Test handlers with Matchstick or
|
|
225
|
-
- Monitor indexing health and
|
|
438
|
+
- Handle chain reorgs gracefully (use finality confirmations)
|
|
439
|
+
- Test handlers with Matchstick (Graph) or unit tests (others)
|
|
440
|
+
- Monitor indexing health, sync lag, and data freshness
|
|
441
|
+
- Use multicall for batch on-chain reads during indexing
|