liquid-sdk 1.4.0 → 1.5.1
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/dist/index.d.mts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +95 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
- package/skills/README.md +51 -0
- package/skills/bid-in-auction.md +296 -0
- package/skills/deploy-token.md +260 -0
- package/skills/index-tokens.md +501 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# Skill: Index Liquid Protocol Tokens
|
|
2
|
+
|
|
3
|
+
You are an AI agent that indexes and tracks token deployments on Liquid Protocol. This skill teaches you how to discover tokens, build an index, track new launches in real-time, and query the full on-chain state of any Liquid token on Base.
|
|
4
|
+
|
|
5
|
+
## What You Can Index
|
|
6
|
+
|
|
7
|
+
Every token deployed through Liquid Protocol emits a `TokenCreated` event with rich on-chain data:
|
|
8
|
+
|
|
9
|
+
- Token address, name, symbol, image URL
|
|
10
|
+
- Deployer address and admin address
|
|
11
|
+
- Metadata (description, social links, audit URLs)
|
|
12
|
+
- Context (originating interface, platform, social post ID)
|
|
13
|
+
- Uniswap V4 pool ID, hook contract, paired token
|
|
14
|
+
- LP locker address and MEV module
|
|
15
|
+
- Extensions (dev buy, vault, airdrop, etc.)
|
|
16
|
+
- Block number (for pagination/ordering)
|
|
17
|
+
|
|
18
|
+
All of this is queryable directly from Base mainnet — no backend, no API keys, no database required.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install liquid-sdk viem
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { createPublicClient, http } from "viem";
|
|
30
|
+
import { base } from "viem/chains";
|
|
31
|
+
import { LiquidSDK } from "liquid-sdk";
|
|
32
|
+
|
|
33
|
+
// Read-only — no wallet needed for indexing
|
|
34
|
+
const publicClient = createPublicClient({ chain: base, transport: http(RPC_URL) });
|
|
35
|
+
const sdk = new LiquidSDK({ publicClient });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Core Indexing Methods
|
|
39
|
+
|
|
40
|
+
### 1. Get All Tokens
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const allTokens = await sdk.getTokens();
|
|
44
|
+
|
|
45
|
+
console.log(`Total tokens: ${allTokens.length}`);
|
|
46
|
+
for (const token of allTokens) {
|
|
47
|
+
console.log(`${token.tokenName} (${token.tokenSymbol}) — ${token.tokenAddress}`);
|
|
48
|
+
console.log(` Deployed by: ${token.msgSender}`);
|
|
49
|
+
console.log(` Pool ID: ${token.poolId}`);
|
|
50
|
+
console.log(` Block: ${token.blockNumber}`);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2. Get Tokens by Deployer
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// All tokens launched by a specific wallet
|
|
58
|
+
const myTokens = await sdk.getTokens({ deployer: "0x1234..." });
|
|
59
|
+
|
|
60
|
+
// Or use the convenience wrapper
|
|
61
|
+
const myTokens2 = await sdk.getDeployedTokens("0x1234...");
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Note: `msgSender` (deployer) is **not indexed** on-chain, so the SDK fetches all events and filters client-side. For large ranges, use block pagination to keep RPC calls manageable.
|
|
65
|
+
|
|
66
|
+
### 3. Look Up a Single Token
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Fast O(1) lookup — tokenAddress IS indexed on-chain
|
|
70
|
+
const token = await sdk.getTokenEvent("0xTokenAddress...");
|
|
71
|
+
|
|
72
|
+
if (token) {
|
|
73
|
+
console.log(`${token.tokenName} (${token.tokenSymbol})`);
|
|
74
|
+
console.log(`Pool: ${token.poolId}`);
|
|
75
|
+
console.log(`Hook: ${token.poolHook}`);
|
|
76
|
+
console.log(`Locker: ${token.locker}`);
|
|
77
|
+
console.log(`MEV Module: ${token.mevModule}`);
|
|
78
|
+
console.log(`Extensions: ${token.extensions}`);
|
|
79
|
+
console.log(`Image: ${token.tokenImage}`);
|
|
80
|
+
} else {
|
|
81
|
+
console.log("Not a Liquid Protocol token");
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 4. Paginate with Block Ranges
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Page through tokens using block numbers
|
|
89
|
+
const BLOCK_SIZE = 100_000n;
|
|
90
|
+
let fromBlock = 20_000_000n; // start from factory deployment block
|
|
91
|
+
let allTokens: TokenCreatedEvent[] = [];
|
|
92
|
+
|
|
93
|
+
while (true) {
|
|
94
|
+
const toBlock = fromBlock + BLOCK_SIZE;
|
|
95
|
+
const page = await sdk.getTokens({ fromBlock, toBlock });
|
|
96
|
+
|
|
97
|
+
allTokens.push(...page);
|
|
98
|
+
console.log(`Fetched ${page.length} tokens from blocks ${fromBlock}–${toBlock}`);
|
|
99
|
+
|
|
100
|
+
if (page.length === 0) break; // no more tokens
|
|
101
|
+
fromBlock = toBlock + 1n;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(`Total indexed: ${allTokens.length} tokens`);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 5. Cursor-Based Pagination
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Use the last token's blockNumber as cursor for next page
|
|
111
|
+
let cursor = 0n;
|
|
112
|
+
const PAGE_SIZE = 50;
|
|
113
|
+
|
|
114
|
+
async function getNextPage() {
|
|
115
|
+
const tokens = await sdk.getTokens({ fromBlock: cursor + 1n });
|
|
116
|
+
|
|
117
|
+
if (tokens.length > 0) {
|
|
118
|
+
cursor = tokens[tokens.length - 1].blockNumber!;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return tokens;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## TokenCreatedEvent Schema
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
interface TokenCreatedEvent {
|
|
129
|
+
// Addresses
|
|
130
|
+
msgSender: Address; // Wallet that called deployToken()
|
|
131
|
+
tokenAddress: Address; // The deployed ERC-20 (indexed on-chain)
|
|
132
|
+
tokenAdmin: Address; // Can update image/metadata (indexed on-chain)
|
|
133
|
+
|
|
134
|
+
// Token Metadata
|
|
135
|
+
tokenName: string; // e.g., "My Token"
|
|
136
|
+
tokenSymbol: string; // e.g., "MTK"
|
|
137
|
+
tokenImage: string; // Image URL or empty string
|
|
138
|
+
tokenMetadata: string; // JSON string — parse with parseMetadata()
|
|
139
|
+
tokenContext: string; // JSON string — parse with parseContext()
|
|
140
|
+
|
|
141
|
+
// Pool Configuration
|
|
142
|
+
startingTick: number; // Initial Uniswap V4 tick (int24)
|
|
143
|
+
poolHook: Address; // Hook contract (static or dynamic fee)
|
|
144
|
+
poolId: Hex; // Uniswap V4 pool identifier (bytes32)
|
|
145
|
+
pairedToken: Address; // Quote token (usually WETH)
|
|
146
|
+
|
|
147
|
+
// Infrastructure
|
|
148
|
+
locker: Address; // LP locker contract
|
|
149
|
+
mevModule: Address; // MEV module (usually Sniper Auction V2)
|
|
150
|
+
extensionsSupply: bigint; // Total supply allocated to extensions (wei)
|
|
151
|
+
extensions: Address[]; // Active extension contracts
|
|
152
|
+
|
|
153
|
+
// Block Info
|
|
154
|
+
blockNumber?: bigint; // Block where event was emitted
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Parsing Metadata and Context
|
|
159
|
+
|
|
160
|
+
The `tokenMetadata` and `tokenContext` fields are JSON strings. Use the SDK's parsers:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { parseMetadata, parseContext } from "liquid-sdk";
|
|
164
|
+
|
|
165
|
+
const token = await sdk.getTokenEvent(tokenAddress);
|
|
166
|
+
|
|
167
|
+
// Parse metadata
|
|
168
|
+
const meta = parseMetadata(token.tokenMetadata);
|
|
169
|
+
if (meta) {
|
|
170
|
+
console.log("Description:", meta.description);
|
|
171
|
+
console.log("Social links:", meta.socialMediaUrls); // [{ platform, url }]
|
|
172
|
+
console.log("Audit URLs:", meta.auditUrls);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Parse context (deployment provenance)
|
|
176
|
+
const ctx = parseContext(token.tokenContext);
|
|
177
|
+
if (ctx) {
|
|
178
|
+
console.log("Interface:", ctx.interface); // "SDK", "Rainbow Wallet", etc.
|
|
179
|
+
console.log("Platform:", ctx.platform); // "Farcaster", "Twitter", etc.
|
|
180
|
+
console.log("Message ID:", ctx.messageId); // Social post ID
|
|
181
|
+
console.log("User ID:", ctx.id);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Context types:**
|
|
186
|
+
```typescript
|
|
187
|
+
interface LiquidContext {
|
|
188
|
+
interface: string; // System that deployed (e.g., "SDK", "My App")
|
|
189
|
+
platform?: string; // Social platform
|
|
190
|
+
messageId?: string; // Social post/cast ID
|
|
191
|
+
id?: string; // User ID on platform
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
interface LiquidMetadata {
|
|
195
|
+
description?: string;
|
|
196
|
+
socialMediaUrls?: { platform: string; url: string }[];
|
|
197
|
+
auditUrls?: string[];
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Enriching Token Data
|
|
202
|
+
|
|
203
|
+
Once you have the token event, you can query additional on-chain state:
|
|
204
|
+
|
|
205
|
+
### Full Token Info
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const info = await sdk.getTokenInfo(tokenAddress);
|
|
209
|
+
console.log(`${info.name} (${info.symbol})`);
|
|
210
|
+
console.log(`Decimals: ${info.decimals}`); // always 18
|
|
211
|
+
console.log(`Supply: ${info.totalSupply}`); // 100 billion * 10^18
|
|
212
|
+
console.log(`Hook: ${info.deployment.hook}`);
|
|
213
|
+
console.log(`Locker: ${info.deployment.locker}`);
|
|
214
|
+
console.log(`Extensions: ${info.deployment.extensions}`);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Reward Configuration
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
const rewards = await sdk.getTokenRewards(tokenAddress);
|
|
221
|
+
console.log("Recipients:", rewards.rewardRecipients);
|
|
222
|
+
console.log("Splits (bps):", rewards.rewardBps); // e.g., [7000, 3000]
|
|
223
|
+
console.log("Admins:", rewards.rewardAdmins);
|
|
224
|
+
console.log("Pool key:", rewards.poolKey);
|
|
225
|
+
console.log("Position ID:", rewards.positionId);
|
|
226
|
+
console.log("Num positions:", rewards.numPositions);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Pool State
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
const poolConfig = await sdk.getPoolConfig(poolId);
|
|
233
|
+
console.log("Base fee:", poolConfig.baseFee);
|
|
234
|
+
console.log("Max LP fee:", poolConfig.maxLpFee);
|
|
235
|
+
|
|
236
|
+
const feeState = await sdk.getPoolFeeState(poolId);
|
|
237
|
+
console.log("Reference tick:", feeState.referenceTick);
|
|
238
|
+
console.log("Last swap:", feeState.lastSwapTimestamp);
|
|
239
|
+
|
|
240
|
+
const createdAt = await sdk.getPoolCreationTimestamp(poolId);
|
|
241
|
+
console.log("Pool created:", new Date(Number(createdAt) * 1000));
|
|
242
|
+
|
|
243
|
+
const isToken0 = await sdk.isLiquidToken0(poolId);
|
|
244
|
+
console.log("Liquid is token0:", isToken0);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### MEV / Auction State
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const auction = await sdk.getAuctionState(poolId);
|
|
251
|
+
console.log("Auction round:", auction.round);
|
|
252
|
+
console.log("Current fee:", auction.currentFee);
|
|
253
|
+
console.log("Gas peg:", auction.gasPeg);
|
|
254
|
+
|
|
255
|
+
const unlockTime = await sdk.getPoolUnlockTime(poolId);
|
|
256
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
257
|
+
console.log("Pool locked:", now < unlockTime);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Fee & Reward Balances
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// Check accrued fees
|
|
264
|
+
const fees = await sdk.getAvailableFees(feeOwner, tokenAddress);
|
|
265
|
+
const claimable = await sdk.getFeesToClaim(feeOwner, tokenAddress);
|
|
266
|
+
console.log("Total fees:", fees);
|
|
267
|
+
console.log("Claimable now:", claimable);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Vault State (if token has vault extension)
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const vault = await sdk.getVaultAllocation(tokenAddress);
|
|
274
|
+
console.log("Total locked:", vault.amountTotal);
|
|
275
|
+
console.log("Claimed:", vault.amountClaimed);
|
|
276
|
+
console.log("Lockup ends:", new Date(Number(vault.lockupEndTime) * 1000));
|
|
277
|
+
console.log("Vesting ends:", new Date(Number(vault.vestingEndTime) * 1000));
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Airdrop State (if token has airdrop extension)
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const airdrop = await sdk.getAirdropInfo(tokenAddress);
|
|
284
|
+
console.log("Merkle root:", airdrop.merkleRoot);
|
|
285
|
+
console.log("Total supply:", airdrop.totalSupply);
|
|
286
|
+
console.log("Claimed:", airdrop.totalClaimed);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Complete Example: Build a Token Index
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { createPublicClient, http, formatEther } from "viem";
|
|
293
|
+
import { base } from "viem/chains";
|
|
294
|
+
import { LiquidSDK, parseMetadata, parseContext, ADDRESSES } from "liquid-sdk";
|
|
295
|
+
|
|
296
|
+
async function buildIndex() {
|
|
297
|
+
const publicClient = createPublicClient({ chain: base, transport: http(RPC_URL) });
|
|
298
|
+
const sdk = new LiquidSDK({ publicClient });
|
|
299
|
+
|
|
300
|
+
// Fetch all tokens
|
|
301
|
+
const tokens = await sdk.getTokens();
|
|
302
|
+
console.log(`Indexing ${tokens.length} tokens...\n`);
|
|
303
|
+
|
|
304
|
+
const index = [];
|
|
305
|
+
|
|
306
|
+
for (const token of tokens) {
|
|
307
|
+
const meta = parseMetadata(token.tokenMetadata);
|
|
308
|
+
const ctx = parseContext(token.tokenContext);
|
|
309
|
+
|
|
310
|
+
const entry = {
|
|
311
|
+
address: token.tokenAddress,
|
|
312
|
+
name: token.tokenName,
|
|
313
|
+
symbol: token.tokenSymbol,
|
|
314
|
+
image: token.tokenImage,
|
|
315
|
+
description: meta?.description ?? null,
|
|
316
|
+
socialLinks: meta?.socialMediaUrls ?? [],
|
|
317
|
+
deployer: token.msgSender,
|
|
318
|
+
deployedAt: token.blockNumber,
|
|
319
|
+
poolId: token.poolId,
|
|
320
|
+
pairedToken: token.pairedToken,
|
|
321
|
+
hook: token.poolHook,
|
|
322
|
+
locker: token.locker,
|
|
323
|
+
mevModule: token.mevModule,
|
|
324
|
+
extensions: token.extensions,
|
|
325
|
+
launchedVia: ctx?.interface ?? "unknown",
|
|
326
|
+
platform: ctx?.platform ?? null,
|
|
327
|
+
castId: ctx?.messageId ?? null,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
index.push(entry);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return index;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Usage
|
|
337
|
+
const index = await buildIndex();
|
|
338
|
+
console.log(JSON.stringify(index, null, 2));
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Complete Example: Real-Time Token Monitor
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { createPublicClient, http } from "viem";
|
|
345
|
+
import { base } from "viem/chains";
|
|
346
|
+
import { LiquidSDK, parseContext, ADDRESSES } from "liquid-sdk";
|
|
347
|
+
|
|
348
|
+
async function monitorNewTokens(callback: (token: any) => void) {
|
|
349
|
+
const publicClient = createPublicClient({ chain: base, transport: http(RPC_URL) });
|
|
350
|
+
const sdk = new LiquidSDK({ publicClient });
|
|
351
|
+
|
|
352
|
+
let lastBlock = await publicClient.getBlockNumber();
|
|
353
|
+
|
|
354
|
+
console.log(`Monitoring from block ${lastBlock}...`);
|
|
355
|
+
|
|
356
|
+
setInterval(async () => {
|
|
357
|
+
try {
|
|
358
|
+
const currentBlock = await publicClient.getBlockNumber();
|
|
359
|
+
if (currentBlock <= lastBlock) return;
|
|
360
|
+
|
|
361
|
+
const newTokens = await sdk.getTokens({
|
|
362
|
+
fromBlock: lastBlock + 1n,
|
|
363
|
+
toBlock: currentBlock,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
for (const token of newTokens) {
|
|
367
|
+
const ctx = parseContext(token.tokenContext);
|
|
368
|
+
console.log(`\nNew token: ${token.tokenName} (${token.tokenSymbol})`);
|
|
369
|
+
console.log(` Address: ${token.tokenAddress}`);
|
|
370
|
+
console.log(` Deployer: ${token.msgSender}`);
|
|
371
|
+
console.log(` Pool ID: ${token.poolId}`);
|
|
372
|
+
console.log(` Launched via: ${ctx?.interface ?? "unknown"}`);
|
|
373
|
+
callback(token);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
lastBlock = currentBlock;
|
|
377
|
+
} catch (err) {
|
|
378
|
+
console.error("Poll error:", err);
|
|
379
|
+
}
|
|
380
|
+
}, 2000); // poll every 2 seconds (Base block time)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Usage
|
|
384
|
+
monitorNewTokens((token) => {
|
|
385
|
+
// Process new token — save to DB, send alert, etc.
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Complete Example: Enrich a Token for Display
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
async function enrichToken(tokenAddress: `0x${string}`) {
|
|
393
|
+
const publicClient = createPublicClient({ chain: base, transport: http(RPC_URL) });
|
|
394
|
+
const sdk = new LiquidSDK({ publicClient });
|
|
395
|
+
|
|
396
|
+
// Parallel queries for speed
|
|
397
|
+
const [event, info, rewards] = await Promise.all([
|
|
398
|
+
sdk.getTokenEvent(tokenAddress),
|
|
399
|
+
sdk.getTokenInfo(tokenAddress),
|
|
400
|
+
sdk.getTokenRewards(tokenAddress),
|
|
401
|
+
]);
|
|
402
|
+
|
|
403
|
+
if (!event) return null;
|
|
404
|
+
|
|
405
|
+
const meta = parseMetadata(event.tokenMetadata);
|
|
406
|
+
|
|
407
|
+
// Pool state (parallel)
|
|
408
|
+
const [poolConfig, feeState, createdAt, auctionState] = await Promise.all([
|
|
409
|
+
sdk.getPoolConfig(event.poolId),
|
|
410
|
+
sdk.getPoolFeeState(event.poolId),
|
|
411
|
+
sdk.getPoolCreationTimestamp(event.poolId),
|
|
412
|
+
sdk.getAuctionState(event.poolId),
|
|
413
|
+
]);
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
token: {
|
|
417
|
+
address: tokenAddress,
|
|
418
|
+
name: info.name,
|
|
419
|
+
symbol: info.symbol,
|
|
420
|
+
decimals: info.decimals,
|
|
421
|
+
totalSupply: info.totalSupply.toString(),
|
|
422
|
+
image: event.tokenImage,
|
|
423
|
+
description: meta?.description,
|
|
424
|
+
socialLinks: meta?.socialMediaUrls,
|
|
425
|
+
},
|
|
426
|
+
deployment: {
|
|
427
|
+
deployer: event.msgSender,
|
|
428
|
+
admin: event.tokenAdmin,
|
|
429
|
+
block: event.blockNumber,
|
|
430
|
+
hook: event.poolHook,
|
|
431
|
+
locker: event.locker,
|
|
432
|
+
extensions: event.extensions,
|
|
433
|
+
},
|
|
434
|
+
pool: {
|
|
435
|
+
id: event.poolId,
|
|
436
|
+
pairedToken: event.pairedToken,
|
|
437
|
+
baseFee: poolConfig.baseFee,
|
|
438
|
+
maxLpFee: poolConfig.maxLpFee,
|
|
439
|
+
createdAt: Number(createdAt),
|
|
440
|
+
referenceTick: feeState.referenceTick,
|
|
441
|
+
},
|
|
442
|
+
rewards: {
|
|
443
|
+
recipients: rewards.rewardRecipients,
|
|
444
|
+
splits: rewards.rewardBps,
|
|
445
|
+
numPositions: Number(rewards.numPositions),
|
|
446
|
+
},
|
|
447
|
+
auction: {
|
|
448
|
+
round: Number(auctionState.round),
|
|
449
|
+
currentFee: auctionState.currentFee,
|
|
450
|
+
gasPeg: auctionState.gasPeg.toString(),
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## On-Chain Event Signature
|
|
457
|
+
|
|
458
|
+
The `TokenCreated` event emitted by the Liquid Factory:
|
|
459
|
+
|
|
460
|
+
```solidity
|
|
461
|
+
event TokenCreated(
|
|
462
|
+
address msgSender, // NOT indexed — filtered client-side
|
|
463
|
+
address indexed tokenAddress, // indexed — efficient single lookup
|
|
464
|
+
address indexed tokenAdmin, // indexed — filter by admin
|
|
465
|
+
string tokenImage,
|
|
466
|
+
string tokenName,
|
|
467
|
+
string tokenSymbol,
|
|
468
|
+
string tokenMetadata,
|
|
469
|
+
string tokenContext,
|
|
470
|
+
int24 startingTick,
|
|
471
|
+
address poolHook,
|
|
472
|
+
bytes32 poolId,
|
|
473
|
+
address pairedToken,
|
|
474
|
+
address locker,
|
|
475
|
+
address mevModule,
|
|
476
|
+
uint256 extensionsSupply,
|
|
477
|
+
address[] extensions
|
|
478
|
+
);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Indexing implications:**
|
|
482
|
+
- `getTokenEvent(address)` is a single RPC call (uses indexed `tokenAddress`)
|
|
483
|
+
- `getTokens({ deployer })` requires fetching all events then filtering (msgSender not indexed)
|
|
484
|
+
- Block-range pagination is the primary scaling mechanism for large datasets
|
|
485
|
+
|
|
486
|
+
## Performance Tips
|
|
487
|
+
|
|
488
|
+
1. **Use `getTokenEvent()` for single lookups** — it's O(1) via the indexed field
|
|
489
|
+
2. **Paginate with block ranges** for bulk indexing — avoid fetching the entire history in one call
|
|
490
|
+
3. **Parallelize enrichment queries** with `Promise.all()` — pool config, rewards, auction state are all independent reads
|
|
491
|
+
4. **Cache block numbers** to avoid re-indexing already-seen tokens
|
|
492
|
+
5. **Base block time is ~2s** — poll at this interval for near-real-time monitoring
|
|
493
|
+
|
|
494
|
+
## Contract Address
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import { ADDRESSES } from "liquid-sdk";
|
|
498
|
+
|
|
499
|
+
ADDRESSES.FACTORY // 0x0000003482fe299E72d4908368044A8A173BE576
|
|
500
|
+
// All TokenCreated events are emitted from this address
|
|
501
|
+
```
|