@dfm-fi/agent 0.2.1553 → 0.2.1557

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=zap-soak.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zap-soak.d.ts","sourceRoot":"","sources":["../../src/examples/zap-soak.ts"],"names":[],"mappings":""}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Soak test: deposit + redeem, over and over, across several vaults, through the
3
+ * app wallet (the same public API a user's browser calls).
4
+ *
5
+ * It exists to answer one question with numbers instead of opinions: what
6
+ * fraction of mints and redeems land, and when one does not, which leg, which
7
+ * venue, which error. Every transaction it touches is appended to a JSONL
8
+ * ledger, so a failure is reproducible after the fact.
9
+ *
10
+ * SPEND BUDGET — the loop stops when EITHER cap is reached, checked before each
11
+ * cycle against the balances it started from. A soak that drains the wallet it
12
+ * tests with is not a soak, it is an outage.
13
+ *
14
+ * DFM_API_URL=https://n2-api.dfm.finance \
15
+ * DFM_AGENT_KEYPAIR_PATH=~/.config/dfm/test-6vch.json \
16
+ * HELIUS_RPC_URL=… VAULTS=<pda,pda> USDC_PER_CYCLE=1 CYCLES=20 \
17
+ * MAX_SOL_SPEND=0.05 MAX_USDC_SPEND=0.5 LEDGER=/path/soak.jsonl \
18
+ * npx tsx src/examples/zap-soak.ts
19
+ */
20
+ import { Connection, PublicKey } from '@solana/web3.js';
21
+ import { loadConfig } from '../config.js';
22
+ import { SiwsSession } from '../session.js';
23
+ import { DfmApiClient } from '../api-client.js';
24
+ import { appendFileSync } from 'node:fs';
25
+ const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
26
+ async function balances(conn, owner) {
27
+ const [lamports, accounts] = await Promise.all([
28
+ conn.getBalance(owner, 'confirmed'),
29
+ conn.getParsedTokenAccountsByOwner(owner, { mint: new PublicKey(USDC_MINT) }, 'confirmed'),
30
+ ]);
31
+ const usdc = accounts.value.reduce((sum, a) => sum + Number(a.account.data.parsed.info.tokenAmount.uiAmount ?? 0), 0);
32
+ return { sol: lamports / 1e9, usdc };
33
+ }
34
+ async function main() {
35
+ const config = loadConfig();
36
+ const api = new DfmApiClient(config, new SiwsSession(config));
37
+ const conn = new Connection(config.rpcUrl, 'confirmed');
38
+ const vaults = (process.env.VAULTS ?? '').split(',').map((v) => v.trim()).filter(Boolean);
39
+ if (vaults.length === 0)
40
+ throw new Error('VAULTS is required (comma-separated vault PDAs)');
41
+ const usdcPerCycle = Number(process.env.USDC_PER_CYCLE ?? '1');
42
+ const cycles = Number(process.env.CYCLES ?? '10');
43
+ const maxSol = Number(process.env.MAX_SOL_SPEND ?? '0.05');
44
+ const maxUsdc = Number(process.env.MAX_USDC_SPEND ?? '0.5');
45
+ const ledger = process.env.LEDGER ?? 'zap-soak.jsonl';
46
+ const float = await api.getOperatingFloatBalance();
47
+ if (!float?.operatingWalletAddress)
48
+ throw new Error('no app wallet for this identity');
49
+ const app = new PublicKey(float.operatingWalletAddress);
50
+ const start = await balances(conn, app);
51
+ console.log(`soak: ${vaults.length} vault(s) × ${cycles} cycles, ${usdcPerCycle} USDC each`);
52
+ console.log(`start: ${start.sol.toFixed(6)} SOL / ${start.usdc.toFixed(6)} USDC`);
53
+ console.log(`budget: ${maxSol} SOL / ${maxUsdc} USDC · ledger ${ledger}\n`);
54
+ const record = (row) => appendFileSync(ledger, `${JSON.stringify({ at: new Date().toISOString(), ...row })}\n`);
55
+ let ok = 0;
56
+ let failed = 0;
57
+ for (let i = 0; i < cycles; i++) {
58
+ const vault = vaults[i % vaults.length];
59
+ const now = await balances(conn, app);
60
+ const spentSol = start.sol - now.sol;
61
+ const spentUsdc = start.usdc - now.usdc;
62
+ if (spentSol >= maxSol || spentUsdc >= maxUsdc) {
63
+ console.log(`\nBUDGET REACHED after ${i} cycles — spent ${spentSol.toFixed(6)} SOL / ${spentUsdc.toFixed(6)} USDC`);
64
+ record({ event: 'budget_stop', cycles: i, spentSol, spentUsdc });
65
+ break;
66
+ }
67
+ const label = `cycle ${i + 1}/${cycles} ${vault.slice(0, 8)}…`;
68
+ try {
69
+ const dep = await api.executeViaOperatingWallet('deposit', {
70
+ vault,
71
+ usdcAmount: String(Math.round(usdcPerCycle * 1e6)),
72
+ });
73
+ record({ event: 'deposit', vault, cycle: i, ...dep });
74
+ // The deposit answers on submit. Shares exist only after the close mints
75
+ // them, and the orchestrator refuses a second open while one is pending —
76
+ // so wait for the escrow to be GONE, not merely for a share balance.
77
+ await waitForSettled(api, vault, float.operatingWalletAddress);
78
+ const shares = await pollShares(api, vault);
79
+ if (!shares)
80
+ throw new Error('no shares minted');
81
+ const red = await withRetry(() => api.executeViaOperatingWallet('redeem', { vault, shares }));
82
+ record({ event: 'redeem', vault, cycle: i, shares, ...red });
83
+ await waitForSettled(api, vault, float.operatingWalletAddress);
84
+ ok++;
85
+ console.log(`${label} OK (${shares} shares)`);
86
+ }
87
+ catch (e) {
88
+ failed++;
89
+ const message = e.message.slice(0, 300);
90
+ record({ event: 'cycle_failed', vault, cycle: i, error: message });
91
+ console.log(`${label} FAILED — ${message}`);
92
+ }
93
+ }
94
+ const end = await balances(conn, app);
95
+ console.log(`\n${ok} ok · ${failed} failed`);
96
+ console.log(`spent: ${(start.sol - end.sol).toFixed(6)} SOL / ${(start.usdc - end.usdc).toFixed(6)} USDC`);
97
+ record({ event: 'summary', ok, failed, solSpent: start.sol - end.sol, usdcSpent: start.usdc - end.usdc });
98
+ }
99
+ /** No escrow for this (user, vault) = the last operation finished settling. */
100
+ async function waitForSettled(api, vault, user) {
101
+ for (let i = 0; i < 60; i++) {
102
+ if (!(await api.getZapV2Status(vault, user)))
103
+ return;
104
+ await new Promise((r) => setTimeout(r, 5_000));
105
+ }
106
+ throw new Error('escrow still open after 5 minutes');
107
+ }
108
+ /** A 409 means "one is already opening", not "this failed" — back off and retry. */
109
+ async function withRetry(fn) {
110
+ let last;
111
+ for (let i = 0; i < 6; i++) {
112
+ try {
113
+ return await fn();
114
+ }
115
+ catch (e) {
116
+ last = e;
117
+ if (!/409|already opening|could not build a complete/i.test(e.message))
118
+ throw e;
119
+ await new Promise((r) => setTimeout(r, 20_000));
120
+ }
121
+ }
122
+ throw last instanceof Error ? last : new Error(String(last));
123
+ }
124
+ /** The deposit returns as soon as it is submitted; shares appear at the mint. */
125
+ async function pollShares(api, vault) {
126
+ for (let i = 0; i < 40; i++) {
127
+ const positions = (await api.getPortfolioPositions());
128
+ const row = Array.isArray(positions) ? positions.find((p) => p.vaultAddress === vault) : undefined;
129
+ if (row?.shares && row.shares !== '0')
130
+ return row.shares;
131
+ await new Promise((r) => setTimeout(r, 5_000));
132
+ }
133
+ return null;
134
+ }
135
+ main().catch((e) => {
136
+ console.error('FAILED', e instanceof Error ? e.message : e);
137
+ process.exit(1);
138
+ });
139
+ //# sourceMappingURL=zap-soak.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zap-soak.js","sourceRoot":"","sources":["../../src/examples/zap-soak.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dfm-fi/agent",
3
- "version": "0.2.1553",
3
+ "version": "0.2.1557",
4
4
  "description": "DFM v2 MCP server — drive the full DTF launch → deposit → redeem (+ manage) lifecycle from an AI agent against the live API.",
5
5
  "type": "module",
6
6
  "bin": {