pyre-world-kit 2.0.11 → 3.0.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.
Files changed (54) hide show
  1. package/.prettierrc.json +6 -0
  2. package/dist/actions.js +16 -0
  3. package/dist/index.d.ts +38 -4
  4. package/dist/index.js +100 -85
  5. package/dist/providers/action.provider.d.ts +46 -0
  6. package/dist/providers/action.provider.js +331 -0
  7. package/dist/providers/intel.provider.d.ts +29 -0
  8. package/dist/providers/intel.provider.js +363 -0
  9. package/dist/providers/mapper.provider.d.ts +197 -0
  10. package/dist/providers/mapper.provider.js +158 -0
  11. package/dist/providers/registry.provider.d.ts +25 -0
  12. package/dist/providers/registry.provider.js +229 -0
  13. package/dist/providers/state.provider.d.ts +42 -0
  14. package/dist/providers/state.provider.js +348 -0
  15. package/dist/pyre_world.json +34 -229
  16. package/dist/types/action.types.d.ts +41 -0
  17. package/dist/types/action.types.js +2 -0
  18. package/dist/types/intel.types.d.ts +20 -0
  19. package/dist/types/intel.types.js +2 -0
  20. package/dist/types/mapper.types.d.ts +27 -0
  21. package/dist/types/mapper.types.js +22 -0
  22. package/dist/types/registry.types.d.ts +0 -0
  23. package/dist/types/registry.types.js +1 -0
  24. package/dist/types/state.types.d.ts +112 -0
  25. package/dist/types/state.types.js +2 -0
  26. package/dist/types.d.ts +8 -24
  27. package/dist/util.d.ts +29 -0
  28. package/dist/util.js +144 -0
  29. package/dist/vanity.d.ts +3 -3
  30. package/dist/vanity.js +18 -15
  31. package/package.json +4 -2
  32. package/readme.md +134 -122
  33. package/src/index.ts +127 -92
  34. package/src/providers/action.provider.ts +443 -0
  35. package/src/providers/intel.provider.ts +383 -0
  36. package/src/providers/mapper.provider.ts +195 -0
  37. package/src/providers/registry.provider.ts +277 -0
  38. package/src/providers/state.provider.ts +357 -0
  39. package/src/pyre_world.json +35 -230
  40. package/src/types/action.types.ts +76 -0
  41. package/src/types/intel.types.ts +22 -0
  42. package/src/types/mapper.types.ts +84 -0
  43. package/src/types/registry.types.ts +0 -0
  44. package/src/types/state.types.ts +144 -0
  45. package/src/types.ts +329 -333
  46. package/src/util.ts +148 -0
  47. package/src/vanity.ts +27 -14
  48. package/tests/test_e2e.ts +339 -172
  49. package/src/actions.ts +0 -703
  50. package/src/intel.ts +0 -521
  51. package/src/mappers.ts +0 -302
  52. package/src/registry.ts +0 -317
  53. package/tests/test_devnet_e2e.ts +0 -401
  54. package/tests/test_sim.ts +0 -458
