pyre-world-kit 2.0.12 → 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.
- package/.prettierrc.json +6 -0
- package/dist/index.d.ts +38 -4
- package/dist/index.js +100 -85
- package/dist/providers/action.provider.d.ts +46 -0
- package/dist/providers/action.provider.js +331 -0
- package/dist/providers/intel.provider.d.ts +29 -0
- package/dist/providers/intel.provider.js +363 -0
- package/dist/providers/mapper.provider.d.ts +197 -0
- package/dist/providers/mapper.provider.js +158 -0
- package/dist/providers/registry.provider.d.ts +25 -0
- package/dist/providers/registry.provider.js +229 -0
- package/dist/providers/state.provider.d.ts +42 -0
- package/dist/providers/state.provider.js +348 -0
- package/dist/pyre_world.json +34 -229
- package/dist/types/action.types.d.ts +41 -0
- package/dist/types/action.types.js +2 -0
- package/dist/types/intel.types.d.ts +20 -0
- package/dist/types/intel.types.js +2 -0
- package/dist/types/mapper.types.d.ts +27 -0
- package/dist/types/mapper.types.js +22 -0
- package/dist/types/registry.types.d.ts +0 -0
- package/dist/types/registry.types.js +1 -0
- package/dist/types/state.types.d.ts +112 -0
- package/dist/types/state.types.js +2 -0
- package/dist/types.d.ts +8 -24
- package/dist/util.d.ts +29 -0
- package/dist/util.js +144 -0
- package/dist/vanity.d.ts +3 -3
- package/dist/vanity.js +18 -15
- package/package.json +4 -2
- package/readme.md +134 -122
- package/src/index.ts +127 -92
- package/src/providers/action.provider.ts +443 -0
- package/src/providers/intel.provider.ts +383 -0
- package/src/providers/mapper.provider.ts +195 -0
- package/src/providers/registry.provider.ts +277 -0
- package/src/providers/state.provider.ts +357 -0
- package/src/pyre_world.json +35 -230
- package/src/types/action.types.ts +76 -0
- package/src/types/intel.types.ts +22 -0
- package/src/types/mapper.types.ts +84 -0
- package/src/types/registry.types.ts +0 -0
- package/src/types/state.types.ts +144 -0
- package/src/types.ts +329 -333
- package/src/util.ts +148 -0
- package/src/vanity.ts +27 -14
- package/tests/test_e2e.ts +339 -172
- package/src/actions.ts +0 -719
- package/src/intel.ts +0 -521
- package/src/mappers.ts +0 -302
- package/src/registry.ts +0 -317
- package/tests/test_devnet_e2e.ts +0 -401
- package/tests/test_sim.ts +0 -458
package/tests/test_e2e.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Pyre Kit E2E Test
|
|
3
3
|
*
|
|
4
4
|
* Tests the full faction warfare flow against a surfpool fork.
|
|
5
|
+
* All operations are vault-routed. Verifies SOL + token balances,
|
|
6
|
+
* state tracking (tick, sentiment, holdings, history), and PNL.
|
|
5
7
|
*
|
|
6
8
|
* Prerequisites:
|
|
7
9
|
* surfpool start --network mainnet --no-tui
|
|
@@ -10,204 +12,369 @@
|
|
|
10
12
|
* pnpm test (or: npx tsx tests/test_e2e.ts)
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
|
-
import { Connection, LAMPORTS_PER_SOL } from '@solana/web3.js'
|
|
15
|
+
import { Connection, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'
|
|
16
|
+
import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token'
|
|
14
17
|
import {
|
|
18
|
+
PyreKit,
|
|
15
19
|
createEphemeralAgent,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
startVaultPnlTracker,
|
|
21
|
+
} from '../src/index.js'
|
|
22
|
+
|
|
23
|
+
const RPC_URL = process.env.RPC_URL ?? 'http://localhost:8899'
|
|
24
|
+
let passed = 0
|
|
25
|
+
let failed = 0
|
|
26
|
+
|
|
27
|
+
function assert(condition: boolean, msg: string) {
|
|
28
|
+
if (!condition) {
|
|
29
|
+
console.error(` ✗ FAIL: ${msg}`)
|
|
30
|
+
failed++
|
|
31
|
+
} else {
|
|
32
|
+
console.log(` ✓ ${msg}`)
|
|
33
|
+
passed++
|
|
34
|
+
}
|
|
35
|
+
}
|
|
32
36
|
|
|
33
37
|
async function sendAndConfirm(connection: Connection, agent: ReturnType<typeof createEphemeralAgent>, result: any) {
|
|
34
|
-
const tx = result.transaction
|
|
35
|
-
const signed = agent.sign(tx)
|
|
36
|
-
const sig = await connection.sendRawTransaction(signed.serialize())
|
|
37
|
-
await connection.confirmTransaction(sig, 'confirmed')
|
|
38
|
-
console.log(` tx: ${sig}`)
|
|
38
|
+
const tx = result.transaction
|
|
39
|
+
const signed = agent.sign(tx)
|
|
40
|
+
const sig = await connection.sendRawTransaction(signed.serialize())
|
|
41
|
+
await connection.confirmTransaction(sig, 'confirmed')
|
|
42
|
+
console.log(` tx: ${sig}`)
|
|
39
43
|
|
|
40
|
-
// Handle additional transactions
|
|
41
44
|
if (result.additionalTransactions) {
|
|
42
45
|
for (const addlTx of result.additionalTransactions) {
|
|
43
|
-
const addlSigned = agent.sign(addlTx)
|
|
44
|
-
const addlSig = await connection.sendRawTransaction(addlSigned.serialize())
|
|
45
|
-
await connection.confirmTransaction(addlSig, 'confirmed')
|
|
46
|
-
console.log(` additional tx: ${addlSig}`)
|
|
46
|
+
const addlSigned = agent.sign(addlTx)
|
|
47
|
+
const addlSig = await connection.sendRawTransaction(addlSigned.serialize())
|
|
48
|
+
await connection.confirmTransaction(addlSig, 'confirmed')
|
|
49
|
+
console.log(` additional tx: ${addlSig}`)
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
return sig
|
|
53
|
+
return sig
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function sol(lamports: number): string {
|
|
57
|
+
return (lamports / LAMPORTS_PER_SOL).toFixed(6)
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
async function main() {
|
|
54
|
-
const connection = new Connection(RPC_URL, 'confirmed')
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
const connection = new Connection(RPC_URL, 'confirmed')
|
|
62
|
+
const agent = createEphemeralAgent()
|
|
63
|
+
const agent2 = createEphemeralAgent()
|
|
64
|
+
|
|
65
|
+
// Each agent gets its own kit (state is per-agent)
|
|
66
|
+
const kit = new PyreKit(connection, agent.publicKey)
|
|
67
|
+
const kit2 = new PyreKit(connection, agent2.publicKey)
|
|
68
|
+
|
|
69
|
+
/** Get token balance across wallet ATA + vault ATA */
|
|
70
|
+
async function getTokenBalance(k: PyreKit, mint: string, owner: string): Promise<number> {
|
|
71
|
+
const mintPk = new PublicKey(mint)
|
|
72
|
+
let total = 0
|
|
73
|
+
try {
|
|
74
|
+
const ata = getAssociatedTokenAddressSync(mintPk, new PublicKey(owner), false, TOKEN_2022_PROGRAM_ID)
|
|
75
|
+
const info = await connection.getTokenAccountBalance(ata)
|
|
76
|
+
total += Number(info.value.amount)
|
|
77
|
+
} catch {}
|
|
78
|
+
try {
|
|
79
|
+
const vault = await k.actions.getStrongholdForAgent(owner)
|
|
80
|
+
if (vault) {
|
|
81
|
+
const vaultPk = new PublicKey(vault.address)
|
|
82
|
+
const vaultAta = getAssociatedTokenAddressSync(mintPk, vaultPk, true, TOKEN_2022_PROGRAM_ID)
|
|
83
|
+
const info = await connection.getTokenAccountBalance(vaultAta)
|
|
84
|
+
total += Number(info.value.amount)
|
|
85
|
+
}
|
|
86
|
+
} catch {}
|
|
87
|
+
return total
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Get vault SOL balance in lamports */
|
|
91
|
+
async function getVaultSolLamports(k: PyreKit, wallet: string): Promise<number> {
|
|
92
|
+
try {
|
|
93
|
+
const vault = await k.actions.getStrongholdForAgent(wallet)
|
|
94
|
+
return vault ? Math.round(vault.sol_balance * LAMPORTS_PER_SOL) : 0
|
|
95
|
+
} catch {
|
|
96
|
+
return 0
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log(`Pyre Kit E2E Test — RPC: ${RPC_URL}\n`)
|
|
101
|
+
|
|
102
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
103
|
+
// SETUP: Agents + Vaults + Faction
|
|
104
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
105
|
+
|
|
106
|
+
console.log('1. Creating agents...')
|
|
107
|
+
console.log(` Agent 1: ${agent.publicKey}`)
|
|
108
|
+
console.log(` Agent 2: ${agent2.publicKey}`)
|
|
109
|
+
|
|
110
|
+
console.log(' Requesting airdrops...')
|
|
66
111
|
const [airdropSig, airdropSig2] = await Promise.all([
|
|
67
112
|
connection.requestAirdrop(agent.keypair.publicKey, 10 * LAMPORTS_PER_SOL),
|
|
68
|
-
connection.requestAirdrop(agent2.keypair.publicKey,
|
|
69
|
-
])
|
|
113
|
+
connection.requestAirdrop(agent2.keypair.publicKey, 10 * LAMPORTS_PER_SOL),
|
|
114
|
+
])
|
|
70
115
|
await Promise.all([
|
|
71
116
|
connection.confirmTransaction(airdropSig, 'confirmed'),
|
|
72
117
|
connection.confirmTransaction(airdropSig2, 'confirmed'),
|
|
73
|
-
])
|
|
74
|
-
console.log(' Airdrops confirmed');
|
|
75
|
-
|
|
76
|
-
// 2. Create stronghold (vault)
|
|
77
|
-
console.log('\n2. Creating stronghold...');
|
|
78
|
-
const strongholdResult = await createStronghold(connection, {
|
|
79
|
-
creator: agent.publicKey,
|
|
80
|
-
});
|
|
81
|
-
await sendAndConfirm(connection, agent, strongholdResult);
|
|
82
|
-
console.log(' Stronghold created');
|
|
83
|
-
|
|
84
|
-
// 3. Fund stronghold
|
|
85
|
-
console.log('\n3. Funding stronghold...');
|
|
86
|
-
const fundResult = await fundStronghold(connection, {
|
|
87
|
-
depositor: agent.publicKey,
|
|
88
|
-
stronghold_creator: agent.publicKey,
|
|
89
|
-
amount_sol: 5 * LAMPORTS_PER_SOL,
|
|
90
|
-
});
|
|
91
|
-
await sendAndConfirm(connection, agent, fundResult);
|
|
92
|
-
console.log(' Funded 5 SOL');
|
|
93
|
-
|
|
94
|
-
// 4. Recruit agent (link wallet)
|
|
95
|
-
console.log('\n4. Recruiting agent (self-link)...');
|
|
96
|
-
// Agent is already linked as creator, but let's verify
|
|
97
|
-
const agentLink = await getStrongholdForAgent(connection, agent.publicKey);
|
|
98
|
-
console.log(` Stronghold found: ${agentLink ? 'yes' : 'no'}`);
|
|
99
|
-
if (agentLink) {
|
|
100
|
-
console.log(` SOL balance: ${agentLink.sol_balance / LAMPORTS_PER_SOL} SOL`);
|
|
101
|
-
}
|
|
118
|
+
])
|
|
102
119
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
120
|
+
console.log('\n2. Creating vaults...')
|
|
121
|
+
const vault1 = await kit.actions.createStronghold({ creator: agent.publicKey })
|
|
122
|
+
await sendAndConfirm(connection, agent, vault1)
|
|
123
|
+
const vault2 = await kit2.actions.createStronghold({ creator: agent2.publicKey })
|
|
124
|
+
await sendAndConfirm(connection, agent2, vault2)
|
|
125
|
+
|
|
126
|
+
console.log('\n3. Funding vaults...')
|
|
127
|
+
const fund1 = await kit.actions.fundStronghold({
|
|
128
|
+
depositor: agent.publicKey, stronghold_creator: agent.publicKey, amount_sol: 5 * LAMPORTS_PER_SOL,
|
|
129
|
+
})
|
|
130
|
+
await sendAndConfirm(connection, agent, fund1)
|
|
131
|
+
const fund2 = await kit2.actions.fundStronghold({
|
|
132
|
+
depositor: agent2.publicKey, stronghold_creator: agent2.publicKey, amount_sol: 5 * LAMPORTS_PER_SOL,
|
|
133
|
+
})
|
|
134
|
+
await sendAndConfirm(connection, agent2, fund2)
|
|
135
|
+
|
|
136
|
+
// Initialize state from chain (resolves vault, loads holdings)
|
|
137
|
+
console.log('\n4. Initializing state...')
|
|
138
|
+
const state1 = await kit.state.init()
|
|
139
|
+
const state2 = await kit2.state.init()
|
|
140
|
+
console.log(` Agent 1 vault: ${state1.vaultCreator ?? 'none'}`)
|
|
141
|
+
console.log(` Agent 2 vault: ${state2.vaultCreator ?? 'none'}`)
|
|
142
|
+
assert(state1.initialized, 'agent 1 state initialized')
|
|
143
|
+
assert(state2.initialized, 'agent 2 state initialized')
|
|
144
|
+
assert(state1.vaultCreator !== null, 'agent 1 vault resolved')
|
|
145
|
+
assert(state2.vaultCreator !== null, 'agent 2 vault resolved')
|
|
146
|
+
assert(kit.state.tick === 0, `agent 1 tick starts at 0: ${kit.state.tick}`)
|
|
147
|
+
|
|
148
|
+
console.log('\n5. Launching faction...')
|
|
149
|
+
const launchResult = await kit.actions.launch({
|
|
150
|
+
founder: agent.publicKey, name: 'Balance Test Faction', symbol: 'BTEST',
|
|
151
|
+
metadata_uri: 'https://pyre.gg/test.json', community_faction: true,
|
|
152
|
+
})
|
|
153
|
+
await sendAndConfirm(connection, agent, launchResult)
|
|
154
|
+
await kit.state.record('launch', launchResult.mint.toBase58(), 'launched BTEST')
|
|
155
|
+
const mint = launchResult.mint.toBase58()
|
|
156
|
+
console.log(` Faction: ${mint}`)
|
|
157
|
+
|
|
158
|
+
assert(kit.state.tick === 1, `tick incremented to 1: ${kit.state.tick}`)
|
|
159
|
+
assert(kit.state.state!.actionCounts.launch === 1, `launch count = 1`)
|
|
160
|
+
assert(kit.state.getSentiment(mint) === 3, `launch sentiment = +3: ${kit.state.getSentiment(mint)}`)
|
|
161
|
+
assert(kit.state.history.length === 1, `history has 1 entry`)
|
|
162
|
+
|
|
163
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
164
|
+
// TEST: VAULT BUY + STATE TRACKING
|
|
165
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
166
|
+
|
|
167
|
+
console.log('\n═══ TEST: Vault buy (join) + state tracking ═══')
|
|
168
|
+
const buyLamports = Math.floor(0.5 * LAMPORTS_PER_SOL)
|
|
169
|
+
|
|
170
|
+
const buyVaultBefore = await getVaultSolLamports(kit, agent.publicKey)
|
|
171
|
+
const buyTokensBefore = await getTokenBalance(kit, mint, agent.publicKey)
|
|
172
|
+
const buyTotalBefore = await kit.intel.getAgentSolLamports(agent.publicKey)
|
|
173
|
+
|
|
174
|
+
console.log(` PRE — vault: ${sol(buyVaultBefore)}, tokens: ${buyTokensBefore}, total: ${sol(buyTotalBefore)}`)
|
|
175
|
+
|
|
176
|
+
const pnl = await startVaultPnlTracker(kit.intel, agent.publicKey)
|
|
177
|
+
|
|
178
|
+
const joinResult = await kit.actions.join({
|
|
179
|
+
mint, agent: agent.publicKey, amount_sol: buyLamports,
|
|
180
|
+
strategy: 'fortify', message: 'Vault buy test', stronghold: agent.publicKey,
|
|
181
|
+
})
|
|
182
|
+
await sendAndConfirm(connection, agent, joinResult)
|
|
183
|
+
await kit.state.record('join', mint, 'joined BTEST for 0.5 SOL — "Vault buy test"')
|
|
184
|
+
|
|
185
|
+
const buyVaultAfter = await getVaultSolLamports(kit, agent.publicKey)
|
|
186
|
+
const buyTokensAfter = await getTokenBalance(kit, mint, agent.publicKey)
|
|
187
|
+
const buyTotalAfter = await kit.intel.getAgentSolLamports(agent.publicKey)
|
|
188
|
+
const { spent, received } = await pnl.finish()
|
|
189
|
+
|
|
190
|
+
console.log(` POST — vault: ${sol(buyVaultAfter)}, tokens: ${buyTokensAfter}, total: ${sol(buyTotalAfter)}`)
|
|
191
|
+
console.log(` PNL — spent: ${sol(spent)}, received: ${sol(received)}`)
|
|
192
|
+
|
|
193
|
+
// Balance assertions
|
|
194
|
+
assert(buyTokensAfter > buyTokensBefore, `tokens increased: ${buyTokensBefore} → ${buyTokensAfter}`)
|
|
195
|
+
assert(buyVaultAfter < buyVaultBefore, `vault SOL decreased: ${sol(buyVaultBefore)} → ${sol(buyVaultAfter)}`)
|
|
196
|
+
assert(buyTotalAfter < buyTotalBefore, `total SOL decreased: ${sol(buyTotalBefore)} → ${sol(buyTotalAfter)}`)
|
|
197
|
+
assert(spent > 0, `PNL spent > 0: ${sol(spent)}`)
|
|
198
|
+
|
|
199
|
+
// State assertions
|
|
200
|
+
assert(kit.state.tick === 2, `tick = 2: ${kit.state.tick}`)
|
|
201
|
+
assert(kit.state.state!.actionCounts.join === 1, `join count = 1`)
|
|
202
|
+
assert(kit.state.getSentiment(mint) === 4, `sentiment after launch(+3) + join(+1) = 4: ${kit.state.getSentiment(mint)}`)
|
|
203
|
+
assert(kit.state.getBalance(mint) > 0, `state holdings updated: ${kit.state.getBalance(mint)}`)
|
|
204
|
+
|
|
205
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
206
|
+
// TEST: VAULT SELL (defect) + SENTIMENT
|
|
207
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
208
|
+
|
|
209
|
+
console.log('\n═══ TEST: Vault defect (sell half) + sentiment ═══')
|
|
210
|
+
const sellAmount = Math.floor(buyTokensAfter / 2)
|
|
211
|
+
const sentimentBeforeDefect = kit.state.getSentiment(mint)
|
|
212
|
+
|
|
213
|
+
const defVaultBefore = await getVaultSolLamports(kit, agent.publicKey)
|
|
214
|
+
const defTokensBefore = await getTokenBalance(kit, mint, agent.publicKey)
|
|
215
|
+
const defTotalBefore = await kit.intel.getAgentSolLamports(agent.publicKey)
|
|
125
216
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
console.log(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
217
|
+
console.log(` PRE — vault: ${sol(defVaultBefore)}, tokens: ${defTokensBefore}, total: ${sol(defTotalBefore)}, sentiment: ${sentimentBeforeDefect}`)
|
|
218
|
+
|
|
219
|
+
const defPnl = await startVaultPnlTracker(kit.intel, agent.publicKey)
|
|
220
|
+
|
|
221
|
+
const defectResult = await kit.actions.defect({
|
|
222
|
+
mint, agent: agent.publicKey, amount_tokens: sellAmount,
|
|
223
|
+
message: 'Vault defect test', stronghold: agent.publicKey,
|
|
224
|
+
})
|
|
225
|
+
await sendAndConfirm(connection, agent, defectResult)
|
|
226
|
+
await kit.state.record('defect', mint, 'defected from BTEST — "Vault defect test"')
|
|
227
|
+
|
|
228
|
+
const defVaultAfter = await getVaultSolLamports(kit, agent.publicKey)
|
|
229
|
+
const defTokensAfter = await getTokenBalance(kit, mint, agent.publicKey)
|
|
230
|
+
const defTotalAfter = await kit.intel.getAgentSolLamports(agent.publicKey)
|
|
231
|
+
const defPnlResult = await defPnl.finish()
|
|
232
|
+
|
|
233
|
+
console.log(` POST — vault: ${sol(defVaultAfter)}, tokens: ${defTokensAfter}, total: ${sol(defTotalAfter)}, sentiment: ${kit.state.getSentiment(mint)}`)
|
|
234
|
+
console.log(` PNL — spent: ${sol(defPnlResult.spent)}, received: ${sol(defPnlResult.received)}`)
|
|
235
|
+
console.log(` VAULT DELTA: ${sol(defVaultAfter - defVaultBefore)}`)
|
|
236
|
+
|
|
237
|
+
// Balance assertions
|
|
238
|
+
assert(defTokensAfter < defTokensBefore, `tokens decreased: ${defTokensBefore} → ${defTokensAfter}`)
|
|
239
|
+
assert(defTotalAfter > defTotalBefore, `total SOL increased: ${sol(defTotalBefore)} → ${sol(defTotalAfter)}`)
|
|
240
|
+
assert(defVaultAfter > defVaultBefore, `vault SOL increased: ${sol(defVaultBefore)} → ${sol(defVaultAfter)}`)
|
|
241
|
+
assert(defPnlResult.received > 0, `PNL received > 0: ${sol(defPnlResult.received)}`)
|
|
242
|
+
|
|
243
|
+
// State assertions
|
|
244
|
+
assert(kit.state.tick === 3, `tick = 3: ${kit.state.tick}`)
|
|
245
|
+
assert(kit.state.getSentiment(mint) === sentimentBeforeDefect - 2, `defect drops sentiment by 2: ${sentimentBeforeDefect} → ${kit.state.getSentiment(mint)}`)
|
|
246
|
+
|
|
247
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
248
|
+
// TEST: FUD + SENTIMENT
|
|
249
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
250
|
+
|
|
251
|
+
console.log('\n═══ TEST: FUD (micro sell) + sentiment ═══')
|
|
252
|
+
const sentimentBeforeFud = kit.state.getSentiment(mint)
|
|
253
|
+
const fudTokensBefore = await getTokenBalance(kit, mint, agent.publicKey)
|
|
254
|
+
|
|
255
|
+
console.log(` PRE — tokens: ${fudTokensBefore}, sentiment: ${sentimentBeforeFud}`)
|
|
256
|
+
|
|
257
|
+
const fudResult = await kit.actions.fud({
|
|
258
|
+
mint, agent: agent.publicKey, message: 'This faction is weak!',
|
|
149
259
|
stronghold: agent.publicKey,
|
|
150
|
-
})
|
|
151
|
-
await sendAndConfirm(connection, agent,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
console.log(`
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
260
|
+
})
|
|
261
|
+
await sendAndConfirm(connection, agent, fudResult)
|
|
262
|
+
await kit.state.record('fud', mint, 'fud BTEST — "This faction is weak!"')
|
|
263
|
+
|
|
264
|
+
const fudTokensAfter = await getTokenBalance(kit, mint, agent.publicKey)
|
|
265
|
+
const fudTokenDelta = fudTokensBefore - fudTokensAfter
|
|
266
|
+
|
|
267
|
+
console.log(` POST — tokens: ${fudTokensAfter}, sentiment: ${kit.state.getSentiment(mint)}`)
|
|
268
|
+
console.log(` TOKEN DELTA: ${fudTokenDelta} (expect ~100)`)
|
|
269
|
+
|
|
270
|
+
assert(fudTokenDelta > 0, `FUD sold tokens: ${fudTokenDelta}`)
|
|
271
|
+
assert(fudTokenDelta <= 100, `FUD micro amount: ${fudTokenDelta}`)
|
|
272
|
+
assert(kit.state.tick === 4, `tick = 4: ${kit.state.tick}`)
|
|
273
|
+
assert(kit.state.getSentiment(mint) === sentimentBeforeFud - 1.5, `fud drops sentiment by 1.5: ${kit.state.getSentiment(mint)}`)
|
|
161
274
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// Verify rally
|
|
172
|
-
const detailAfterRally = await getFaction(connection, factionMint);
|
|
173
|
-
console.log(` Rallies: ${detailAfterRally.rallies}`);
|
|
174
|
-
|
|
175
|
-
// 12. Defect with message
|
|
176
|
-
console.log('\n12. Defecting from faction...');
|
|
177
|
-
// Sell half of what we bought
|
|
178
|
-
const sellAmount = Math.floor(quote.tokens_to_user / 2);
|
|
179
|
-
const defectResult = await defect(connection, {
|
|
180
|
-
mint: factionMint,
|
|
181
|
-
agent: agent.publicKey,
|
|
182
|
-
amount_tokens: sellAmount,
|
|
183
|
-
message: 'Strategic withdrawal. Will return.',
|
|
275
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
276
|
+
// TEST: MESSAGE + STATE
|
|
277
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
278
|
+
|
|
279
|
+
console.log('\n═══ TEST: Message (micro buy) + state ═══')
|
|
280
|
+
const sentimentBeforeMsg = kit.state.getSentiment(mint)
|
|
281
|
+
|
|
282
|
+
const msgResult = await kit.actions.message({
|
|
283
|
+
mint, agent: agent.publicKey, message: 'We are strong!',
|
|
184
284
|
stronghold: agent.publicKey,
|
|
185
|
-
})
|
|
186
|
-
await sendAndConfirm(connection, agent,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const members = await getMembers(connection, factionMint);
|
|
192
|
-
console.log(` Total members: ${members.total_members}`);
|
|
193
|
-
for (const m of members.members.slice(0, 5)) {
|
|
194
|
-
console.log(` ${m.address.slice(0, 8)}... — ${m.balance} (${m.percentage.toFixed(2)}%)`);
|
|
195
|
-
}
|
|
285
|
+
})
|
|
286
|
+
await sendAndConfirm(connection, agent, msgResult)
|
|
287
|
+
await kit.state.record('message', mint, 'said in BTEST — "We are strong!"')
|
|
288
|
+
|
|
289
|
+
assert(kit.state.tick === 5, `tick = 5: ${kit.state.tick}`)
|
|
290
|
+
assert(kit.state.getSentiment(mint) === sentimentBeforeMsg + 0.5, `message bumps sentiment by 0.5: ${kit.state.getSentiment(mint)}`)
|
|
196
291
|
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
292
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
293
|
+
// TEST: AGENT 2 JOIN + SEPARATE STATE
|
|
294
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
295
|
+
|
|
296
|
+
console.log('\n═══ TEST: Agent 2 join (separate state) ═══')
|
|
297
|
+
assert(kit2.state.tick === 0, `agent 2 tick starts at 0: ${kit2.state.tick}`)
|
|
298
|
+
|
|
299
|
+
const join2 = await kit2.actions.join({
|
|
300
|
+
mint, agent: agent2.publicKey, amount_sol: Math.floor(0.3 * LAMPORTS_PER_SOL),
|
|
301
|
+
strategy: 'smelt', message: 'Agent 2 joining', stronghold: agent2.publicKey,
|
|
302
|
+
})
|
|
303
|
+
await sendAndConfirm(connection, agent2, join2)
|
|
304
|
+
await kit2.state.record('join', mint, 'joined BTEST')
|
|
305
|
+
|
|
306
|
+
assert(kit2.state.tick === 1, `agent 2 tick = 1: ${kit2.state.tick}`)
|
|
307
|
+
assert(kit.state.tick === 5, `agent 1 tick unchanged: ${kit.state.tick}`)
|
|
308
|
+
assert(kit2.state.getSentiment(mint) === 1, `agent 2 sentiment = +1 (join): ${kit2.state.getSentiment(mint)}`)
|
|
309
|
+
|
|
310
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
311
|
+
// TEST: RALLY + STATE
|
|
312
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
313
|
+
|
|
314
|
+
console.log('\n═══ TEST: Rally + state ═══')
|
|
315
|
+
const detail1 = await kit.actions.getFaction(mint)
|
|
316
|
+
|
|
317
|
+
const rallyResult = await kit2.actions.rally({
|
|
318
|
+
mint, agent: agent2.publicKey, stronghold: agent2.publicKey,
|
|
319
|
+
})
|
|
320
|
+
await sendAndConfirm(connection, agent2, rallyResult)
|
|
321
|
+
await kit2.state.record('rally', mint, 'rallied BTEST')
|
|
322
|
+
kit2.state.markRallied(mint)
|
|
323
|
+
|
|
324
|
+
const detail2 = await kit.actions.getFaction(mint)
|
|
325
|
+
assert(detail2.rallies > detail1.rallies, `rallies increased: ${detail1.rallies} → ${detail2.rallies}`)
|
|
326
|
+
assert(kit2.state.tick === 2, `agent 2 tick = 2: ${kit2.state.tick}`)
|
|
327
|
+
assert(kit2.state.getSentiment(mint) === 4, `agent 2 sentiment after join(+1) + rally(+3) = 4: ${kit2.state.getSentiment(mint)}`)
|
|
328
|
+
assert(kit2.state.hasRallied(mint), `agent 2 marked as rallied`)
|
|
329
|
+
|
|
330
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
331
|
+
// TEST: SERIALIZE / HYDRATE
|
|
332
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
333
|
+
|
|
334
|
+
console.log('\n═══ TEST: Serialize / Hydrate ═══')
|
|
335
|
+
const serialized = kit.state.serialize()
|
|
336
|
+
console.log(` Serialized: tick=${serialized.tick}, actions=${JSON.stringify(serialized.actionCounts)}, sentiment keys=${Object.keys(serialized.sentiment).length}, history=${serialized.recentHistory.length}`)
|
|
337
|
+
|
|
338
|
+
// Create a fresh kit and hydrate
|
|
339
|
+
const kitRestored = new PyreKit(connection, agent.publicKey)
|
|
340
|
+
kitRestored.state.hydrate(serialized)
|
|
341
|
+
|
|
342
|
+
assert(kitRestored.state.tick === kit.state.tick, `hydrated tick matches: ${kitRestored.state.tick}`)
|
|
343
|
+
assert(kitRestored.state.getSentiment(mint) === kit.state.getSentiment(mint), `hydrated sentiment matches: ${kitRestored.state.getSentiment(mint)}`)
|
|
344
|
+
assert(kitRestored.state.state!.actionCounts.join === kit.state.state!.actionCounts.join, `hydrated join count matches`)
|
|
345
|
+
assert(kitRestored.state.state!.actionCounts.defect === kit.state.state!.actionCounts.defect, `hydrated defect count matches`)
|
|
346
|
+
assert(kitRestored.state.history.length === kit.state.history.length, `hydrated history length matches: ${kitRestored.state.history.length}`)
|
|
347
|
+
|
|
348
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
349
|
+
// TEST: MEMBERS
|
|
350
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
351
|
+
|
|
352
|
+
console.log('\n═══ TEST: Members ═══')
|
|
353
|
+
try {
|
|
354
|
+
const members = await kit.actions.getMembers(mint)
|
|
355
|
+
console.log(` Total members: ${members.total_members}`)
|
|
356
|
+
for (const m of members.members.slice(0, 5)) {
|
|
357
|
+
console.log(` ${m.address.slice(0, 8)}... — ${m.balance} (${m.percentage.toFixed(2)}%)`)
|
|
358
|
+
}
|
|
359
|
+
assert(members.total_members > 0, `has members: ${members.total_members}`)
|
|
360
|
+
} catch (err: any) {
|
|
361
|
+
console.log(` Skipped — RPC error: ${err.message?.slice(0, 80)}`)
|
|
205
362
|
}
|
|
206
363
|
|
|
207
|
-
|
|
364
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
365
|
+
// RESULTS
|
|
366
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
367
|
+
|
|
368
|
+
console.log(`\n${'═'.repeat(50)}`)
|
|
369
|
+
console.log(`Results: ${passed} passed, ${failed} failed`)
|
|
370
|
+
console.log(`${'═'.repeat(50)}`)
|
|
371
|
+
|
|
372
|
+
if (failed > 0) {
|
|
373
|
+
process.exit(1)
|
|
374
|
+
}
|
|
208
375
|
}
|
|
209
376
|
|
|
210
377
|
main().catch((err) => {
|
|
211
|
-
console.error('\n✗ Test failed:', err)
|
|
212
|
-
process.exit(1)
|
|
213
|
-
})
|
|
378
|
+
console.error('\n✗ Test failed:', err)
|
|
379
|
+
process.exit(1)
|
|
380
|
+
})
|