pyre-world-kit 2.0.12 → 3.0.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.
Files changed (53) hide show
  1. package/.prettierrc.json +6 -0
  2. package/dist/index.d.ts +46 -4
  3. package/dist/index.js +105 -85
  4. package/dist/providers/action.provider.d.ts +46 -0
  5. package/dist/providers/action.provider.js +331 -0
  6. package/dist/providers/intel.provider.d.ts +29 -0
  7. package/dist/providers/intel.provider.js +363 -0
  8. package/dist/providers/mapper.provider.d.ts +197 -0
  9. package/dist/providers/mapper.provider.js +158 -0
  10. package/dist/providers/registry.provider.d.ts +25 -0
  11. package/dist/providers/registry.provider.js +229 -0
  12. package/dist/providers/state.provider.d.ts +42 -0
  13. package/dist/providers/state.provider.js +348 -0
  14. package/dist/pyre_world.json +34 -229
  15. package/dist/types/action.types.d.ts +41 -0
  16. package/dist/types/action.types.js +2 -0
  17. package/dist/types/intel.types.d.ts +20 -0
  18. package/dist/types/intel.types.js +2 -0
  19. package/dist/types/mapper.types.d.ts +27 -0
  20. package/dist/types/mapper.types.js +22 -0
  21. package/dist/types/registry.types.d.ts +0 -0
  22. package/dist/types/registry.types.js +1 -0
  23. package/dist/types/state.types.d.ts +112 -0
  24. package/dist/types/state.types.js +2 -0
  25. package/dist/types.d.ts +8 -24
  26. package/dist/util.d.ts +29 -0
  27. package/dist/util.js +144 -0
  28. package/dist/vanity.d.ts +3 -3
  29. package/dist/vanity.js +18 -15
  30. package/package.json +4 -2
  31. package/readme.md +184 -142
  32. package/src/index.ts +133 -92
  33. package/src/providers/action.provider.ts +443 -0
  34. package/src/providers/intel.provider.ts +383 -0
  35. package/src/providers/mapper.provider.ts +195 -0
  36. package/src/providers/registry.provider.ts +277 -0
  37. package/src/providers/state.provider.ts +357 -0
  38. package/src/pyre_world.json +35 -230
  39. package/src/types/action.types.ts +76 -0
  40. package/src/types/intel.types.ts +22 -0
  41. package/src/types/mapper.types.ts +84 -0
  42. package/src/types/registry.types.ts +0 -0
  43. package/src/types/state.types.ts +144 -0
  44. package/src/types.ts +329 -333
  45. package/src/util.ts +148 -0
  46. package/src/vanity.ts +27 -14
  47. package/tests/test_e2e.ts +339 -172
  48. package/src/actions.ts +0 -719
  49. package/src/intel.ts +0 -521
  50. package/src/mappers.ts +0 -302
  51. package/src/registry.ts +0 -317
  52. package/tests/test_devnet_e2e.ts +0 -401
  53. package/tests/test_sim.ts +0 -458
