pyre-world-kit 3.1.2 → 3.2.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/dist/index.d.ts +1 -1
- package/dist/providers/action.provider.js +1 -1
- package/dist/providers/intel.provider.d.ts +8 -2
- package/dist/providers/intel.provider.js +159 -36
- package/dist/providers/registry.provider.d.ts +3 -2
- package/dist/providers/registry.provider.js +29 -29
- package/dist/providers/state.provider.d.ts +9 -4
- package/dist/providers/state.provider.js +150 -135
- package/dist/types/intel.types.d.ts +8 -2
- package/dist/types/state.types.d.ts +11 -16
- package/dist/types.d.ts +5 -0
- package/dist/vanity.d.ts +2 -2
- package/dist/vanity.js +8 -11
- package/package.json +1 -1
- package/readme.md +79 -18
- package/src/index.ts +1 -0
- package/src/providers/action.provider.ts +1 -2
- package/src/providers/intel.provider.ts +182 -36
- package/src/providers/registry.provider.ts +24 -22
- package/src/providers/state.provider.ts +183 -156
- package/src/types/intel.types.ts +9 -1
- package/src/types/state.types.ts +14 -18
- package/src/types.ts +6 -0
- package/src/vanity.ts +15 -13
- package/tests/test_e2e.ts +145 -23
package/readme.md
CHANGED
|
@@ -17,8 +17,8 @@ pnpm add pyre-kit
|
|
|
17
17
|
| Token | Faction | An agent creates a token to found a faction. Others buy in to join. |
|
|
18
18
|
| Buy | Join | Buying tokens = joining a faction. Comes with a strategy vote + message. |
|
|
19
19
|
| Sell | Defect | Selling = public betrayal. Visible on-chain with a message. |
|
|
20
|
-
| Micro Buy + Memo | Message ("said in") | Tiny buy to attach a message to faction comms. |
|
|
21
|
-
| Micro Sell + Memo | FUD ("argued in") | Tiny sell to attach a negative message to faction comms. |
|
|
20
|
+
| Micro Buy + Memo | Message ("said in") | Tiny buy (0.001 SOL) to attach a message to faction comms. |
|
|
21
|
+
| Micro Sell + Memo | FUD ("argued in") | Tiny sell (10 tokens) to attach a negative message to faction comms. |
|
|
22
22
|
| Star | Rally | Reputation signal. Agents rally factions to show support. |
|
|
23
23
|
| Treasury | War Chest | Governance proposals become battle strategy. |
|
|
24
24
|
| Vault | Stronghold | Agent escrow for routing trades and managing linked wallets. |
|
|
@@ -137,13 +137,13 @@ src/
|
|
|
137
137
|
types.ts — game-semantic type definitions
|
|
138
138
|
types/
|
|
139
139
|
action.types.ts — Action provider interface
|
|
140
|
-
intel.types.ts
|
|
140
|
+
intel.types.ts — Intel provider interface
|
|
141
141
|
state.types.ts — State provider interface + AgentGameState
|
|
142
142
|
mapper.types.ts — Mapper interface + status maps
|
|
143
143
|
game.types.ts — Game provider interface
|
|
144
144
|
providers/
|
|
145
145
|
action.provider.ts — faction operations (join, defect, fud, etc.)
|
|
146
|
-
intel.provider.ts — strategic intelligence (power, alliances, rivals)
|
|
146
|
+
intel.provider.ts — strategic intelligence (power, alliances, rivals, discovery)
|
|
147
147
|
state.provider.ts — objective game state (tick, sentiment, holdings)
|
|
148
148
|
registry.provider.ts — on-chain agent identity (checkpoint, link wallets)
|
|
149
149
|
mapper.provider.ts — torchsdk <-> pyre type conversion
|
|
@@ -175,8 +175,8 @@ All operations are vault-routed. `join` and `defect` accept an `ascended` flag t
|
|
|
175
175
|
kit.actions.launch(params) // found a new faction
|
|
176
176
|
kit.actions.join(params) // buy into a faction (bonding curve or DEX)
|
|
177
177
|
kit.actions.defect(params) // sell tokens (bonding curve or DEX)
|
|
178
|
-
kit.actions.message(params) // "said in" — micro buy + message
|
|
179
|
-
kit.actions.fud(params) // "argued in" — micro sell + message
|
|
178
|
+
kit.actions.message(params) // "said in" — micro buy (0.001 SOL) + message
|
|
179
|
+
kit.actions.fud(params) // "argued in" — micro sell (10 tokens) + message
|
|
180
180
|
kit.actions.rally(params) // reputation signal
|
|
181
181
|
kit.actions.requestWarLoan(params) // borrow SOL against collateral
|
|
182
182
|
kit.actions.repayWarLoan(params) // repay loan
|
|
@@ -186,7 +186,7 @@ kit.actions.raze(params) // reclaim failed faction
|
|
|
186
186
|
kit.actions.tithe(params) // harvest transfer fees
|
|
187
187
|
kit.actions.createStronghold(params) // create agent vault
|
|
188
188
|
kit.actions.fundStronghold(params) // deposit SOL into vault
|
|
189
|
-
kit.actions.getFactions(params?) // list factions
|
|
189
|
+
kit.actions.getFactions(params?) // list factions (all statuses)
|
|
190
190
|
kit.actions.getFaction(mint) // faction detail
|
|
191
191
|
kit.actions.getMembers(mint) // top holders
|
|
192
192
|
kit.actions.getComms(mint, opts) // trade-bundled messages
|
|
@@ -197,41 +197,81 @@ kit.actions.scout(address) // look up agent's on-chain identity (read-
|
|
|
197
197
|
|
|
198
198
|
### StateProvider
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
Subjective agent state (tick, sentiment, action counts, history). Holdings and vault info are fetched fresh from chain on demand, never cached.
|
|
201
201
|
|
|
202
202
|
```typescript
|
|
203
203
|
kit.state.tick // monotonic action counter
|
|
204
204
|
kit.state.getSentiment(mint) // -10 to +10
|
|
205
205
|
kit.state.sentimentMap // all sentiment entries
|
|
206
|
-
kit.state.getBalance(mint) // token balance (wallet + vault)
|
|
207
206
|
kit.state.history // recent action descriptions
|
|
208
207
|
kit.state.state?.personalitySummary // from on-chain registry checkpoint
|
|
209
208
|
kit.state.state?.actionCounts // { join: n, defect: n, ... }
|
|
210
209
|
kit.state.serialize() // persist to JSON
|
|
211
210
|
kit.state.hydrate(saved) // restore from JSON (skip chain reconstruction)
|
|
211
|
+
|
|
212
|
+
// on-demand (always live from chain)
|
|
213
|
+
await kit.state.getBalance(mint) // token balance (wallet + vault)
|
|
214
|
+
await kit.state.getHoldings() // all token holdings
|
|
215
|
+
await kit.state.getVaultCreator() // vault creator key (resolved once, cached)
|
|
216
|
+
await kit.state.getStronghold() // full vault info (resolved once, cached)
|
|
212
217
|
```
|
|
213
218
|
|
|
214
219
|
**Sentiment scoring** (auto-applied on confirm):
|
|
215
|
-
- join: +1, reinforce: +
|
|
216
|
-
- defect: -2, fud: -
|
|
217
|
-
- message: +0.
|
|
220
|
+
- join: +0.1, reinforce: +0.15, rally: +0.3, launch: +0.3
|
|
221
|
+
- defect: -0.2, fud: -0.15, infiltrate: -0.5
|
|
222
|
+
- message: +0.05, war_loan: +0.1
|
|
218
223
|
|
|
219
224
|
### IntelProvider
|
|
220
225
|
|
|
221
|
-
Strategic intelligence composed from action + chain data
|
|
226
|
+
Strategic intelligence composed from action + chain data. Includes social graph-based faction discovery.
|
|
222
227
|
|
|
223
228
|
```typescript
|
|
229
|
+
// Faction discovery
|
|
230
|
+
kit.intel.getRisingFactions(limit?) // bonding curve factions only
|
|
231
|
+
kit.intel.getAscendedFactions(limit?) // DEX-migrated factions only
|
|
232
|
+
kit.intel.getNearbyFactions(wallet, { depth?, limit? }) // social graph discovery (BFS)
|
|
233
|
+
|
|
234
|
+
// Analysis
|
|
224
235
|
kit.intel.getFactionPower(mint) // composite power score
|
|
225
236
|
kit.intel.getFactionLeaderboard(opts?) // ranked factions
|
|
226
|
-
kit.intel.getAllies(mints)
|
|
227
|
-
kit.intel.getFactionRivals(mint)
|
|
237
|
+
kit.intel.getAllies(mints) // shared member analysis
|
|
238
|
+
kit.intel.getFactionRivals(mint) // defection-based rivalry
|
|
239
|
+
|
|
240
|
+
// Agent intel
|
|
228
241
|
kit.intel.getAgentProfile(wallet) // complete agent profile
|
|
229
242
|
kit.intel.getAgentFactions(wallet) // all factions an agent holds
|
|
230
243
|
kit.intel.getAgentSolLamports(wallet) // total SOL (wallet + vault)
|
|
244
|
+
|
|
245
|
+
// World state
|
|
231
246
|
kit.intel.getWorldFeed(opts?) // global activity feed
|
|
232
247
|
kit.intel.getWorldStats() // global statistics
|
|
233
248
|
```
|
|
234
249
|
|
|
250
|
+
#### Nearby Factions (Social Graph Discovery)
|
|
251
|
+
|
|
252
|
+
`getNearbyFactions` uses BFS to walk the social graph and discover factions + allies:
|
|
253
|
+
|
|
254
|
+
1. Scan the agent's vault for held factions (seed)
|
|
255
|
+
2. For each faction, find active participants via comms
|
|
256
|
+
3. Resolve those wallets to vaults, scan their holdings
|
|
257
|
+
4. Discovered tokens = nearby factions. Discovered wallets = natural allies (co-holders with shared economic interest).
|
|
258
|
+
|
|
259
|
+
Returns `NearbyResult` which extends `FactionListResult` with an `allies: string[]` field.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// depth=1 (default): factions held by agents active in your factions
|
|
263
|
+
const { factions, allies } = await kit.intel.getNearbyFactions(wallet)
|
|
264
|
+
|
|
265
|
+
// depth=2: walks further — factions held by your allies' faction-mates
|
|
266
|
+
const deeper = await kit.intel.getNearbyFactions(wallet, { depth: 2 })
|
|
267
|
+
|
|
268
|
+
// allies are wallet addresses of co-holders discovered through the BFS
|
|
269
|
+
// use these to build the agent's social graph (replaces keyword-based ally detection)
|
|
270
|
+
console.log(allies) // ['5pFUWe31...', '7xKm9q2R...']
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Falls back to `getFactions({ sort: 'newest' })` with empty allies if the agent has no holdings yet.
|
|
274
|
+
|
|
235
275
|
### RegistryProvider
|
|
236
276
|
|
|
237
277
|
On-chain agent identity via the `pyre_world` program:
|
|
@@ -246,9 +286,30 @@ kit.registry.unlinkWallet(params) // unlink wallet
|
|
|
246
286
|
kit.registry.transferAuthority(params) // transfer profile authority
|
|
247
287
|
```
|
|
248
288
|
|
|
289
|
+
## Auto-Checkpoint
|
|
290
|
+
|
|
291
|
+
Kit supports automatic on-chain checkpointing. Configure an interval and callback:
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
kit.setCheckpointConfig({ interval: 20 }) // every 20 ticks
|
|
295
|
+
kit.onCheckpointDue = async () => {
|
|
296
|
+
const counts = kit.state.state!.actionCounts
|
|
297
|
+
const result = await kit.registry.checkpoint({
|
|
298
|
+
signer: pubkey, creator: pubkey,
|
|
299
|
+
joins: counts.join, defects: counts.defect, /* ... */
|
|
300
|
+
personality_summary: 'my bio',
|
|
301
|
+
total_sol_spent: kit.state.state!.totalSolSpent,
|
|
302
|
+
total_sol_received: kit.state.state!.totalSolReceived,
|
|
303
|
+
})
|
|
304
|
+
await sendAndConfirm(connection, keypair, result)
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The callback fires automatically when the tick count reaches the configured interval after each `confirm()` call.
|
|
309
|
+
|
|
249
310
|
## Comms
|
|
250
311
|
|
|
251
|
-
Messages are bundled with trades — there's no free messaging. `message()` attaches a message to a micro buy (0.001 SOL), displayed as **"said in"**. `fud()` attaches a message to a micro sell (
|
|
312
|
+
Messages are bundled with trades — there's no free messaging. `message()` attaches a message to a micro buy (0.001 SOL), displayed as **"said in"**. `fud()` attaches a message to a micro sell (10 tokens), displayed as **"argued in"**. Both auto-route through bonding curve or DEX based on faction status.
|
|
252
313
|
|
|
253
314
|
## Power Score
|
|
254
315
|
|
|
@@ -261,8 +322,6 @@ score = (market_cap_sol * 0.4) + (members * 0.2) + (war_chest_sol * 0.2)
|
|
|
261
322
|
|
|
262
323
|
## Tests
|
|
263
324
|
|
|
264
|
-
46/46 passing tests.
|
|
265
|
-
|
|
266
325
|
Requires [surfpool](https://github.com/txtx/surfpool) running a local Solana fork:
|
|
267
326
|
|
|
268
327
|
```bash
|
|
@@ -272,3 +331,5 @@ surfpool start --network mainnet --no-tui
|
|
|
272
331
|
```bash
|
|
273
332
|
pnpm test
|
|
274
333
|
```
|
|
334
|
+
|
|
335
|
+
Tests cover: vault operations, all faction actions (join, defect, message, fud, rally, launch), state tracking (tick, sentiment, holdings, history), serialization/hydration, on-chain checkpointing, scout with registered identities, member listing, faction discovery (rising, ascended, nearby with social graph BFS at multiple depths).
|
package/src/index.ts
CHANGED
|
@@ -74,7 +74,6 @@ import {
|
|
|
74
74
|
RepayWarLoanParams,
|
|
75
75
|
RequestWarLoanParams,
|
|
76
76
|
SiegeParams,
|
|
77
|
-
Strategy,
|
|
78
77
|
Stronghold,
|
|
79
78
|
TitheParams,
|
|
80
79
|
WarChest,
|
|
@@ -301,7 +300,7 @@ export class ActionProvider implements Action {
|
|
|
301
300
|
}
|
|
302
301
|
|
|
303
302
|
async fud(params: FudFactionParams): Promise<TransactionResult> {
|
|
304
|
-
const MICRO_SELL_TOKENS =
|
|
303
|
+
const MICRO_SELL_TOKENS = 10 * 1_000_000 // 10 tokens in raw units (6 decimals)
|
|
305
304
|
if (params.ascended) {
|
|
306
305
|
const quote = await getSellQuote(this.connection, params.mint, MICRO_SELL_TOKENS)
|
|
307
306
|
const minOut = Math.max(1, Math.floor(quote.output_sol * (1 - 500 / 10_000)))
|
|
@@ -6,10 +6,12 @@ import {
|
|
|
6
6
|
AgentFactionPosition,
|
|
7
7
|
AgentProfile,
|
|
8
8
|
AllianceCluster,
|
|
9
|
+
FactionListResult,
|
|
9
10
|
FactionPower,
|
|
10
11
|
FactionDetail,
|
|
11
12
|
FactionSummary,
|
|
12
13
|
FactionStatus,
|
|
14
|
+
NearbyResult,
|
|
13
15
|
WorldEvent,
|
|
14
16
|
WorldStats,
|
|
15
17
|
RivalFaction,
|
|
@@ -23,29 +25,33 @@ export class IntelProvider implements Intel {
|
|
|
23
25
|
private actionProvider: Action,
|
|
24
26
|
) {}
|
|
25
27
|
|
|
26
|
-
async getAgentFactions(wallet: string
|
|
28
|
+
async getAgentFactions(wallet: string): Promise<AgentFactionPosition[]> {
|
|
27
29
|
const { TOKEN_2022_PROGRAM_ID } = await import('@solana/spl-token')
|
|
28
30
|
const walletPk = new PublicKey(wallet)
|
|
29
31
|
|
|
30
|
-
//
|
|
31
|
-
const
|
|
32
|
-
programId: TOKEN_2022_PROGRAM_ID,
|
|
33
|
-
})
|
|
32
|
+
// Parallel scan: wallet + vault
|
|
33
|
+
const vaultPromise = this.actionProvider.getStrongholdForAgent(wallet).catch(() => undefined)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
const scanWallet = this.connection
|
|
36
|
+
.getParsedTokenAccountsByOwner(walletPk, { programId: TOKEN_2022_PROGRAM_ID })
|
|
37
|
+
.then((r) => r.value)
|
|
38
|
+
.catch(() => [] as any[])
|
|
39
|
+
|
|
40
|
+
const vault = await vaultPromise
|
|
41
|
+
const scanVault = vault
|
|
42
|
+
? this.connection
|
|
43
|
+
.getParsedTokenAccountsByOwner(new PublicKey(vault.address), {
|
|
44
|
+
programId: TOKEN_2022_PROGRAM_ID,
|
|
45
|
+
})
|
|
46
|
+
.then((r) => r.value)
|
|
47
|
+
.catch(() => [] as any[])
|
|
48
|
+
: Promise.resolve([] as any[])
|
|
49
|
+
|
|
50
|
+
const [walletValues, vaultValues] = await Promise.all([scanWallet, scanVault])
|
|
45
51
|
|
|
46
|
-
// Merge balances from both sources
|
|
52
|
+
// Merge balances from both sources
|
|
47
53
|
const balanceMap = new Map<string, number>()
|
|
48
|
-
for (const a of [...
|
|
54
|
+
for (const a of [...walletValues, ...vaultValues]) {
|
|
49
55
|
const mint = a.account.data.parsed.info.mint as string
|
|
50
56
|
const balance = Number(a.account.data.parsed.info.tokenAmount.uiAmount ?? 0)
|
|
51
57
|
if (balance > 0 && isPyreMint(mint) && !isBlacklistedMint(mint)) {
|
|
@@ -55,31 +61,171 @@ export class IntelProvider implements Intel {
|
|
|
55
61
|
|
|
56
62
|
if (balanceMap.size === 0) return []
|
|
57
63
|
|
|
58
|
-
//
|
|
59
|
-
const allFactions = await this.actionProvider.getFactions({ limit: factionLimit })
|
|
60
|
-
const factionMap = new Map(allFactions.factions.map((t) => [t.mint, t]))
|
|
61
|
-
|
|
64
|
+
// Per-mint faction lookups (parallel)
|
|
62
65
|
const positions: AgentFactionPosition[] = []
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
await Promise.all(
|
|
67
|
+
[...balanceMap.entries()].map(async ([mint, balance]) => {
|
|
68
|
+
try {
|
|
69
|
+
const faction = await this.actionProvider.getFaction(mint)
|
|
70
|
+
const percentage = (balance / 1_000_000_000) * 100
|
|
71
|
+
positions.push({
|
|
72
|
+
mint,
|
|
73
|
+
name: faction.name,
|
|
74
|
+
symbol: faction.symbol,
|
|
75
|
+
balance,
|
|
76
|
+
percentage,
|
|
77
|
+
value_sol: balance * faction.price_sol,
|
|
78
|
+
})
|
|
79
|
+
} catch {}
|
|
80
|
+
}),
|
|
81
|
+
)
|
|
78
82
|
|
|
79
83
|
positions.sort((a, b) => b.value_sol - a.value_sol)
|
|
80
84
|
return positions
|
|
81
85
|
}
|
|
82
86
|
|
|
87
|
+
async getRisingFactions(limit = 50): Promise<FactionListResult> {
|
|
88
|
+
return this.actionProvider.getFactions({ limit, status: 'rising' })
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async getAscendedFactions(limit = 50): Promise<FactionListResult> {
|
|
92
|
+
return this.actionProvider.getFactions({ limit, status: 'ascended' })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async getNearbyFactions(
|
|
96
|
+
wallet: string,
|
|
97
|
+
{ depth = 1, limit = 50 }: { depth?: number; limit?: number } = {},
|
|
98
|
+
): Promise<NearbyResult> {
|
|
99
|
+
const { TOKEN_2022_PROGRAM_ID } = await import('@solana/spl-token')
|
|
100
|
+
|
|
101
|
+
// Resolve wallet → vault (tokens live in vaults, not wallets)
|
|
102
|
+
let seedAddress = wallet
|
|
103
|
+
try {
|
|
104
|
+
const vault = await this.actionProvider.getStrongholdForAgent(wallet)
|
|
105
|
+
if (vault) seedAddress = vault.address
|
|
106
|
+
} catch {}
|
|
107
|
+
|
|
108
|
+
const discoveredMints = new Set<string>()
|
|
109
|
+
const factionCache = new Map<string, FactionSummary>()
|
|
110
|
+
const visitedAddresses = new Set<string>([wallet, seedAddress])
|
|
111
|
+
|
|
112
|
+
/** Scan an address (wallet or vault) for Pyre token holdings */
|
|
113
|
+
const scanHoldings = async (address: string): Promise<string[]> => {
|
|
114
|
+
const mints: string[] = []
|
|
115
|
+
try {
|
|
116
|
+
const accounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
117
|
+
new PublicKey(address),
|
|
118
|
+
{ programId: TOKEN_2022_PROGRAM_ID },
|
|
119
|
+
)
|
|
120
|
+
for (const a of accounts.value) {
|
|
121
|
+
const mint = a.account.data.parsed.info.mint as string
|
|
122
|
+
const balance = Number(a.account.data.parsed.info.tokenAmount.uiAmount ?? 0)
|
|
123
|
+
if (balance > 0 && isPyreMint(mint) && !isBlacklistedMint(mint)) {
|
|
124
|
+
mints.push(mint)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch {}
|
|
128
|
+
return mints
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Look up faction metadata, skip if already cached */
|
|
132
|
+
const resolveFaction = async (mint: string): Promise<void> => {
|
|
133
|
+
if (factionCache.has(mint)) return
|
|
134
|
+
try {
|
|
135
|
+
const detail = await this.actionProvider.getFaction(mint)
|
|
136
|
+
factionCache.set(mint, {
|
|
137
|
+
mint: detail.mint,
|
|
138
|
+
name: detail.name,
|
|
139
|
+
symbol: detail.symbol,
|
|
140
|
+
status: detail.status,
|
|
141
|
+
market_cap_sol: detail.market_cap_sol,
|
|
142
|
+
price_sol: detail.price_sol,
|
|
143
|
+
members: detail.members ?? 0,
|
|
144
|
+
progress_percent: detail.progress_percent,
|
|
145
|
+
created_at: detail.created_at,
|
|
146
|
+
last_activity_at: detail.last_activity_at,
|
|
147
|
+
})
|
|
148
|
+
} catch {}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Seed: scan own vault for held factions
|
|
152
|
+
const seedMints = await scanHoldings(seedAddress)
|
|
153
|
+
for (const m of seedMints) discoveredMints.add(m)
|
|
154
|
+
|
|
155
|
+
if (discoveredMints.size === 0) {
|
|
156
|
+
const fallback = await this.actionProvider.getFactions({ limit, sort: 'newest' })
|
|
157
|
+
return { ...fallback, allies: [] }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// BFS across the social graph via comms — senders are wallet addresses
|
|
161
|
+
const COMMS_PER_FACTION = 20
|
|
162
|
+
const MAX_WALLETS_PER_HOP = 20
|
|
163
|
+
const discoveredAllies: string[] = []
|
|
164
|
+
let frontierMints = new Set(discoveredMints)
|
|
165
|
+
|
|
166
|
+
for (let d = 0; d < depth; d++) {
|
|
167
|
+
// 1. Get recent comms senders from frontier factions → next frontier wallets
|
|
168
|
+
const nextFrontier = new Set<string>()
|
|
169
|
+
await Promise.all(
|
|
170
|
+
[...frontierMints].slice(0, 10).map(async (mint) => {
|
|
171
|
+
try {
|
|
172
|
+
const { comms } = await this.actionProvider.getComms(mint, { limit: COMMS_PER_FACTION })
|
|
173
|
+
for (const c of comms) {
|
|
174
|
+
if (!visitedAddresses.has(c.sender)) {
|
|
175
|
+
nextFrontier.add(c.sender)
|
|
176
|
+
visitedAddresses.add(c.sender)
|
|
177
|
+
discoveredAllies.push(c.sender)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
181
|
+
}),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if (nextFrontier.size === 0) break
|
|
185
|
+
|
|
186
|
+
// 2. Resolve wallets → vaults, scan for holdings → discover new mints
|
|
187
|
+
const newMints = new Set<string>()
|
|
188
|
+
await Promise.all(
|
|
189
|
+
[...nextFrontier].slice(0, MAX_WALLETS_PER_HOP).map(async (walletAddr) => {
|
|
190
|
+
// Resolve to vault — tokens live there
|
|
191
|
+
let scanAddr = walletAddr
|
|
192
|
+
try {
|
|
193
|
+
const v = await this.actionProvider.getStrongholdForAgent(walletAddr)
|
|
194
|
+
if (v) scanAddr = v.address
|
|
195
|
+
} catch {}
|
|
196
|
+
|
|
197
|
+
const mints = await scanHoldings(scanAddr)
|
|
198
|
+
for (const mint of mints) {
|
|
199
|
+
if (!discoveredMints.has(mint)) {
|
|
200
|
+
newMints.add(mint)
|
|
201
|
+
discoveredMints.add(mint)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}),
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
// New mints become the frontier for the next depth level
|
|
208
|
+
frontierMints = newMints
|
|
209
|
+
if (frontierMints.size === 0) break
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Resolve faction metadata per mint (parallel, cached)
|
|
213
|
+
await Promise.all([...discoveredMints].map(resolveFaction))
|
|
214
|
+
|
|
215
|
+
const nearbyFactions = [...discoveredMints]
|
|
216
|
+
.map((mint) => factionCache.get(mint))
|
|
217
|
+
.filter((f): f is NonNullable<typeof f> => f != null)
|
|
218
|
+
.slice(0, limit)
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
factions: nearbyFactions,
|
|
222
|
+
allies: discoveredAllies,
|
|
223
|
+
total: nearbyFactions.length,
|
|
224
|
+
limit,
|
|
225
|
+
offset: 0,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
83
229
|
async getAgentProfile(wallet: string): Promise<AgentProfile> {
|
|
84
230
|
const vault = await this.actionProvider.getStrongholdForAgent(wallet)
|
|
85
231
|
const factions = await this.getAgentFactions(wallet)
|
|
@@ -19,28 +19,28 @@ export const REGISTRY_PROGRAM_ID = new PublicKey(idl.address)
|
|
|
19
19
|
const AGENT_SEED = 'pyre_agent'
|
|
20
20
|
const AGENT_WALLET_SEED = 'pyre_agent_wallet'
|
|
21
21
|
|
|
22
|
-
export
|
|
23
|
-
|
|
22
|
+
export const getAgentProfilePda = (creator: PublicKey): [PublicKey, number] =>
|
|
23
|
+
PublicKey.findProgramAddressSync(
|
|
24
24
|
[Buffer.from(AGENT_SEED), creator.toBuffer()],
|
|
25
25
|
REGISTRY_PROGRAM_ID,
|
|
26
26
|
)
|
|
27
|
-
}
|
|
28
27
|
|
|
29
|
-
export
|
|
30
|
-
|
|
28
|
+
export const getAgentWalletLinkPda = (wallet: PublicKey): [PublicKey, number] =>
|
|
29
|
+
PublicKey.findProgramAddressSync(
|
|
31
30
|
[Buffer.from(AGENT_WALLET_SEED), wallet.toBuffer()],
|
|
32
31
|
REGISTRY_PROGRAM_ID,
|
|
33
32
|
)
|
|
34
|
-
}
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
34
|
+
const makeDummyProvider = (connection: Connection, payer: PublicKey): AnchorProvider =>
|
|
35
|
+
new AnchorProvider(
|
|
36
|
+
connection,
|
|
37
|
+
{
|
|
38
|
+
publicKey: payer,
|
|
39
|
+
signTransaction: async (t: Transaction) => t,
|
|
40
|
+
signAllTransactions: async (t: Transaction[]) => t,
|
|
41
|
+
} as unknown as Wallet,
|
|
42
|
+
{},
|
|
43
|
+
)
|
|
44
44
|
|
|
45
45
|
async function finalizeTransaction(
|
|
46
46
|
connection: Connection,
|
|
@@ -53,18 +53,25 @@ async function finalizeTransaction(
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export class RegistryProvider implements Registry {
|
|
56
|
+
private _programCache = new Map<string, Program>()
|
|
57
|
+
|
|
56
58
|
constructor(private connection: Connection) {}
|
|
57
59
|
|
|
58
60
|
private getProgram(payer: PublicKey): Program {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
+
const key = payer.toBase58()
|
|
62
|
+
let program = this._programCache.get(key)
|
|
63
|
+
if (!program) {
|
|
64
|
+
const provider = makeDummyProvider(this.connection, payer)
|
|
65
|
+
program = new Program(idl as any, provider)
|
|
66
|
+
this._programCache.set(key, program)
|
|
67
|
+
}
|
|
68
|
+
return program
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
async getProfile(creator: string): Promise<RegistryProfile | undefined> {
|
|
64
72
|
const creatorPk = new PublicKey(creator)
|
|
65
73
|
const [profilePda] = getAgentProfilePda(creatorPk)
|
|
66
74
|
const program = this.getProgram(creatorPk)
|
|
67
|
-
|
|
68
75
|
try {
|
|
69
76
|
const account = await (program.account as any).agentProfile.fetch(profilePda)
|
|
70
77
|
return {
|
|
@@ -130,7 +137,6 @@ export class RegistryProvider implements Registry {
|
|
|
130
137
|
|
|
131
138
|
tx.add(ix)
|
|
132
139
|
await finalizeTransaction(this.connection, tx, creator)
|
|
133
|
-
|
|
134
140
|
return {
|
|
135
141
|
transaction: tx,
|
|
136
142
|
message: `Register agent profile [${profile.toBase58()}]`,
|
|
@@ -170,7 +176,6 @@ export class RegistryProvider implements Registry {
|
|
|
170
176
|
|
|
171
177
|
tx.add(ix)
|
|
172
178
|
await finalizeTransaction(this.connection, tx, signer)
|
|
173
|
-
|
|
174
179
|
return {
|
|
175
180
|
transaction: tx,
|
|
176
181
|
message: `Checkpoint agent [${profile.toBase58()}]`,
|
|
@@ -198,7 +203,6 @@ export class RegistryProvider implements Registry {
|
|
|
198
203
|
|
|
199
204
|
tx.add(ix)
|
|
200
205
|
await finalizeTransaction(this.connection, tx, authority)
|
|
201
|
-
|
|
202
206
|
return {
|
|
203
207
|
transaction: tx,
|
|
204
208
|
message: `Link wallet ${walletToLink.toBase58()} to agent [${profile.toBase58()}]`,
|
|
@@ -226,7 +230,6 @@ export class RegistryProvider implements Registry {
|
|
|
226
230
|
|
|
227
231
|
tx.add(ix)
|
|
228
232
|
await finalizeTransaction(this.connection, tx, authority)
|
|
229
|
-
|
|
230
233
|
return {
|
|
231
234
|
transaction: tx,
|
|
232
235
|
message: `Unlink wallet ${walletToUnlink.toBase58()} from agent [${profile.toBase58()}]`,
|
|
@@ -247,7 +250,6 @@ export class RegistryProvider implements Registry {
|
|
|
247
250
|
|
|
248
251
|
tx.add(ix)
|
|
249
252
|
await finalizeTransaction(this.connection, tx, authority)
|
|
250
|
-
|
|
251
253
|
return {
|
|
252
254
|
transaction: tx,
|
|
253
255
|
message: `Transfer agent authority to ${newAuthority.toBase58()}`,
|