@dfm-fi/agent 0.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.
Files changed (59) hide show
  1. package/README.md +326 -0
  2. package/dist/api-client.d.ts +137 -0
  3. package/dist/api-client.d.ts.map +1 -0
  4. package/dist/api-client.js +215 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/config.d.ts +56 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +55 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/create-flow.d.ts +62 -0
  11. package/dist/create-flow.d.ts.map +1 -0
  12. package/dist/create-flow.js +88 -0
  13. package/dist/create-flow.js.map +1 -0
  14. package/dist/examples/deposit-redeem-loop.d.ts +2 -0
  15. package/dist/examples/deposit-redeem-loop.d.ts.map +1 -0
  16. package/dist/examples/deposit-redeem-loop.js +67 -0
  17. package/dist/examples/deposit-redeem-loop.js.map +1 -0
  18. package/dist/examples/launch-deposit-redeem.d.ts +2 -0
  19. package/dist/examples/launch-deposit-redeem.d.ts.map +1 -0
  20. package/dist/examples/launch-deposit-redeem.js +166 -0
  21. package/dist/examples/launch-deposit-redeem.js.map +1 -0
  22. package/dist/examples/list-and-status.d.ts +2 -0
  23. package/dist/examples/list-and-status.d.ts.map +1 -0
  24. package/dist/examples/list-and-status.js +67 -0
  25. package/dist/examples/list-and-status.js.map +1 -0
  26. package/dist/manager-flow.d.ts +73 -0
  27. package/dist/manager-flow.d.ts.map +1 -0
  28. package/dist/manager-flow.js +110 -0
  29. package/dist/manager-flow.js.map +1 -0
  30. package/dist/mcp-server.d.ts +3 -0
  31. package/dist/mcp-server.d.ts.map +1 -0
  32. package/dist/mcp-server.js +529 -0
  33. package/dist/mcp-server.js.map +1 -0
  34. package/dist/recovery-flow.d.ts +81 -0
  35. package/dist/recovery-flow.d.ts.map +1 -0
  36. package/dist/recovery-flow.js +123 -0
  37. package/dist/recovery-flow.js.map +1 -0
  38. package/dist/session.d.ts +52 -0
  39. package/dist/session.d.ts.map +1 -0
  40. package/dist/session.js +107 -0
  41. package/dist/session.js.map +1 -0
  42. package/dist/signing.d.ts +30 -0
  43. package/dist/signing.d.ts.map +1 -0
  44. package/dist/signing.js +129 -0
  45. package/dist/signing.js.map +1 -0
  46. package/dist/submit.d.ts +31 -0
  47. package/dist/submit.d.ts.map +1 -0
  48. package/dist/submit.js +165 -0
  49. package/dist/submit.js.map +1 -0
  50. package/dist/types.d.ts +191 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +16 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/zap-v2-flow.d.ts +76 -0
  55. package/dist/zap-v2-flow.d.ts.map +1 -0
  56. package/dist/zap-v2-flow.js +176 -0
  57. package/dist/zap-v2-flow.js.map +1 -0
  58. package/package.json +60 -0
  59. package/skills.md +153 -0