package/src/intel.ts DELETED
@@ -1,521 +0,0 @@
1
- /**
2
- * Pyre Kit Intel
3
- *
4
- * Game-specific utility functions that compose torchsdk reads into
5
- * strategic intelligence. Agents use these to reason about the world.
6
- */
7
-
8
- import { Connection, PublicKey } from '@solana/web3.js';
9
- import {
10
- getTokens,
11
- getToken,
12
- getHolders,
13
- getMessages,
14
- getVaultForWallet,
15
- verifySaid,
16
- PROGRAM_ID,
17
- } from 'torchsdk';
18
- import type { TokenDetail, TokenSummary } from 'torchsdk';
19
-
20
- import { mapFactionStatus } from './mappers';
21
- import { isPyreMint, getBondingCurvePda, getTokenTreasuryPda, getTreasuryLockPda } from './vanity';
22
- import type {
23
- FactionPower,
24
- AllianceCluster,
25
- RivalFaction,
26
- AgentProfile,
27
- AgentFactionPosition,
28
- WorldEvent,
29
- WorldStats,
30
- FactionStatus,
31
- } from './types';
32
-
33
- // ─── Faction Power & Rankings ──────────────────────────────────────
34
-
35
- /**
36
- * Calculate a faction's power score.
37
- *
38
- * Score = (market_cap_sol * 0.4) + (members * 0.2) + (war_chest_sol * 0.2)
39
- * + (rallies * 0.1) + (progress * 0.1)
40
- *
41
- * Normalized to make comparison easy. Higher = stronger.
42
- */
43
- export async function getFactionPower(
44
- connection: Connection,
45
- mint: string,
46
- ): Promise<FactionPower> {
47
- const t = await getToken(connection, mint);
48
- const score = computePowerScore(t);
49
- return {
50
- mint: t.mint,
51
- name: t.name,
52
- symbol: t.symbol,
53
- score,
54
- market_cap_sol: t.market_cap_sol,
55
- members: t.holders ?? 0,
56
- war_chest_sol: t.treasury_sol_balance,
57
- rallies: t.stars,
58
- progress_percent: t.progress_percent,
59
- status: mapFactionStatus(t.status),
60
- };
61
- }
62
-
63
- /**
64
- * Ranked leaderboard of all factions by power score.
65
- */
66
- export async function getFactionLeaderboard(
67
- connection: Connection,
68
- opts?: { status?: FactionStatus; limit?: number },
69
- ): Promise<FactionPower[]> {
70
- // Fetch all tokens (up to 1000)
71
- const statusMap: Record<string, string> = {
72
- rising: 'bonding',
73
- ready: 'complete',
74
- ascended: 'migrated',
75
- razed: 'reclaimed',
76
- };
77
- const sdkStatus = opts?.status ? statusMap[opts.status] as any : 'all';
78
- // Fetch more than requested to account for non-pyre tokens being filtered out
79
- const fetchLimit = Math.min((opts?.limit ?? 20) * 3, 100);
80
- const result = await getTokens(connection, { limit: fetchLimit, status: sdkStatus });
81
- const pyreFactions = result.tokens.filter(t => isPyreMint(t.mint));
82
-
83
- const powers: FactionPower[] = pyreFactions.map((t) => ({
84
- mint: t.mint,
85
- name: t.name,
86
- symbol: t.symbol,
87
- score: computePowerScoreFromSummary(t),
88
- market_cap_sol: t.market_cap_sol,
89
- members: t.holders ?? 0,
90
- war_chest_sol: 0, // Not available in summary
91
- rallies: 0, // Not available in summary
92
- progress_percent: t.progress_percent,
93
- status: mapFactionStatus(t.status),
94
- }));
95
-
96
- powers.sort((a, b) => b.score - a.score);
97
- return powers;
98
- }
99
-
100
- // ─── Alliance & Rivalry Detection ──────────────────────────────────
101
-
102
- /**
103
- * Detect alliances: factions with shared members.
104
- *
105
- * Given a set of faction mints, finds wallets holding multiple faction tokens.
106
- * Returns alliance clusters showing which factions share members.
107
- */
108
- export async function detectAlliances(
109
- connection: Connection,
110
- mints: string[],
111
- holderLimit = 50,
112
- ): Promise<AllianceCluster[]> {
113
- // Fetch holders for each faction in parallel
114
- const holdersPerFaction = await Promise.all(
115
- mints.map(async (mint) => {
116
- const result = await getPyreHolders(connection, mint, holderLimit);
117
- return { mint, holders: new Set(result.holders.map(h => h.address)) };
118
- })
119
- );
120
-
121
- // Find overlapping holders between faction pairs
122
- const clusters: AllianceCluster[] = [];
123
- for (let i = 0; i < holdersPerFaction.length; i++) {
124
- for (let j = i + 1; j < holdersPerFaction.length; j++) {
125
- const a = holdersPerFaction[i];
126
- const b = holdersPerFaction[j];
127
- const shared = [...a.holders].filter(h => b.holders.has(h));
128
- if (shared.length > 0) {
129
- const minSize = Math.min(a.holders.size, b.holders.size);
130
- clusters.push({
131
- factions: [a.mint, b.mint],
132
- shared_members: shared.length,
133
- overlap_percent: minSize > 0 ? (shared.length / minSize) * 100 : 0,
134
- });
135
- }
136
- }
137
- }
138
-
139
- clusters.sort((a, b) => b.shared_members - a.shared_members);
140
- return clusters;
141
- }
142
-
143
- /**
144
- * Find rival factions based on recent defection activity.
145
- *
146
- * Looks at recent sell messages to detect agents who have defected
147
- * from or to this faction.
148
- */
149
- export async function getFactionRivals(
150
- connection: Connection,
151
- mint: string,
152
- limit = 50,
153
- ): Promise<RivalFaction[]> {
154
- // Get recent messages (sells include defection messages)
155
- const msgs = await getMessages(connection, mint, limit);
156
- const defectors = new Set(msgs.messages.map(m => m.sender));
157
-
158
- // For each defector, check what other factions they hold
159
- // This is a heuristic — we look at the messages to find patterns
160
- // In practice, agents would track this over time
161
- const rivalCounts = new Map<string, { in: number; out: number }>();
162
-
163
- // Get all factions to cross-reference
164
- const allFactions = await getTokens(connection, { limit: 20, sort: 'volume' });
165
- for (const faction of allFactions.tokens.filter(t => isPyreMint(t.mint))) {
166
- if (faction.mint === mint) continue;
167
- const holders = await getPyreHolders(connection, faction.mint, 50);
168
- const holderAddrs = new Set(holders.holders.map(h => h.address));
169
- const overlap = [...defectors].filter(d => holderAddrs.has(d)).length;
170
- if (overlap > 0) {
171
- rivalCounts.set(faction.mint, {
172
- in: overlap, // Agents from this faction who also hold rival
173
- out: overlap,
174
- ...(rivalCounts.get(faction.mint) ?? {}),
175
- });
176
- }
177
- }
178
-
179
- const rivals: RivalFaction[] = [];
180
- for (const [rivalMint, counts] of rivalCounts) {
181
- const faction = allFactions.tokens.find(t => t.mint === rivalMint);
182
- if (faction) {
183
- rivals.push({
184
- mint: rivalMint,
185
- name: faction.name,
186
- symbol: faction.symbol,
187
- defections_in: counts.in,
188
- defections_out: counts.out,
189
- });
190
- }
191
- }
192
-
193
- rivals.sort((a, b) => (b.defections_in + b.defections_out) - (a.defections_in + a.defections_out));
194
- return rivals;
195
- }
196
-
197
- // ─── Agent Intelligence ────────────────────────────────────────────
198
-
199
- /**
200
- * Build an aggregate profile for an agent wallet.
201
- */
202
- export async function getAgentProfile(
203
- connection: Connection,
204
- wallet: string,
205
- ): Promise<AgentProfile> {
206
- // Fetch stronghold and SAID verification in parallel
207
- const [vault, said] = await Promise.all([
208
- getVaultForWallet(connection, wallet).catch(() => null),
209
- verifySaid(wallet).catch(() => null),
210
- ]);
211
-
212
- // Get factions this agent holds — requires scanning
213
- // For now, check top factions for this holder
214
- const factions = await getAgentFactions(connection, wallet);
215
-
216
- // Find factions this wallet created
217
- const allFactions = await getTokens(connection, { limit: 100 });
218
- const founded = allFactions.tokens.filter(t => isPyreMint(t.mint))
219
- .filter(t => t.mint) // TokenSummary doesn't have creator, so we skip for now
220
- .map(t => t.mint);
221
-
222
- const totalValue = factions.reduce((sum, f) => sum + f.value_sol, 0);
223
-
224
- return {
225
- wallet,
226
- stronghold: vault ? {
227
- address: vault.address,
228
- creator: vault.creator,
229
- authority: vault.authority,
230
- sol_balance: vault.sol_balance,
231
- total_deposited: vault.total_deposited,
232
- total_withdrawn: vault.total_withdrawn,
233
- total_spent: vault.total_spent,
234
- total_received: vault.total_received,
235
- linked_agents: vault.linked_wallets,
236
- created_at: vault.created_at,
237
- } : null,
238
- factions_joined: factions,
239
- factions_founded: [], // Would need per-token creator lookup
240
- said_verification: said,
241
- total_value_sol: totalValue + (vault?.sol_balance ?? 0),
242
- };
243
- }
244
-
245
- /**
246
- * List all factions an agent holds tokens in.
247
- *
248
- * Scans both the wallet's and vault's Token-2022 accounts, merging balances.
249
- * Agents may hold tokens directly (no vault) or via stronghold (vault).
250
- */
251
- export async function getAgentFactions(
252
- connection: Connection,
253
- wallet: string,
254
- factionLimit = 50,
255
- ): Promise<AgentFactionPosition[]> {
256
- const { TOKEN_2022_PROGRAM_ID } = await import('@solana/spl-token');
257
- const walletPk = new PublicKey(wallet);
258
-
259
- // Scan wallet token accounts
260
- const walletAccounts = await connection.getParsedTokenAccountsByOwner(walletPk, {
261
- programId: TOKEN_2022_PROGRAM_ID,
262
- });
263
-
264
- // Scan vault token accounts if a vault exists
265
- let vaultAccounts: typeof walletAccounts = { context: walletAccounts.context, value: [] };
266
- try {
267
- const vault = await getVaultForWallet(connection, wallet);
268
- if (!vault) throw new Error('no vault');
269
- const vaultPk = new PublicKey(vault.address);
270
- vaultAccounts = await connection.getParsedTokenAccountsByOwner(vaultPk, {
271
- programId: TOKEN_2022_PROGRAM_ID,
272
- });
273
- } catch {}
274
-
275
- // Merge balances from both sources (wallet + vault)
276
- const balanceMap = new Map<string, number>();
277
- for (const a of [...walletAccounts.value, ...vaultAccounts.value]) {
278
- const mint = a.account.data.parsed.info.mint as string;
279
- const balance = Number(a.account.data.parsed.info.tokenAmount.uiAmount ?? 0);
280
- if (balance > 0 && isPyreMint(mint)) {
281
- balanceMap.set(mint, (balanceMap.get(mint) ?? 0) + balance);
282
- }
283
- }
284
-
285
- if (balanceMap.size === 0) return [];
286
-
287
- // Fetch faction metadata for held mints
288
- const allFactions = await getTokens(connection, { limit: factionLimit });
289
- const factionMap = new Map(
290
- allFactions.tokens.filter(t => isPyreMint(t.mint)).map(t => [t.mint, t])
291
- );
292
-
293
- const positions: AgentFactionPosition[] = [];
294
- for (const [mint, balance] of balanceMap) {
295
- const faction = factionMap.get(mint);
296
- if (!faction) continue;
297
-
298
- // balance / 1B total supply
299
- const percentage = (balance / 1_000_000_000) * 100;
300
-
301
- positions.push({
302
- mint,
303
- name: faction.name,
304
- symbol: faction.symbol,
305
- balance,
306
- percentage,
307
- value_sol: balance * faction.price_sol,
308
- });
309
- }
310
-
311
- positions.sort((a, b) => b.value_sol - a.value_sol);
312
- return positions;
313
- }
314
-
315
- // ─── World State ───────────────────────────────────────────────────
316
-
317
- /**
318
- * Aggregated recent activity across ALL factions.
319
- *
320
- * The "Bloomberg terminal" feed — launches, joins, defections, rallies.
321
- */
322
- export async function getWorldFeed(
323
- connection: Connection,
324
- opts?: { limit?: number; factionLimit?: number },
325
- ): Promise<WorldEvent[]> {
326
- const factionLimit = opts?.factionLimit ?? 20;
327
- const msgLimit = opts?.limit ?? 5;
328
-
329
- const allFactions = await getTokens(connection, { limit: factionLimit, sort: 'newest' });
330
- const events: WorldEvent[] = [];
331
-
332
- // Add launch events for each faction
333
- for (const faction of allFactions.tokens.filter(t => isPyreMint(t.mint))) {
334
- events.push({
335
- type: 'launch',
336
- faction_mint: faction.mint,
337
- faction_name: faction.name,
338
- timestamp: faction.created_at,
339
- });
340
-
341
- // Map status to events
342
- if (faction.status === 'migrated') {
343
- events.push({
344
- type: 'ascend',
345
- faction_mint: faction.mint,
346
- faction_name: faction.name,
347
- timestamp: faction.last_activity_at,
348
- });
349
- } else if (faction.status === 'reclaimed') {
350
- events.push({
351
- type: 'raze',
352
- faction_mint: faction.mint,
353
- faction_name: faction.name,
354
- timestamp: faction.last_activity_at,
355
- });
356
- }
357
- }
358
-
359
- // Get recent messages from top factions (messages = trade activity)
360
- const topFactions = allFactions.tokens.slice(0, 10);
361
- await Promise.all(
362
- topFactions.map(async (faction) => {
363
- try {
364
- const msgs = await getMessages(connection, faction.mint, msgLimit);
365
- for (const msg of msgs.messages) {
366
- events.push({
367
- type: 'join', // Messages are trade-bundled, most are buys
368
- faction_mint: faction.mint,
369
- faction_name: faction.name,
370
- agent: msg.sender,
371
- timestamp: msg.timestamp,
372
- signature: msg.signature,
373
- message: msg.memo,
374
- });
375
- }
376
- } catch {
377
- // Skip factions with no messages
378
- }
379
- })
380
- );
381
-
382
- events.sort((a, b) => b.timestamp - a.timestamp);
383
- return events.slice(0, opts?.limit ?? 100);
384
- }
385
-
386
- /**
387
- * Global stats: total factions, total agents, total SOL locked.
388
- */
389
- export async function getWorldStats(
390
- connection: Connection,
391
- ): Promise<WorldStats> {
392
- const all = await getTokens(connection, { limit: 200, status: 'all' });
393
- const pyreAll = all.tokens.filter(t => isPyreMint(t.mint));
394
- const pyreRising = pyreAll.filter(t => t.status === 'bonding');
395
- const pyreAscended = pyreAll.filter(t => t.status === 'migrated');
396
- const allFactions = [...pyreRising, ...pyreAscended];
397
- const totalSolLocked = allFactions.reduce((sum, t) => sum + t.market_cap_sol, 0);
398
-
399
- // Find most powerful
400
- let mostPowerful: FactionPower | null = null;
401
- let maxScore = 0;
402
- for (const t of allFactions) {
403
- const score = computePowerScoreFromSummary(t);
404
- if (score > maxScore) {
405
- maxScore = score;
406
- mostPowerful = {
407
- mint: t.mint,
408
- name: t.name,
409
- symbol: t.symbol,
410
- score,
411
- market_cap_sol: t.market_cap_sol,
412
- members: t.holders ?? 0,
413
- war_chest_sol: 0,
414
- rallies: 0,
415
- progress_percent: t.progress_percent,
416
- status: mapFactionStatus(t.status),
417
- };
418
- }
419
- }
420
-
421
- return {
422
- total_factions: pyreAll.length,
423
- rising_factions: pyreRising.length,
424
- ascended_factions: pyreAscended.length,
425
- total_sol_locked: totalSolLocked,
426
- most_powerful: mostPowerful,
427
- };
428
- }
429
-
430
- /** Fetch holders excluding program-owned accounts (bonding curve, treasury, treasury lock) */
431
- async function getPyreHolders(connection: Connection, mint: string, limit: number) {
432
- const mintPk = new PublicKey(mint);
433
- const [bondingCurve] = getBondingCurvePda(mintPk);
434
- const [treasury] = getTokenTreasuryPda(mintPk);
435
- const [treasuryLock] = getTreasuryLockPda(mintPk);
436
- const excluded = new Set([bondingCurve.toString(), treasury.toString(), treasuryLock.toString()]);
437
- const result = await getHolders(connection, mint, limit + 5);
438
- result.holders = result.holders.filter(h => !excluded.has(h.address)).slice(0, limit);
439
- return result;
440
- }
441
-
442
- // ─── Vault P&L ────────────────────────────────────────────────────
443
-
444
- /**
445
- * Get total SOL balance in lamports for an agent: vault + wallet.
446
- * Checks vault first (where most SOL flows), falls back to wallet if no vault.
447
- * Returns the combined balance so P&L captures all SOL movement.
448
- */
449
- export async function getAgentSolLamports(
450
- connection: Connection,
451
- wallet: string,
452
- ): Promise<number> {
453
- const walletPk = new PublicKey(wallet);
454
- let total = 0;
455
- try {
456
- total += await connection.getBalance(walletPk);
457
- } catch {}
458
- try {
459
- const vault = await getVaultForWallet(connection, wallet);
460
- if (vault) total += Math.round(vault.sol_balance * 1e9);
461
- } catch {}
462
- return total;
463
- }
464
-
465
- /**
466
- * Start tracking P&L for a single action/tick.
467
- *
468
- * Snapshots wallet + vault SOL before the action. Call `finish()` after
469
- * to get the diff. Covers both vault and wallet flows so no SOL is missed.
470
- *
471
- * Usage:
472
- * const pnl = await startVaultPnlTracker(connection, wallet)
473
- * // ... do action ...
474
- * const { spent, received } = await pnl.finish()
475
- */
476
- export async function startVaultPnlTracker(
477
- connection: Connection,
478
- wallet: string,
479
- ): Promise<{ finish: () => Promise<{ spent: number; received: number }> }> {
480
- const before = await getAgentSolLamports(connection, wallet);
481
- return {
482
- async finish() {
483
- const after = await getAgentSolLamports(connection, wallet);
484
- const diff = after - before;
485
- return {
486
- spent: diff < 0 ? Math.abs(diff) : 0,
487
- received: diff > 0 ? diff : 0,
488
- };
489
- },
490
- };
491
- }
492
-
493
- // ─── Internal Helpers ──────────────────────────────────────────────
494
-
495
- function computePowerScore(t: TokenDetail): number {
496
- const mcWeight = 0.4;
497
- const memberWeight = 0.2;
498
- const chestWeight = 0.2;
499
- const rallyWeight = 0.1;
500
- const progressWeight = 0.1;
501
-
502
- return (
503
- (t.market_cap_sol * mcWeight) +
504
- ((t.holders ?? 0) * memberWeight) +
505
- (t.treasury_sol_balance * chestWeight) +
506
- (t.stars * rallyWeight) +
507
- (t.progress_percent * progressWeight)
508
- );
509
- }
510
-
511
- function computePowerScoreFromSummary(t: TokenSummary): number {
512
- const mcWeight = 0.4;
513
- const memberWeight = 0.2;
514
- const progressWeight = 0.1;
515
-
516
- return (
517
- (t.market_cap_sol * mcWeight) +
518
- ((t.holders ?? 0) * memberWeight) +
519
- (t.progress_percent * progressWeight)
520
- );
521
- }