@@ -0,0 +1,383 @@
1
+ import { Connection, PublicKey } from '@solana/web3.js'
2
+ import { MapperProvider } from './mapper.provider'
3
+ import {
4
+ AgentFactionPosition,
5
+ AgentProfile,
6
+ AllianceCluster,
7
+ FactionPower,
8
+ FactionDetail,
9
+ FactionSummary,
10
+ FactionStatus,
11
+ WorldEvent,
12
+ WorldStats,
13
+ RivalFaction,
14
+ } from '../types'
15
+ import { Intel } from '../types/intel.types'
16
+ import { getBondingCurvePda, getTokenTreasuryPda, getTreasuryLockPda, isPyreMint } from '../vanity'
17
+ import { isBlacklistedMint } from '../util'
18
+ import { Action } from '../types/action.types'
19
+
20
+ export class IntelProvider implements Intel {
21
+ constructor(
22
+ private connection: Connection,
23
+ private actionProvider: Action,
24
+ ) {}
25
+
26
+ async getAgentFactions(wallet: string, factionLimit = 50): Promise<AgentFactionPosition[]> {
27
+ const { TOKEN_2022_PROGRAM_ID } = await import('@solana/spl-token')
28
+ const walletPk = new PublicKey(wallet)
29
+
30
+ // Scan wallet token accounts
31
+ const walletAccounts = await this.connection.getParsedTokenAccountsByOwner(walletPk, {
32
+ programId: TOKEN_2022_PROGRAM_ID,
33
+ })
34
+
35
+ // Scan vault token accounts if a vault exists
36
+ let vaultAccounts: typeof walletAccounts = { context: walletAccounts.context, value: [] }
37
+ try {
38
+ const vault = await this.actionProvider.getStrongholdForAgent(wallet)
39
+ if (!vault) throw new Error('no vault')
40
+ const vaultPk = new PublicKey(vault.address)
41
+ vaultAccounts = await this.connection.getParsedTokenAccountsByOwner(vaultPk, {
42
+ programId: TOKEN_2022_PROGRAM_ID,
43
+ })
44
+ } catch {}
45
+
46
+ // Merge balances from both sources (wallet + vault)
47
+ const balanceMap = new Map<string, number>()
48
+ for (const a of [...walletAccounts.value, ...vaultAccounts.value]) {
49
+ const mint = a.account.data.parsed.info.mint as string
50
+ const balance = Number(a.account.data.parsed.info.tokenAmount.uiAmount ?? 0)
51
+ if (balance > 0 && isPyreMint(mint) && !isBlacklistedMint(mint)) {
52
+ balanceMap.set(mint, (balanceMap.get(mint) ?? 0) + balance)
53
+ }
54
+ }
55
+
56
+ if (balanceMap.size === 0) return []
57
+
58
+ // Fetch faction metadata for held mints
59
+ const allFactions = await this.actionProvider.getFactions({ limit: factionLimit })
60
+ const factionMap = new Map(allFactions.factions.map((t) => [t.mint, t]))
61
+
62
+ const positions: AgentFactionPosition[] = []
63
+ for (const [mint, balance] of balanceMap) {
64
+ const faction = factionMap.get(mint)
65
+ if (!faction) continue
66
+
67
+ // balance / 1B total supply
68
+ const percentage = (balance / 1_000_000_000) * 100
69
+ positions.push({
70
+ mint,
71
+ name: faction.name,
72
+ symbol: faction.symbol,
73
+ balance,
74
+ percentage,
75
+ value_sol: balance * faction.price_sol,
76
+ })
77
+ }
78
+
79
+ positions.sort((a, b) => b.value_sol - a.value_sol)
80
+ return positions
81
+ }
82
+
83
+ async getAgentProfile(wallet: string): Promise<AgentProfile> {
84
+ const vault = await this.actionProvider.getStrongholdForAgent(wallet)
85
+ const factions = await this.getAgentFactions(wallet)
86
+ const totalValue = factions.reduce((sum, f) => sum + f.value_sol, 0)
87
+ return {
88
+ wallet,
89
+ stronghold: vault
90
+ ? {
91
+ address: vault.address,
92
+ creator: vault.creator,
93
+ authority: vault.authority,
94
+ sol_balance: vault.sol_balance,
95
+ total_deposited: vault.total_deposited,
96
+ total_withdrawn: vault.total_withdrawn,
97
+ total_spent: vault.total_spent,
98
+ total_received: vault.total_received,
99
+ linked_agents: vault.linked_agents,
100
+ created_at: vault.created_at,
101
+ }
102
+ : null,
103
+ factions_joined: factions,
104
+ factions_founded: [], // Would need per-token creator lookup
105
+ total_value_sol: totalValue + (vault?.sol_balance ?? 0),
106
+ }
107
+ }
108
+
109
+ async getAgentSolLamports(wallet: string): Promise<number> {
110
+ const walletPk = new PublicKey(wallet)
111
+ let total = 0
112
+ try {
113
+ total += await this.connection.getBalance(walletPk)
114
+ } catch {}
115
+ try {
116
+ const vault = await this.actionProvider.getStrongholdForAgent(wallet)
117
+ if (vault) total += Math.round(vault.sol_balance * 1e9)
118
+ } catch {}
119
+ return total
120
+ }
121
+
122
+ async getAllies(mints: string[], holderLimit = 50): Promise<AllianceCluster[]> {
123
+ const holdersPerFaction = await Promise.all(
124
+ mints.map(async (mint) => {
125
+ const result = await this.getPyreHolders(mint, holderLimit)
126
+ return { mint, holders: new Set(result.members.map((h) => h.address)) }
127
+ }),
128
+ )
129
+
130
+ // Find overlapping holders between faction pairs
131
+ const clusters: AllianceCluster[] = []
132
+ for (let i = 0; i < holdersPerFaction.length; i++) {
133
+ for (let j = i + 1; j < holdersPerFaction.length; j++) {
134
+ const a = holdersPerFaction[i]
135
+ const b = holdersPerFaction[j]
136
+ const shared = [...a.holders].filter((h) => b.holders.has(h))
137
+ if (shared.length > 0) {
138
+ const minSize = Math.min(a.holders.size, b.holders.size)
139
+ clusters.push({
140
+ factions: [a.mint, b.mint],
141
+ shared_members: shared.length,
142
+ overlap_percent: minSize > 0 ? (shared.length / minSize) * 100 : 0,
143
+ })
144
+ }
145
+ }
146
+ }
147
+
148
+ clusters.sort((a, b) => b.shared_members - a.shared_members)
149
+ return clusters
150
+ }
151
+
152
+ async getFactionPower(mint: string): Promise<FactionPower> {
153
+ const t = await this.actionProvider.getFaction(mint)
154
+ const score = this.computePowerScore(t)
155
+ return {
156
+ mint: t.mint,
157
+ name: t.name,
158
+ symbol: t.symbol,
159
+ score,
160
+ market_cap_sol: t.market_cap_sol,
161
+ members: t.members ?? 0,
162
+ war_chest_sol: t.war_chest_sol,
163
+ rallies: t.rallies,
164
+ progress_percent: t.progress_percent,
165
+ status: t.status,
166
+ }
167
+ }
168
+
169
+ async getFactionLeaderboard({
170
+ status,
171
+ limit,
172
+ }: {
173
+ status?: FactionStatus
174
+ limit?: number
175
+ }): Promise<FactionPower[]> {
176
+ const statusMap: Record<FactionStatus, string> = {
177
+ rising: 'bonding',
178
+ ready: 'complete',
179
+ ascended: 'migrated',
180
+ razed: 'reclaimed',
181
+ }
182
+
183
+ const fetchLimit = Math.min((limit ?? 20) * 3, 100)
184
+ const { factions } = await this.actionProvider.getFactions({
185
+ limit: fetchLimit,
186
+ status: status,
187
+ })
188
+ const powers: FactionPower[] = factions.map((t) => ({
189
+ mint: t.mint,
190
+ name: t.name,
191
+ symbol: t.symbol,
192
+ score: this.computePowerScoreFromSummary(t),
193
+ market_cap_sol: t.market_cap_sol,
194
+ members: t.members ?? 0,
195
+ war_chest_sol: 0, // Not available in summary
196
+ rallies: 0, // Not available in summary
197
+ progress_percent: t.progress_percent,
198
+ status: t.status,
199
+ }))
200
+
201
+ powers.sort((a, b) => b.score - a.score)
202
+ return powers
203
+ }
204
+
205
+ async getFactionRivals(
206
+ mint: string,
207
+ { limit = 50 }: { limit?: number },
208
+ ): Promise<RivalFaction[]> {
209
+ const { comms } = await this.actionProvider.getComms(mint, { limit })
210
+ const defectors = new Set(comms.map((m) => m.sender))
211
+ const rivalCounts = new Map<string, { in: number; out: number }>()
212
+ const { factions } = await this.actionProvider.getFactions({ limit: 20, sort: 'volume' })
213
+ for (const faction of factions) {
214
+ if (faction.mint === mint) continue
215
+ const { members } = await this.getPyreHolders(faction.mint, 50)
216
+ const holderAddrs = new Set(members.map((h) => h.address))
217
+ const overlap = [...defectors].filter((d) => holderAddrs.has(d)).length
218
+ if (overlap > 0) {
219
+ rivalCounts.set(faction.mint, {
220
+ in: overlap,
221
+ out: overlap,
222
+ ...(rivalCounts.get(faction.mint) ?? {}),
223
+ })
224
+ }
225
+ }
226
+
227
+ const rivals: RivalFaction[] = []
228
+ for (const [rivalMint, counts] of rivalCounts) {
229
+ const faction = factions.find((t) => t.mint === rivalMint)
230
+ if (faction) {
231
+ rivals.push({
232
+ mint: rivalMint,
233
+ name: faction.name,
234
+ symbol: faction.symbol,
235
+ defections_in: counts.in,
236
+ defections_out: counts.out,
237
+ })
238
+ }
239
+ }
240
+
241
+ rivals.sort((a, b) => b.defections_in + b.defections_out - (a.defections_in + a.defections_out))
242
+ return rivals
243
+ }
244
+
245
+ async getWorldFeed({
246
+ limit,
247
+ factionLimit,
248
+ }: {
249
+ limit?: number
250
+ factionLimit?: number
251
+ }): Promise<WorldEvent[]> {
252
+ const fLimit = factionLimit ?? 20
253
+ const msgLimit = limit ?? 5
254
+ const allFactions = await this.actionProvider.getFactions({ limit: fLimit, sort: 'newest' })
255
+ const events: WorldEvent[] = []
256
+ for (const faction of allFactions.factions) {
257
+ events.push({
258
+ type: 'launch',
259
+ faction_mint: faction.mint,
260
+ faction_name: faction.name,
261
+ timestamp: faction.created_at,
262
+ })
263
+
264
+ if (faction.status === 'ascended') {
265
+ events.push({
266
+ type: 'ascend',
267
+ faction_mint: faction.mint,
268
+ faction_name: faction.name,
269
+ timestamp: faction.last_activity_at,
270
+ })
271
+ } else if (faction.status === 'razed') {
272
+ events.push({
273
+ type: 'raze',
274
+ faction_mint: faction.mint,
275
+ faction_name: faction.name,
276
+ timestamp: faction.last_activity_at,
277
+ })
278
+ }
279
+ }
280
+
281
+ const topFactions = allFactions.factions.slice(0, 10)
282
+ await Promise.all(
283
+ topFactions.map(async (faction) => {
284
+ try {
285
+ const { comms } = await this.actionProvider.getComms(faction.mint, { limit: msgLimit })
286
+ for (const msg of comms) {
287
+ events.push({
288
+ type: 'join', // Messages are trade-bundled, most are buys
289
+ faction_mint: faction.mint,
290
+ faction_name: faction.name,
291
+ agent: msg.sender,
292
+ timestamp: msg.timestamp,
293
+ signature: msg.signature,
294
+ message: msg.memo,
295
+ })
296
+ }
297
+ } catch {
298
+ // Skip factions with no messages
299
+ }
300
+ }),
301
+ )
302
+ events.sort((a, b) => b.timestamp - a.timestamp)
303
+ return events.slice(0, limit ?? 100)
304
+ }
305
+
306
+ async getWorldStats(): Promise<WorldStats> {
307
+ const { factions } = await this.actionProvider.getFactions({ limit: 200, status: 'all' })
308
+ const pyreRising = factions.filter((t) => t.status === 'rising')
309
+ const pyreAscended = factions.filter((t) => t.status === 'ascended')
310
+ const allFactions = [...pyreRising, ...pyreAscended]
311
+ const totalSolLocked = allFactions.reduce((sum, t) => sum + t.market_cap_sol, 0)
312
+
313
+ let mostPowerful: FactionPower | null = null
314
+ let maxScore = 0
315
+ for (const t of allFactions) {
316
+ const score = this.computePowerScoreFromSummary(t)
317
+ if (score > maxScore) {
318
+ maxScore = score
319
+ mostPowerful = {
320
+ mint: t.mint,
321
+ name: t.name,
322
+ symbol: t.symbol,
323
+ score,
324
+ market_cap_sol: t.market_cap_sol,
325
+ members: t.members ?? 0,
326
+ war_chest_sol: 0,
327
+ rallies: 0,
328
+ progress_percent: t.progress_percent,
329
+ status: t.status,
330
+ }
331
+ }
332
+ }
333
+
334
+ return {
335
+ total_factions: factions.length,
336
+ rising_factions: pyreRising.length,
337
+ ascended_factions: pyreAscended.length,
338
+ total_sol_locked: totalSolLocked,
339
+ most_powerful: mostPowerful,
340
+ }
341
+ }
342
+
343
+ private computePowerScore(t: FactionDetail) {
344
+ const mcWeight = 0.4
345
+ const memberWeight = 0.2
346
+ const chestWeight = 0.2
347
+ const rallyWeight = 0.1
348
+ const progressWeight = 0.1
349
+ return (
350
+ t.market_cap_sol * mcWeight +
351
+ (t.members ?? 0) * memberWeight +
352
+ t.war_chest_sol * chestWeight +
353
+ t.rallies * rallyWeight +
354
+ t.progress_percent * progressWeight
355
+ )
356
+ }
357
+
358
+ private computePowerScoreFromSummary(t: FactionSummary) {
359
+ const mcWeight = 0.4
360
+ const memberWeight = 0.2
361
+ const progressWeight = 0.1
362
+ return (
363
+ t.market_cap_sol * mcWeight +
364
+ (t.members ?? 0) * memberWeight +
365
+ t.progress_percent * progressWeight
366
+ )
367
+ }
368
+
369
+ private async getPyreHolders(mint: string, limit: number) {
370
+ const mintPk = new PublicKey(mint)
371
+ const [bondingCurve] = getBondingCurvePda(mintPk)
372
+ const [treasury] = getTokenTreasuryPda(mintPk)
373
+ const [treasuryLock] = getTreasuryLockPda(mintPk)
374
+ const excluded = new Set([
375
+ bondingCurve.toString(),
376
+ treasury.toString(),
377
+ treasuryLock.toString(),
378
+ ])
379
+ const result = await this.actionProvider.getMembers(mint, limit + 5)
380
+ result.members = result.members.filter((h) => !excluded.has(h.address)).slice(0, limit)
381
+ return result
382
+ }
383
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Pyre Kit Mappers
3
+ *
4
+ * Internal mapping functions between Torch SDK types and Pyre game types.
5
+ */
6
+
7
+ import type {
8
+ AllLoanPositionsResult,
9
+ BuyTransactionResult,
10
+ CreateTokenResult,
11
+ Holder,
12
+ HoldersResult,
13
+ LendingInfo,
14
+ LoanPositionInfo,
15
+ LoanPositionWithKey,
16
+ MessagesResult,
17
+ TokenDetail,
18
+ TokenListResult,
19
+ TokenMessage,
20
+ TokenStatus,
21
+ TokenSummary,
22
+ VaultInfo,
23
+ VaultWalletLinkInfo,
24
+ } from 'torchsdk'
25
+
26
+ import type { FactionStatus, FactionStatusFilter, Strategy } from '../types'
27
+ import { Mapper, STATUS_FILTER_REVERSE, STATUS_MAP, STATUS_REVERSE } from '../types/mapper.types'
28
+
29
+ export class MapperProvider implements Mapper {
30
+ allLoansResult = (r: AllLoanPositionsResult) => ({
31
+ positions: r.positions.map(this.loanWithKeyToWarLoan),
32
+ pool_price_sol: r.pool_price_sol,
33
+ })
34
+
35
+ buyResult = (r: BuyTransactionResult) => ({
36
+ transaction: r.transaction,
37
+ additionalTransactions: r.additionalTransactions,
38
+ message: r.message,
39
+ migrationTransaction: r.migrationTransaction,
40
+ })
41
+
42
+ createResult = (r: CreateTokenResult) => ({
43
+ transaction: r.transaction,
44
+ additionalTransactions: r.additionalTransactions,
45
+ message: r.message,
46
+ mint: r.mint,
47
+ mintKeypair: r.mintKeypair,
48
+ })
49
+
50
+ factionStatus = (status: TokenStatus) => STATUS_MAP[status]
51
+
52
+ holdersResult = (r: HoldersResult) => ({
53
+ members: r.holders.map(this.holderToMember),
54
+ total_members: r.total_holders,
55
+ })
56
+
57
+ holderToMember = (h: Holder) => ({
58
+ address: h.address,
59
+ balance: h.balance,
60
+ percentage: h.percentage,
61
+ })
62
+
63
+ lendingToWarChest = (l: LendingInfo) => ({
64
+ interest_rate_bps: l.interest_rate_bps,
65
+ max_ltv_bps: l.max_ltv_bps,
66
+ liquidation_threshold_bps: l.liquidation_threshold_bps,
67
+ liquidation_bonus_bps: l.liquidation_bonus_bps,
68
+ utilization_cap_bps: l.utilization_cap_bps,
69
+ borrow_share_multiplier: l.borrow_share_multiplier,
70
+ total_sol_lent: l.total_sol_lent,
71
+ active_loans: l.active_loans,
72
+ war_chest_sol_available: l.treasury_sol_available,
73
+ warnings: l.warnings,
74
+ })
75
+
76
+ loanToWarLoan = (l: LoanPositionInfo) => ({
77
+ collateral_amount: l.collateral_amount,
78
+ borrowed_amount: l.borrowed_amount,
79
+ accrued_interest: l.accrued_interest,
80
+ total_owed: l.total_owed,
81
+ collateral_value_sol: l.collateral_value_sol,
82
+ current_ltv_bps: l.current_ltv_bps,
83
+ health: l.health,
84
+ warnings: l.warnings,
85
+ })
86
+
87
+ loanWithKeyToWarLoan = (l: LoanPositionWithKey) => ({
88
+ ...this.loanToWarLoan(l),
89
+ borrower: l.borrower,
90
+ })
91
+
92
+ messagesResult = (r: MessagesResult) => ({
93
+ comms: r.messages.map(this.tokenMessageToComms),
94
+ total: r.total,
95
+ })
96
+
97
+ strategy = (vote: 'burn' | 'return') => (vote === 'burn' ? 'smelt' : 'fortify')
98
+
99
+ tokenDetailToFaction = (t: TokenDetail) => ({
100
+ mint: t.mint,
101
+ name: t.name,
102
+ symbol: t.symbol,
103
+ description: t.description,
104
+ image: t.image,
105
+ status: this.factionStatus(t.status),
106
+ price_sol: t.price_sol,
107
+ price_usd: t.price_usd,
108
+ market_cap_sol: t.market_cap_sol,
109
+ market_cap_usd: t.market_cap_usd,
110
+ progress_percent: t.progress_percent,
111
+ sol_raised: t.sol_raised,
112
+ sol_target: t.sol_target,
113
+ total_supply: t.total_supply,
114
+ circulating_supply: t.circulating_supply,
115
+ tokens_in_curve: t.tokens_in_curve,
116
+ tokens_in_vote_vault: t.tokens_in_vote_vault,
117
+ tokens_burned: t.tokens_burned,
118
+ war_chest_sol: t.treasury_sol_balance,
119
+ war_chest_tokens: t.treasury_token_balance,
120
+ total_bought_back: t.total_bought_back,
121
+ buyback_count: t.buyback_count,
122
+ votes_scorched_earth: t.votes_burn,
123
+ votes_fortify: t.votes_return,
124
+ founder: t.creator,
125
+ members: t.holders,
126
+ rallies: t.stars,
127
+ created_at: t.created_at,
128
+ last_activity_at: t.last_activity_at,
129
+ twitter: t.twitter,
130
+ telegram: t.telegram,
131
+ website: t.website,
132
+ founder_verified: t.creator_verified,
133
+ founder_trust_tier: t.creator_trust_tier,
134
+ founder_said_name: t.creator_said_name,
135
+ founder_badge_url: t.creator_badge_url,
136
+ warnings: t.warnings,
137
+ })
138
+
139
+ tokenListResult = (r: TokenListResult) => ({
140
+ factions: r.tokens.map(this.tokenSummaryToFaction),
141
+ total: r.total,
142
+ limit: r.limit,
143
+ offset: r.offset,
144
+ })
145
+
146
+ tokenMessageToComms = (m: TokenMessage) => ({
147
+ signature: m.signature,
148
+ memo: m.memo,
149
+ sender: m.sender,
150
+ timestamp: m.timestamp,
151
+ sender_verified: m.sender_verified,
152
+ sender_trust_tier: m.sender_trust_tier,
153
+ sender_said_name: m.sender_said_name,
154
+ sender_badge_url: m.sender_badge_url,
155
+ })
156
+
157
+ tokenStatus = (status: FactionStatus) => STATUS_REVERSE[status]
158
+
159
+ tokenStatusFilter = (status: FactionStatusFilter) => STATUS_FILTER_REVERSE[status]
160
+
161
+ tokenSummaryToFaction = (t: TokenSummary) => ({
162
+ mint: t.mint,
163
+ name: t.name,
164
+ symbol: t.symbol,
165
+ status: this.factionStatus(t.status),
166
+ price_sol: t.price_sol,
167
+ market_cap_sol: t.market_cap_sol,
168
+ progress_percent: t.progress_percent,
169
+ members: t.holders,
170
+ created_at: t.created_at,
171
+ last_activity_at: t.last_activity_at,
172
+ })
173
+
174
+ vote = (strategy: Strategy) => (strategy === 'smelt' ? 'burn' : 'return')
175
+
176
+ vaultToStronghold = (v: VaultInfo) => ({
177
+ address: v.address,
178
+ creator: v.creator,
179
+ authority: v.authority,
180
+ sol_balance: v.sol_balance,
181
+ total_deposited: v.total_deposited,
182
+ total_withdrawn: v.total_withdrawn,
183
+ total_spent: v.total_spent,
184
+ total_received: v.total_received,
185
+ linked_agents: v.linked_wallets,
186
+ created_at: v.created_at,
187
+ })
188
+
189
+ walletLinkToAgentLink = (l: VaultWalletLinkInfo) => ({
190
+ address: l.address,
191
+ stronghold: l.vault,
192
+ wallet: l.wallet,
193
+ linked_at: l.linked_at,
194
+ })
195
+ }