@@ -0,0 +1,215 @@
1
+ const BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
2
+ function requireBase58(addr, label) {
3
+ if (!BASE58_RE.test(addr)) {
4
+ throw new Error(`Invalid base58 ${label}: ${addr}`);
5
+ }
6
+ }
7
+ export class DfmApiClient {
8
+ session;
9
+ baseUrl;
10
+ constructor(config, session) {
11
+ this.session = session;
12
+ this.baseUrl = config.apiUrl;
13
+ }
14
+ // ─── low-level request helpers ─────────────────────────────────────────
15
+ async publicRequest(path, init) {
16
+ const res = await fetch(`${this.baseUrl}${path}`, {
17
+ ...init,
18
+ headers: { 'Content-Type': 'application/json', ...(init?.headers ?? {}) },
19
+ });
20
+ if (!res.ok) {
21
+ const body = await res.text().catch(() => '');
22
+ throw new Error(`DFM API ${res.status} on ${path}: ${body.slice(0, 400)}`);
23
+ }
24
+ return (await res.json());
25
+ }
26
+ async authRequest(path, init) {
27
+ const send = async (token) => fetch(`${this.baseUrl}${path}`, {
28
+ ...init,
29
+ headers: {
30
+ 'Content-Type': 'application/json',
31
+ Authorization: `Bearer ${token}`,
32
+ ...(init?.headers ?? {}),
33
+ },
34
+ });
35
+ let token = await this.session.getAccessToken();
36
+ let res = await send(token);
37
+ // One transparent re-auth on token expiry/rotation.
38
+ if (res.status === 401) {
39
+ token = await this.session.getAccessToken(true);
40
+ res = await send(token);
41
+ }
42
+ if (!res.ok) {
43
+ const body = await res.text().catch(() => '');
44
+ throw new Error(`DFM API ${res.status} on ${path}: ${body.slice(0, 400)}`);
45
+ }
46
+ // 204 No Content → undefined
47
+ if (res.status === 204)
48
+ return undefined;
49
+ return (await res.json());
50
+ }
51
+ // ─── READ (safe) ───────────────────────────────────────────────────────
52
+ /** `GET /vaults` (public). Supports the API's documented query params. */
53
+ async listVaults(params) {
54
+ const q = new URLSearchParams();
55
+ if (params?.category)
56
+ q.set('category', params.category);
57
+ if (params?.dtfType)
58
+ q.set('dtfType', params.dtfType);
59
+ if (params?.status)
60
+ q.set('status', params.status);
61
+ if (params?.sortBy)
62
+ q.set('sortBy', params.sortBy);
63
+ if (params?.page)
64
+ q.set('page', String(params.page));
65
+ if (params?.limit)
66
+ q.set('limit', String(params.limit));
67
+ const qs = q.toString();
68
+ return this.publicRequest(`/vaults${qs ? `?${qs}` : ''}`);
69
+ }
70
+ /** `GET /vaults/:address` (public) — wraps the doc under `{ vault, ... }`. */
71
+ async getVault(address) {
72
+ requireBase58(address, 'vault address');
73
+ return this.publicRequest(`/vaults/${address}`);
74
+ }
75
+ /** `GET /access/status?wallet=` (public) — closed-alpha gate decision. */
76
+ async getAccessStatus(wallet) {
77
+ requireBase58(wallet, 'wallet');
78
+ return this.publicRequest(`/access/status?wallet=${wallet}`);
79
+ }
80
+ /**
81
+ * `GET /vaults/:address/zap-v2/status/:user` (auth) — decoded ZapEscrowV2 or
82
+ * null. The session wallet must equal `user` (or be the vault admin), else 403.
83
+ * Returns `null` when the user has no open escrow.
84
+ */
85
+ async getZapV2Status(vaultAddress, user) {
86
+ requireBase58(vaultAddress, 'vault address');
87
+ requireBase58(user, 'user wallet');
88
+ return this.authRequest(`/vaults/${vaultAddress}/zap-v2/status/${user}`);
89
+ }
90
+ /**
91
+ * `GET /portfolio` (auth) — the API reads the wallet from the JWT; there is NO
92
+ * portfolio-by-arbitrary-address endpoint. So this returns the SESSION
93
+ * wallet's positions. (To inspect another wallet you'd need that wallet's
94
+ * keypair / session.)
95
+ */
96
+ async getPortfolioPositions() {
97
+ return this.authRequest('/portfolio');
98
+ }
99
+ /** `GET /portfolio/summary` (auth) — total value + PnL for the session wallet. */
100
+ async getPortfolioSummary() {
101
+ return this.authRequest('/portfolio/summary');
102
+ }
103
+ // ─── WRITE (zap-v2 prepare endpoints) ──────────────────────────────────
104
+ // These return UNSIGNED transactions. They do not move funds by themselves —
105
+ // the agent must sign locally + submit. The MCP write tools gate these behind
106
+ // DFM_AGENT_WRITE_ENABLED. The prepare calls themselves are auth-gated and
107
+ // rate-limited (tx_prepare tier) server-side.
108
+ // REVIEW: real-money path — parent must review before enabling.
109
+ /** `POST /vaults/:address/zap-in-v2/open/prepare` — pull USDC, pin plan. */
110
+ async prepareZapInOpenV2(vaultAddress, usdcAmountRaw, opts) {
111
+ requireBase58(vaultAddress, 'vault address');
112
+ return this.authRequest(`/vaults/${vaultAddress}/zap-in-v2/open/prepare`, { method: 'POST', body: JSON.stringify({ usdcAmount: usdcAmountRaw, ...opts }) });
113
+ }
114
+ /** `POST /vaults/:address/zap-in-v2/close/prepare` — CPI mint_in_kind_v2. */
115
+ async prepareZapInCloseV2(vaultAddress) {
116
+ requireBase58(vaultAddress, 'vault address');
117
+ return this.authRequest(`/vaults/${vaultAddress}/zap-in-v2/close/prepare`, { method: 'POST', body: JSON.stringify({}) });
118
+ }
119
+ /** `POST /vaults/:address/zap-out-v2/open/prepare` — burn shares → escrow. */
120
+ async prepareZapOutOpenV2(vaultAddress, sharesRaw, opts) {
121
+ requireBase58(vaultAddress, 'vault address');
122
+ return this.authRequest(`/vaults/${vaultAddress}/zap-out-v2/open/prepare`, { method: 'POST', body: JSON.stringify({ shares: sharesRaw, ...opts }) });
123
+ }
124
+ /** `POST /vaults/:address/zap-out-v2/close/prepare` — exit fee, pay net USDC. */
125
+ async prepareZapOutCloseV2(vaultAddress) {
126
+ requireBase58(vaultAddress, 'vault address');
127
+ return this.authRequest(`/vaults/${vaultAddress}/zap-out-v2/close/prepare`, { method: 'POST', body: JSON.stringify({}) });
128
+ }
129
+ /**
130
+ * `POST /vaults/:address/zap-v2/cancel/prepare` — return the escrow's current
131
+ * USDC + per-asset balances to the test wallet as-is (no swaps, no oracle),
132
+ * then close it. 404 server-side when no escrow is open. Single v0 tx.
133
+ */
134
+ async prepareZapCancelV2(vaultAddress) {
135
+ requireBase58(vaultAddress, 'vault address');
136
+ return this.authRequest(`/vaults/${vaultAddress}/zap-v2/cancel/prepare`, { method: 'POST', body: JSON.stringify({}) });
137
+ }
138
+ /**
139
+ * `POST /vaults/:address/zap-v2/update-envelope/prepare` — LOOSEN the pinned
140
+ * per-leg floors + slippage (and optionally extend expiry) so a stalled escrow
141
+ * can complete. Monotonic loosening is enforced on-chain; the API rejects any
142
+ * non-positive `newMinOuts`. Single v0 tx.
143
+ */
144
+ async prepareUpdateEnvelopeV2(vaultAddress, body) {
145
+ requireBase58(vaultAddress, 'vault address');
146
+ return this.authRequest(`/vaults/${vaultAddress}/zap-v2/update-envelope/prepare`, { method: 'POST', body: JSON.stringify(body) });
147
+ }
148
+ // ─── WRITE (create / launch a DTF) ─────────────────────────────────────
149
+ // The create flow is prepare → sign locally → submit → confirm. Mirrors the
150
+ // zap-v2 write gating (DFM_AGENT_WRITE_ENABLED). The prepare is auth-gated +
151
+ // rate-limited (tx_prepare tier) server-side; the authed JWT wallet is the
152
+ // vault creator/admin (body-supplied wallet is ignored by the API).
153
+ // REVIEW: real-money path — parent must review before enabling.
154
+ /**
155
+ * `POST /vaults/create/prepare` — build the on-chain `create_vault` tx. The
156
+ * creator is the SESSION wallet (from the JWT), not a body field. Returns a
157
+ * single unsigned v0 tx + the `vaultAddress` PDA + a server-built ALT.
158
+ */
159
+ async prepareCreateVault(params) {
160
+ return this.authRequest('/vaults/create/prepare', {
161
+ method: 'POST',
162
+ body: JSON.stringify(params),
163
+ });
164
+ }
165
+ /**
166
+ * `POST /vaults/create/confirm` — record the vault doc after the create tx
167
+ * confirms. Only `signature` + `vaultAddress` are authoritative; the API reads
168
+ * the economic params back from its prepare-cache (so they can't be tampered
169
+ * post-prepare). MUST be called inside the cache TTL (~15 min).
170
+ */
171
+ async confirmCreateVault(signature, vaultAddress) {
172
+ requireBase58(vaultAddress, 'vault address');
173
+ return this.authRequest('/vaults/create/confirm', {
174
+ method: 'POST',
175
+ body: JSON.stringify({ signature, vaultAddress }),
176
+ });
177
+ }
178
+ // ─── WRITE (vault MANAGER actions: rebalance / fees / transfer-admin) ───
179
+ // Each requires the signed-in wallet to be the vault ADMIN (API-enforced).
180
+ // Same prepare → sign → submit pattern. REVIEW: control path — parent reviews.
181
+ /**
182
+ * `POST /vaults/:address/assets/prepare` (vault admin) — build an
183
+ * `update_vault_assets` FULL-REPLACE basket tx. The API enforces the same
184
+ * guards as the chain (manual mode, 24h timelock, Σ bps == 10000, no dupes,
185
+ * ≤15 on mainnet) → clean 4xx instead of a chain revert.
186
+ */
187
+ async prepareUpdateAssets(vaultAddress, assets) {
188
+ requireBase58(vaultAddress, 'vault address');
189
+ return this.authRequest(`/vaults/${vaultAddress}/assets/prepare`, { method: 'POST', body: JSON.stringify({ assets }) });
190
+ }
191
+ /** `POST /vaults/:address/assets/confirm` — record the basket change post-broadcast. */
192
+ async confirmUpdateAssets(vaultAddress, signature) {
193
+ requireBase58(vaultAddress, 'vault address');
194
+ return this.authRequest(`/vaults/${vaultAddress}/assets/confirm`, { method: 'POST', body: JSON.stringify({ signature }) });
195
+ }
196
+ /**
197
+ * `PATCH /vaults/:address/fees` (vault admin) — build an `update_management_fee`
198
+ * tx (bps ≤2000). Reverts on a fees-immutable vault.
199
+ */
200
+ async prepareUpdateManagementFee(vaultAddress, managementFeeBps) {
201
+ requireBase58(vaultAddress, 'vault address');
202
+ return this.authRequest(`/vaults/${vaultAddress}/fees`, { method: 'PATCH', body: JSON.stringify({ managementFeeBps }) });
203
+ }
204
+ /**
205
+ * `POST /vaults/:address/transfer-admin` (vault admin) — initiate transferring
206
+ * vault admin to `newAdmin` (the new admin must ACCEPT separately). HIGH
207
+ * sensitivity — gates the agent's `transfer_admin` tool.
208
+ */
209
+ async prepareTransferAdmin(vaultAddress, newAdmin) {
210
+ requireBase58(vaultAddress, 'vault address');
211
+ requireBase58(newAdmin, 'new admin wallet');
212
+ return this.authRequest(`/vaults/${vaultAddress}/transfer-admin`, { method: 'POST', body: JSON.stringify({ newAdmin }) });
213
+ }
214
+ }
215
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Agent configuration — loaded from env vars set by the OPERATOR locally.
3
+ *
4
+ * SECURITY MODEL (read before changing anything here):
5
+ * - The agent operates with ITS OWN test-wallet keypair, loaded LOCALLY by the
6
+ * operator from a file path (`DFM_AGENT_KEYPAIR_PATH`) or an env-injected
7
+ * secret (`DFM_AGENT_KEYPAIR_JSON`). The keypair is NEVER passed through the
8
+ * MCP protocol, tool arguments, or tool results, and is NEVER logged.
9
+ * - That test wallet must be on the closed-alpha access allowlist (a sign-in
10
+ * from a non-allowed wallet is rejected server-side — see auth.service).
11
+ * - The protocol wallet (`9GjE…`) must NOT be used here. The agent signs with
12
+ * a throwaway test wallet funded with a few dollars of USDC.
13
+ * - All real-money WRITE tools (deposit / redeem) are GATED behind
14
+ * `DFM_AGENT_WRITE_ENABLED=true` and default OFF.
15
+ */
16
+ export interface AgentConfig {
17
+ /** DFM v2 backend API base, INCLUDING the `/api/v2` prefix. */
18
+ apiUrl: string;
19
+ /** Solana cluster (devnet | mainnet-beta). Mainnet is the live system. */
20
+ cluster: 'devnet' | 'mainnet-beta';
21
+ /**
22
+ * Helius (or other dedicated) RPC endpoint. Used only by the WRITE path to
23
+ * submit signed transactions. The read tools do not need it.
24
+ */
25
+ rpcUrl?: string;
26
+ /**
27
+ * SIWS domain the API binds sign-in messages to (e.g. `n2.dfm.finance`).
28
+ * Informational only — the agent signs the EXACT message the API returns from
29
+ * `POST /auth/nonce`, so it does not need to reconstruct the domain. Surfaced
30
+ * for diagnostics / sanity-logging.
31
+ */
32
+ siwsDomain?: string;
33
+ /**
34
+ * Where to load the test-wallet keypair from. Exactly ONE of these should be
35
+ * set by the operator when WRITE is enabled (path preferred). Format: the
36
+ * standard Solana 64-byte secret-key JSON array (e.g. `solana-keygen` output).
37
+ * Read lazily + only inside the signing module — never eagerly, never logged.
38
+ */
39
+ keypairPath?: string;
40
+ keypairJsonEnvPresent: boolean;
41
+ /**
42
+ * Master kill-switch for the real-money WRITE tools (deposit/redeem). Default
43
+ * OFF. When false, the write tools refuse with a clear message and never load
44
+ * the keypair or touch the chain.
45
+ */
46
+ writeEnabled: boolean;
47
+ }
48
+ /**
49
+ * Normalize the API base so it always ends with the `/api/v2` global prefix and
50
+ * has no trailing slash. Accepts either a bare origin
51
+ * (`https://n2-api.dfm.finance`) or a full base
52
+ * (`https://n2-api.dfm.finance/api/v2`).
53
+ */
54
+ export declare function normalizeApiUrl(raw: string): string;
55
+ export declare function loadConfig(): AgentConfig;
56
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,OAAO,EAAE,QAAQ,GAAG,cAAc,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC;CACvB;AAMD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,UAAU,IAAI,WAAW,CAyBxC"}
package/dist/config.js ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Agent configuration — loaded from env vars set by the OPERATOR locally.
3
+ *
4
+ * SECURITY MODEL (read before changing anything here):
5
+ * - The agent operates with ITS OWN test-wallet keypair, loaded LOCALLY by the
6
+ * operator from a file path (`DFM_AGENT_KEYPAIR_PATH`) or an env-injected
7
+ * secret (`DFM_AGENT_KEYPAIR_JSON`). The keypair is NEVER passed through the
8
+ * MCP protocol, tool arguments, or tool results, and is NEVER logged.
9
+ * - That test wallet must be on the closed-alpha access allowlist (a sign-in
10
+ * from a non-allowed wallet is rejected server-side — see auth.service).
11
+ * - The protocol wallet (`9GjE…`) must NOT be used here. The agent signs with
12
+ * a throwaway test wallet funded with a few dollars of USDC.
13
+ * - All real-money WRITE tools (deposit / redeem) are GATED behind
14
+ * `DFM_AGENT_WRITE_ENABLED=true` and default OFF.
15
+ */
16
+ function parseCluster(raw) {
17
+ return raw === 'mainnet-beta' ? 'mainnet-beta' : raw === 'devnet' ? 'devnet' : 'mainnet-beta';
18
+ }
19
+ /**
20
+ * Normalize the API base so it always ends with the `/api/v2` global prefix and
21
+ * has no trailing slash. Accepts either a bare origin
22
+ * (`https://n2-api.dfm.finance`) or a full base
23
+ * (`https://n2-api.dfm.finance/api/v2`).
24
+ */
25
+ export function normalizeApiUrl(raw) {
26
+ let url = raw.trim().replace(/\/+$/, '');
27
+ if (!/\/api\/v2$/.test(url)) {
28
+ url = `${url}/api/v2`;
29
+ }
30
+ return url;
31
+ }
32
+ export function loadConfig() {
33
+ // Default to the live closed-mainnet API so the read tools work out of the box.
34
+ const apiUrl = normalizeApiUrl(process.env.DFM_API_URL ?? 'https://n2-api.dfm.finance');
35
+ const cluster = parseCluster(process.env.SOLANA_CLUSTER);
36
+ // RPC is optional (only the WRITE path uses it). On mainnet, refuse the
37
+ // rate-limited public fallback so a misconfigured write agent fails loudly
38
+ // instead of silently throttling through api.mainnet-beta.solana.com.
39
+ let rpcUrl = process.env.HELIUS_RPC_URL ?? process.env.DFM_RPC_URL;
40
+ if (!rpcUrl && cluster === 'devnet') {
41
+ rpcUrl = 'https://api.devnet.solana.com';
42
+ }
43
+ const writeEnabled = process.env.DFM_AGENT_WRITE_ENABLED === 'true';
44
+ return {
45
+ apiUrl,
46
+ cluster,
47
+ rpcUrl,
48
+ siwsDomain: process.env.SIWS_DOMAIN,
49
+ keypairPath: process.env.DFM_AGENT_KEYPAIR_PATH,
50
+ keypairJsonEnvPresent: typeof process.env.DFM_AGENT_KEYPAIR_JSON === 'string'
51
+ && process.env.DFM_AGENT_KEYPAIR_JSON.trim().length > 0,
52
+ writeEnabled,
53
+ };
54
+ }
55
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAmCH,SAAS,YAAY,CAAC,GAAuB;IAC3C,OAAO,GAAG,KAAK,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC;AAChG,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,GAAG,GAAG,GAAG,GAAG,SAAS,CAAC;IACxB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,gFAAgF;IAChF,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,4BAA4B,CAAC,CAAC;IACxF,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEzD,wEAAwE;IACxE,2EAA2E;IAC3E,sEAAsE;IACtE,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IACnE,IAAI,CAAC,MAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,GAAG,+BAA+B,CAAC;IAC3C,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,MAAM,CAAC;IAEpE,OAAO;QACL,MAAM;QACN,OAAO;QACP,MAAM;QACN,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;QACnC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB;QAC/C,qBAAqB,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,QAAQ;eACxE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QACzD,YAAY;KACb,CAAC;AACJ,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Vault LAUNCH (create DTF) lifecycle driver — a real-money WRITE path.
3
+ *
4
+ * ⚠️ GATED. Reachable only when DFM_AGENT_WRITE_ENABLED=true. Creating a vault
5
+ * pulls the on-chain creation fee (USDC) from the test wallet and writes a vault
6
+ * the operator then owns/administers. The parent must review before enabling.
7
+ * REVIEW: real-money path — parent must review before enabling.
8
+ *
9
+ * ─── Flow (prepare → sign → submit → confirm) ──────────────────────────────
10
+ * 1. POST /vaults/create/prepare → ONE unsigned v0 tx + the vaultAddress PDA
11
+ * + a server-built per-create ALT. The creator is the SESSION wallet.
12
+ * 2. sign the tx LOCALLY with the test keypair.
13
+ * 3. submit + confirm via RPC.
14
+ * 4. POST /vaults/create/confirm { signature, vaultAddress } → records the
15
+ * vault doc (reads economic params from the prepare-cache, so they cannot
16
+ * be tampered after prepare). MUST land inside the ~15-min cache TTL.
17
+ *
18
+ * If confirm fails AFTER the tx confirmed on-chain, the vault EXISTS on-chain
19
+ * but has no Mongo doc ("ghost vault"). We surface the signature + vaultAddress
20
+ * so the operator can retry confirm (within TTL) or reconcile — we never hide it.
21
+ * ───────────────────────────────────────────────────────────────────────────
22
+ */
23
+ import type { AgentConfig } from './config.js';
24
+ import type { DfmApiClient } from './api-client.js';
25
+ import type { CreateAssetInput } from './types.js';
26
+ export interface LaunchVaultParams {
27
+ name: string;
28
+ symbol: string;
29
+ assets: CreateAssetInput[];
30
+ /** DTF type — defaults to 'index' (the only live type). */
31
+ dtfType?: string;
32
+ /** Category enum (0-7, 255). Defaults to 255 (uncategorized/other). */
33
+ category?: number;
34
+ managementFeeBps?: number;
35
+ exitFeeBps?: number;
36
+ description?: string;
37
+ metadataUri?: string;
38
+ }
39
+ export interface LaunchVaultResult {
40
+ ok: boolean;
41
+ vault: string;
42
+ signature: string;
43
+ vaultMint: string;
44
+ vaultIndex: number;
45
+ /** Set when /create/confirm failed after the tx confirmed (ghost-vault risk). */
46
+ confirmError?: string;
47
+ message: string;
48
+ }
49
+ /**
50
+ * Validate the basket client-side BEFORE hitting the network — fast, clear
51
+ * failures the agent can fix without a round-trip. (The API re-validates
52
+ * everything; this is a courtesy layer.)
53
+ */
54
+ export declare function validateLaunchParams(params: LaunchVaultParams): void;
55
+ /**
56
+ * Drive a full vault LAUNCH (create DTF). Returns when the vault doc is recorded
57
+ * (confirm succeeded) — or, if confirm fails after the on-chain tx confirmed,
58
+ * returns `ok:false` WITH the signature + vaultAddress so the operator can
59
+ * recover (the on-chain vault exists either way).
60
+ */
61
+ export declare function runLaunchVault(config: AgentConfig, api: DfmApiClient, params: LaunchVaultParams): Promise<LaunchVaultResult>;
62
+ //# sourceMappingURL=create-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-flow.d.ts","sourceRoot":"","sources":["../src/create-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CA4BpE;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAyD5B"}
@@ -0,0 +1,88 @@
1
+ import { signBase64Transaction } from './signing.js';
2
+ import { submitSignedTx } from './submit.js';
3
+ const MAX_ASSETS = 15; // mainnet basket cap (zap-v2 OPEN tx ceiling)
4
+ /**
5
+ * Validate the basket client-side BEFORE hitting the network — fast, clear
6
+ * failures the agent can fix without a round-trip. (The API re-validates
7
+ * everything; this is a courtesy layer.)
8
+ */
9
+ export function validateLaunchParams(params) {
10
+ if (!params.assets || params.assets.length === 0) {
11
+ throw new Error('launch_vault needs at least 1 basket asset.');
12
+ }
13
+ if (params.assets.length > MAX_ASSETS) {
14
+ throw new Error(`Basket has ${params.assets.length} assets — the mainnet cap is ${MAX_ASSETS} ` +
15
+ `(the zap-v2 OPEN tx hits the 1232-byte v0 limit beyond that).`);
16
+ }
17
+ const sum = params.assets.reduce((acc, a) => acc + a.mintBps, 0);
18
+ if (sum !== 10_000) {
19
+ throw new Error(`Basket allocations must sum to exactly 10000 bps; got ${sum}. ` +
20
+ `(${params.assets.map((a) => `${a.symbol}=${a.mintBps}`).join(', ')})`);
21
+ }
22
+ for (const a of params.assets) {
23
+ if (!Number.isInteger(a.mintBps) || a.mintBps <= 0 || a.mintBps > 10_000) {
24
+ throw new Error(`Asset ${a.symbol} has invalid mintBps ${a.mintBps} (must be 1..10000).`);
25
+ }
26
+ }
27
+ if ((params.managementFeeBps ?? 0) > 2_000) {
28
+ throw new Error('managementFeeBps must be ≤ 2000 (20%/yr).');
29
+ }
30
+ if ((params.exitFeeBps ?? 0) > 1_000) {
31
+ throw new Error('exitFeeBps must be ≤ 1000 (10%).');
32
+ }
33
+ }
34
+ /**
35
+ * Drive a full vault LAUNCH (create DTF). Returns when the vault doc is recorded
36
+ * (confirm succeeded) — or, if confirm fails after the on-chain tx confirmed,
37
+ * returns `ok:false` WITH the signature + vaultAddress so the operator can
38
+ * recover (the on-chain vault exists either way).
39
+ */
40
+ export async function runLaunchVault(config, api, params) {
41
+ validateLaunchParams(params);
42
+ // 1. PREPARE — build the create tx. Entry fee is forced to 0 (mainnet rule).
43
+ const prepared = await api.prepareCreateVault({
44
+ name: params.name,
45
+ symbol: params.symbol,
46
+ dtfType: params.dtfType ?? 'index',
47
+ category: params.category ?? 255,
48
+ assets: params.assets,
49
+ entryFeeBps: 0,
50
+ exitFeeBps: params.exitFeeBps ?? 0,
51
+ managementFeeBps: params.managementFeeBps ?? 0,
52
+ description: params.description,
53
+ metadataUri: params.metadataUri,
54
+ });
55
+ // 2. SIGN locally + 3. SUBMIT.
56
+ const signed = signBase64Transaction(config, prepared.transaction);
57
+ const signature = await submitSignedTx(config, signed, prepared.blockhash, prepared.lastValidBlockHeight);
58
+ // 4. CONFIRM — record the vault doc. If this fails the on-chain vault already
59
+ // exists (the tx confirmed in step 3), so we DON'T throw it away — we return
60
+ // the recovery info. The operator can retry confirm inside the cache TTL.
61
+ try {
62
+ await api.confirmCreateVault(signature, prepared.vaultAddress);
63
+ }
64
+ catch (e) {
65
+ return {
66
+ ok: false,
67
+ vault: prepared.vaultAddress,
68
+ signature,
69
+ vaultMint: prepared.vaultMint,
70
+ vaultIndex: prepared.vaultIndex,
71
+ confirmError: e.message,
72
+ message: `Create tx CONFIRMED on-chain (vault ${prepared.vaultAddress}) but /create/confirm ` +
73
+ `failed: ${e.message}. The on-chain vault EXISTS. Retry confirm within the ` +
74
+ `~15-min prepare-cache TTL (POST /vaults/create/confirm { signature, vaultAddress }), ` +
75
+ `or it will need a chain reconcile.`,
76
+ };
77
+ }
78
+ return {
79
+ ok: true,
80
+ vault: prepared.vaultAddress,
81
+ signature,
82
+ vaultMint: prepared.vaultMint,
83
+ vaultIndex: prepared.vaultIndex,
84
+ message: `Vault "${params.name}" (${params.symbol}) launched: ${prepared.vaultAddress} ` +
85
+ `(index ${prepared.vaultIndex}). Recorded. Fund the first deposit ≥ ~$12 to clear the $10 floor.`,
86
+ };
87
+ }
88
+ //# sourceMappingURL=create-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-flow.js","sourceRoot":"","sources":["../src/create-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=deposit-redeem-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deposit-redeem-loop.d.ts","sourceRoot":"","sources":["../../src/examples/deposit-redeem-loop.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Example (REAL-MONEY — GATED): the full deposit → redeem loop a tester would
3
+ * run programmatically. Deposits a small USDC amount into a vault, waits for the
4
+ * orchestrator to mint shares, reads the position, then redeems it back to USDC.
5
+ *
6
+ * ⚠️ This MOVES REAL FUNDS on mainnet. It refuses unless DFM_AGENT_WRITE_ENABLED
7
+ * is set, an RPC is configured, and a funded, allowlisted test keypair is loaded.
8
+ * REVIEW: real-money path — parent must review before relying on this.
9
+ *
10
+ * Usage:
11
+ * DFM_API_URL=https://n2-api.dfm.finance \
12
+ * HELIUS_RPC_URL=https://mainnet.helius-rpc.com/?api-key=… \
13
+ * DFM_AGENT_KEYPAIR_PATH=/abs/path/test-wallet.json \
14
+ * DFM_AGENT_WRITE_ENABLED=true \
15
+ * npx tsx src/examples/deposit-redeem-loop.ts <vaultAddress> [usdcRaw=12000000]
16
+ */
17
+ import { loadConfig } from '../config.js';
18
+ import { SiwsSession } from '../session.js';
19
+ import { DfmApiClient } from '../api-client.js';
20
+ import { runDeposit, runRedeem } from '../zap-v2-flow.js';
21
+ async function main() {
22
+ const vault = process.argv[2];
23
+ const usdcRaw = process.argv[3] ?? '12000000'; // $12 default (clears the $10 first-mint floor)
24
+ if (!vault) {
25
+ console.error('Usage: npx tsx deposit-redeem-loop.ts <vaultAddress> [usdcRaw]');
26
+ process.exit(1);
27
+ }
28
+ const config = loadConfig();
29
+ if (!config.writeEnabled) {
30
+ console.error('Refusing: DFM_AGENT_WRITE_ENABLED is not "true". This is a real-money loop.');
31
+ process.exit(1);
32
+ }
33
+ if (!config.rpcUrl) {
34
+ console.error('Refusing: no RPC. Set HELIUS_RPC_URL to submit transactions.');
35
+ process.exit(1);
36
+ }
37
+ const session = new SiwsSession(config);
38
+ const api = new DfmApiClient(config, session);
39
+ const user = session.walletAddress;
40
+ console.log(`Test wallet: ${user} | vault: ${vault} | deposit: ${usdcRaw} raw USDC`);
41
+ // 1. Deposit.
42
+ console.log('\n→ DEPOSIT (open → orchestrator cranks → close)…');
43
+ const dep = await runDeposit(config, api, user, vault, usdcRaw);
44
+ console.log(JSON.stringify(dep, null, 2));
45
+ // 2. Read the resulting share balance.
46
+ console.log('\n→ position after deposit…');
47
+ const positions = await api.getPortfolioPositions();
48
+ console.log(JSON.stringify(positions, null, 2));
49
+ // 3. Redeem the freshly minted shares. The exact share amount comes from the
50
+ // on-chain position — a real harness would read it from `positions` above.
51
+ // Here we leave redemption sizing to the operator: pass the share raw amount
52
+ // you want to redeem as an env to avoid accidentally over/under-redeeming.
53
+ const sharesRaw = process.env.DFM_REDEEM_SHARES_RAW;
54
+ if (!sharesRaw) {
55
+ console.log('\nSet DFM_REDEEM_SHARES_RAW to the raw 6dp share amount to redeem ' +
56
+ '(read it from the position above), then re-run the redeem leg.');
57
+ return;
58
+ }
59
+ console.log(`\n→ REDEEM ${sharesRaw} raw shares (open → orchestrator cranks → close)…`);
60
+ const red = await runRedeem(config, api, user, vault, sharesRaw);
61
+ console.log(JSON.stringify(red, null, 2));
62
+ }
63
+ main().catch((e) => {
64
+ console.error(e);
65
+ process.exit(1);
66
+ });
67
+ //# sourceMappingURL=deposit-redeem-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deposit-redeem-loop.js","sourceRoot":"","sources":["../../src/examples/deposit-redeem-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=launch-deposit-redeem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-deposit-redeem.d.ts","sourceRoot":"","sources":["../../src/examples/launch-deposit-redeem.ts"],"names":[],"mappings":""}