@cloakedagent/sdk 0.1.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 (72) hide show
  1. package/README.md +90 -0
  2. package/dist/agent.d.ts +321 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +877 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/config.d.ts +33 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +64 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/constants.d.ts +4 -0
  11. package/dist/constants.d.ts.map +1 -0
  12. package/dist/constants.js +9 -0
  13. package/dist/constants.js.map +1 -0
  14. package/dist/idl.json +1347 -0
  15. package/dist/index.d.ts +10 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +66 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/mcp/index.d.ts +7 -0
  20. package/dist/mcp/index.d.ts.map +1 -0
  21. package/dist/mcp/index.js +374 -0
  22. package/dist/mcp/index.js.map +1 -0
  23. package/dist/mcp/tools.d.ts +26 -0
  24. package/dist/mcp/tools.d.ts.map +1 -0
  25. package/dist/mcp/tools.js +320 -0
  26. package/dist/mcp/tools.js.map +1 -0
  27. package/dist/mcp/types.d.ts +61 -0
  28. package/dist/mcp/types.d.ts.map +1 -0
  29. package/dist/mcp/types.js +4 -0
  30. package/dist/mcp/types.js.map +1 -0
  31. package/dist/relayer.d.ts +130 -0
  32. package/dist/relayer.d.ts.map +1 -0
  33. package/dist/relayer.js +225 -0
  34. package/dist/relayer.js.map +1 -0
  35. package/dist/signer.d.ts +18 -0
  36. package/dist/signer.d.ts.map +1 -0
  37. package/dist/signer.js +34 -0
  38. package/dist/signer.js.map +1 -0
  39. package/dist/token.d.ts +320 -0
  40. package/dist/token.d.ts.map +1 -0
  41. package/dist/token.js +896 -0
  42. package/dist/token.js.map +1 -0
  43. package/dist/types.d.ts +66 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +3 -0
  46. package/dist/types.js.map +1 -0
  47. package/dist/zk/browser-prover.d.ts +85 -0
  48. package/dist/zk/browser-prover.d.ts.map +1 -0
  49. package/dist/zk/browser-prover.js +260 -0
  50. package/dist/zk/browser-prover.js.map +1 -0
  51. package/dist/zk/discovery.d.ts +65 -0
  52. package/dist/zk/discovery.d.ts.map +1 -0
  53. package/dist/zk/discovery.js +143 -0
  54. package/dist/zk/discovery.js.map +1 -0
  55. package/dist/zk/index.d.ts +14 -0
  56. package/dist/zk/index.d.ts.map +1 -0
  57. package/dist/zk/index.js +47 -0
  58. package/dist/zk/index.js.map +1 -0
  59. package/dist/zk/ownership_proof.json +1 -0
  60. package/dist/zk/poseidon.d.ts +31 -0
  61. package/dist/zk/poseidon.d.ts.map +1 -0
  62. package/dist/zk/poseidon.js +103 -0
  63. package/dist/zk/poseidon.js.map +1 -0
  64. package/dist/zk/prover.d.ts +49 -0
  65. package/dist/zk/prover.d.ts.map +1 -0
  66. package/dist/zk/prover.js +120 -0
  67. package/dist/zk/prover.js.map +1 -0
  68. package/dist/zk/secrets.d.ts +62 -0
  69. package/dist/zk/secrets.d.ts.map +1 -0
  70. package/dist/zk/secrets.js +98 -0
  71. package/dist/zk/secrets.js.map +1 -0
  72. package/package.json +74 -0
package/dist/agent.js ADDED
@@ -0,0 +1,877 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CloakedAgent = void 0;
7
+ const web3_js_1 = require("@solana/web3.js");
8
+ const anchor_1 = require("@coral-xyz/anchor");
9
+ const bs58_1 = __importDefault(require("bs58"));
10
+ const constants_1 = require("./constants");
11
+ const idl_json_1 = __importDefault(require("./idl.json"));
12
+ const zk_1 = require("./zk");
13
+ const relayer_1 = require("./relayer");
14
+ // Seconds in a day for daily limit calculations
15
+ const SECONDS_PER_DAY = 86400;
16
+ /**
17
+ * CloakedAgent - Represents a Cloaked Agent on Solana
18
+ *
19
+ * A Cloaked Agent is a Solana keypair where:
20
+ * - The private key is the "Agent Key" (what users save)
21
+ * - The derived PDA holds the SOL balance
22
+ * - Signing with the keypair authorizes spending
23
+ *
24
+ * Two modes of operation:
25
+ * - Agent mode (via constructor): Has Agent Key, can spend
26
+ * - Owner mode (via forOwner): No Agent Key, can manage (freeze/unfreeze/update/close)
27
+ */
28
+ class CloakedAgent {
29
+ /**
30
+ * Create a CloakedAgent from an Agent Key (agent mode - can spend)
31
+ * @param agentKey - Base58 encoded secret key
32
+ * @param rpcUrl - Solana RPC endpoint URL
33
+ */
34
+ constructor(agentKey, rpcUrl) {
35
+ // Private mode fields
36
+ this._ownerCommitment = null;
37
+ this._agentSecret = null;
38
+ this._nonce = null;
39
+ // Cached PDAs (set by forPrivateOwner to avoid getter issues)
40
+ this._agentStatePda = null;
41
+ this._vaultPda = null;
42
+ const secretKey = bs58_1.default.decode(agentKey);
43
+ this.keypair = web3_js_1.Keypair.fromSecretKey(secretKey);
44
+ this.delegatePubkey = this.keypair.publicKey;
45
+ this.connection = new web3_js_1.Connection(rpcUrl, "confirmed");
46
+ // Derive the legacy PDA (seeds = ["token", delegate]) - kept for backward compatibility
47
+ const [pda, bump] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token"), this.delegatePubkey.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
48
+ this._pda = pda;
49
+ this._bump = bump;
50
+ }
51
+ /**
52
+ * Create a CloakedAgent for owner management (freeze, unfreeze, update, close, withdraw, deposit)
53
+ * Does NOT have delegate keypair - cannot spend.
54
+ * Use this when you have the delegate's public key but not the Agent Key.
55
+ *
56
+ * @param delegatePubkey - Public key of the delegate (from URL or on-chain data)
57
+ * @param rpcUrl - Solana RPC endpoint URL
58
+ */
59
+ static forOwner(delegatePubkey, rpcUrl) {
60
+ const pubkey = typeof delegatePubkey === "string"
61
+ ? new web3_js_1.PublicKey(delegatePubkey)
62
+ : delegatePubkey;
63
+ const instance = Object.create(CloakedAgent.prototype);
64
+ instance.keypair = null;
65
+ instance.delegatePubkey = pubkey;
66
+ instance.connection = new web3_js_1.Connection(rpcUrl, "confirmed");
67
+ instance._ownerCommitment = null;
68
+ instance._agentSecret = null;
69
+ instance._nonce = null;
70
+ const [pda, bump] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token"), pubkey.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
71
+ instance._pda = pda;
72
+ instance._bump = bump;
73
+ return instance;
74
+ }
75
+ /**
76
+ * Create a CloakedAgent for private owner management (ZK proof-based)
77
+ * Uses the master secret and nonce to derive the agent secret and find the agent.
78
+ *
79
+ * @param masterSecret - Master secret derived from wallet signature
80
+ * @param nonce - Agent index (0, 1, 2, ...)
81
+ * @param rpcUrl - Solana RPC endpoint URL
82
+ * @returns CloakedAgent instance for private management
83
+ */
84
+ static async forPrivateOwner(masterSecret, nonce, rpcUrl) {
85
+ const { agentSecret, commitment } = await (0, zk_1.deriveAgentSecrets)(masterSecret, nonce);
86
+ const connection = new web3_js_1.Connection(rpcUrl, "confirmed");
87
+ // Find agent by commitment
88
+ const found = await (0, zk_1.findAgentByCommitment)(commitment, connection);
89
+ if (!found) {
90
+ throw new Error(`No Cloaked Agent found for nonce ${nonce}`);
91
+ }
92
+ const instance = Object.create(CloakedAgent.prototype);
93
+ instance.keypair = null;
94
+ instance.delegatePubkey = found.delegate;
95
+ instance.connection = connection;
96
+ instance._ownerCommitment = (0, zk_1.commitmentToBytes)(commitment);
97
+ instance._agentSecret = agentSecret;
98
+ instance._nonce = nonce;
99
+ // Legacy PDA (seeds = ["token", delegate]) - kept for backward compatibility
100
+ const [pda, bump] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token"), found.delegate.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
101
+ instance._pda = pda;
102
+ instance._bump = bump;
103
+ // Cache agentStatePda and vaultPda for private mode operations
104
+ const [agentStatePda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("cloaked_agent_state"), found.delegate.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
105
+ instance._agentStatePda = agentStatePda;
106
+ const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("vault"), agentStatePda.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
107
+ instance._vaultPda = vaultPda;
108
+ return instance;
109
+ }
110
+ /**
111
+ * Check if this agent is in private mode
112
+ */
113
+ get isPrivateMode() {
114
+ return this._ownerCommitment !== null && this._agentSecret !== null;
115
+ }
116
+ /**
117
+ * Get the owner commitment (private mode only)
118
+ */
119
+ get ownerCommitment() {
120
+ return this._ownerCommitment;
121
+ }
122
+ /**
123
+ * The agent's public key (used to derive PDA)
124
+ */
125
+ get publicKey() {
126
+ return this.delegatePubkey;
127
+ }
128
+ /**
129
+ * The PDA that holds this agent's SOL balance
130
+ */
131
+ get pda() {
132
+ return this._pda;
133
+ }
134
+ /**
135
+ * The bump seed used for PDA derivation
136
+ */
137
+ get bump() {
138
+ return this._bump;
139
+ }
140
+ /**
141
+ * Get the CloakedAgentState PDA address for this delegate
142
+ */
143
+ get agentStatePda() {
144
+ // Use cached value if available (set by forPrivateOwner)
145
+ if (this._agentStatePda) {
146
+ return this._agentStatePda;
147
+ }
148
+ const [pda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("cloaked_agent_state"), this.delegatePubkey.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
149
+ return pda;
150
+ }
151
+ /**
152
+ * Get the Vault PDA address for this agent
153
+ */
154
+ get vaultPda() {
155
+ // Use cached value if available (set by forPrivateOwner)
156
+ if (this._vaultPda) {
157
+ return this._vaultPda;
158
+ }
159
+ const agentStatePda = this.agentStatePda;
160
+ const [vault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("vault"), agentStatePda.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
161
+ return vault;
162
+ }
163
+ /**
164
+ * Get the current SOL balance of this agent
165
+ * @returns Balance in SOL
166
+ */
167
+ async getBalance() {
168
+ const lamports = await this.connection.getBalance(this.vaultPda);
169
+ return lamports / web3_js_1.LAMPORTS_PER_SOL;
170
+ }
171
+ /**
172
+ * Get the current balance in lamports
173
+ * @returns Balance in lamports
174
+ */
175
+ async getBalanceLamports() {
176
+ return await this.connection.getBalance(this.vaultPda);
177
+ }
178
+ /**
179
+ * Generate a new random Cloaked Agent
180
+ * @param rpcUrl - Solana RPC endpoint URL
181
+ * @returns New agent instance and its Agent Key
182
+ */
183
+ static generate(rpcUrl) {
184
+ const keypair = web3_js_1.Keypair.generate();
185
+ const agentKey = bs58_1.default.encode(keypair.secretKey);
186
+ const agent = new CloakedAgent(agentKey, rpcUrl);
187
+ return { agent, agentKey };
188
+ }
189
+ /**
190
+ * Derive the PDA for any public key (without needing full agent)
191
+ * @param publicKey - Agent's public key
192
+ * @returns The PDA address
193
+ */
194
+ static derivePda(publicKey) {
195
+ const [pda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token"), publicKey.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
196
+ return pda;
197
+ }
198
+ /**
199
+ * Derive PDA with bump
200
+ * @param publicKey - Agent's public key
201
+ * @returns The PDA address and bump
202
+ */
203
+ static derivePdaWithBump(publicKey) {
204
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token"), publicKey.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
205
+ }
206
+ /**
207
+ * Derive CloakedAgentState PDA for a delegate
208
+ */
209
+ static deriveAgentStatePda(delegate) {
210
+ const [pda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("cloaked_agent_state"), delegate.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
211
+ return pda;
212
+ }
213
+ /**
214
+ * Derive Vault PDA from CloakedAgentState PDA
215
+ */
216
+ static deriveVaultPda(agentStatePda) {
217
+ const [vault] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("vault"), agentStatePda.toBuffer()], constants_1.CLOAKED_PROGRAM_ID);
218
+ return vault;
219
+ }
220
+ /**
221
+ * Create a new Cloaked Agent with constraints on-chain
222
+ * @param connection - Solana connection
223
+ * @param owner - Owner wallet (Signer - can be wallet adapter or wrapped Keypair)
224
+ * @param options - Agent creation options
225
+ * @returns New CloakedAgent instance and Agent Key
226
+ */
227
+ static async create(connection, owner, options) {
228
+ const rpcUrl = connection.rpcEndpoint;
229
+ // Generate new delegate keypair
230
+ const delegate = web3_js_1.Keypair.generate();
231
+ const agentKey = bs58_1.default.encode(delegate.secretKey);
232
+ const provider = new anchor_1.AnchorProvider(connection, owner, { commitment: "confirmed" });
233
+ const program = new anchor_1.Program(idl_json_1.default, provider);
234
+ // Convert expiration to unix timestamp (0 = never)
235
+ const expiresAt = options.expiresAt
236
+ ? Math.floor(options.expiresAt.getTime() / 1000)
237
+ : 0;
238
+ // Derive PDAs
239
+ const agentStatePda = CloakedAgent.deriveAgentStatePda(delegate.publicKey);
240
+ const vaultPda = CloakedAgent.deriveVaultPda(agentStatePda);
241
+ // Call create_cloaked_agent instruction
242
+ const signature = await program.methods
243
+ .createCloakedAgent(new anchor_1.BN(options.maxPerTx ?? 0), new anchor_1.BN(options.dailyLimit ?? 0), new anchor_1.BN(options.totalLimit ?? 0), new anchor_1.BN(expiresAt))
244
+ .accounts({
245
+ cloakedAgentState: agentStatePda,
246
+ vault: vaultPda,
247
+ owner: owner.publicKey,
248
+ delegate: delegate.publicKey,
249
+ payer: owner.publicKey,
250
+ systemProgram: web3_js_1.SystemProgram.programId,
251
+ })
252
+ .rpc();
253
+ // If initial deposit requested, deposit funds
254
+ if (options.initialDeposit && options.initialDeposit > 0) {
255
+ await program.methods
256
+ .deposit(new anchor_1.BN(options.initialDeposit))
257
+ .accounts({
258
+ cloakedAgentState: agentStatePda,
259
+ vault: vaultPda,
260
+ depositor: owner.publicKey,
261
+ systemProgram: web3_js_1.SystemProgram.programId,
262
+ })
263
+ .rpc();
264
+ }
265
+ const agent = new CloakedAgent(agentKey, rpcUrl);
266
+ return { agent, agentKey, signature };
267
+ }
268
+ /**
269
+ * Fetch full agent state from on-chain
270
+ * @returns CloakedAgentState with all constraints and spending info
271
+ */
272
+ async getState() {
273
+ const dummyWallet = this.keypair
274
+ ? new anchor_1.Wallet(this.keypair)
275
+ : {
276
+ publicKey: this.delegatePubkey,
277
+ signTransaction: async (tx) => tx,
278
+ signAllTransactions: async (txs) => txs,
279
+ };
280
+ const provider = new anchor_1.AnchorProvider(this.connection, dummyWallet, { commitment: "confirmed" });
281
+ const program = new anchor_1.Program(idl_json_1.default, provider);
282
+ const agentStatePda = this.agentStatePda;
283
+ const vaultPda = this.vaultPda;
284
+ const state = await program.account.cloakedAgentState.fetch(agentStatePda);
285
+ const balance = await this.connection.getBalance(vaultPda);
286
+ const now = Math.floor(Date.now() / 1000);
287
+ const currentDay = Math.floor(now / SECONDS_PER_DAY);
288
+ const lastDay = state.lastDay.toNumber();
289
+ const dailySpent = currentDay > lastDay ? 0 : state.dailySpent.toNumber();
290
+ const dailyLimit = state.dailyLimit.toNumber();
291
+ const dailyRemaining = dailyLimit === 0 ? Number.MAX_SAFE_INTEGER : Math.max(0, dailyLimit - dailySpent);
292
+ const totalSpent = state.totalSpent.toNumber();
293
+ const totalLimit = state.totalLimit.toNumber();
294
+ const totalRemaining = totalLimit === 0 ? Number.MAX_SAFE_INTEGER : Math.max(0, totalLimit - totalSpent);
295
+ const expiresAtTs = state.expiresAt.toNumber();
296
+ const isExpired = expiresAtTs !== 0 && now > expiresAtTs;
297
+ const isFrozen = state.frozen;
298
+ let status;
299
+ if (isFrozen) {
300
+ status = "frozen";
301
+ }
302
+ else if (isExpired) {
303
+ status = "expired";
304
+ }
305
+ else {
306
+ status = "active";
307
+ }
308
+ // Handle Option<Pubkey> for owner - null if private mode
309
+ const owner = state.owner ? state.owner : null;
310
+ const ownerCommitment = new Uint8Array(state.ownerCommitment);
311
+ const isPrivate = owner === null;
312
+ return {
313
+ address: agentStatePda,
314
+ owner,
315
+ ownerCommitment,
316
+ delegate: state.delegate,
317
+ balance,
318
+ constraints: {
319
+ maxPerTx: state.maxPerTx.toNumber(),
320
+ dailyLimit,
321
+ totalLimit,
322
+ expiresAt: expiresAtTs === 0 ? null : new Date(expiresAtTs * 1000),
323
+ frozen: isFrozen,
324
+ },
325
+ spending: {
326
+ totalSpent,
327
+ dailySpent,
328
+ dailyRemaining,
329
+ totalRemaining,
330
+ },
331
+ status,
332
+ createdAt: new Date(state.createdAt.toNumber() * 1000),
333
+ isPrivate,
334
+ };
335
+ }
336
+ /**
337
+ * Spend from vault to destination (delegate signs)
338
+ * Requires agent mode (Agent Key) - throws if in owner mode.
339
+ *
340
+ * Two fee payment modes:
341
+ *
342
+ * 1. **With feePayer (standard mode)**: User's wallet pays tx fee directly
343
+ * - No relayer involved
344
+ * - Normal tx fee (~5k lamports) paid by user wallet
345
+ * - Vault only pays the amount to destination
346
+ *
347
+ * 2. **Without feePayer (agent/MCP mode)**: Relayer pays, vault reimburses
348
+ * - Relayer fronts the tx fee
349
+ * - Vault reimburses relayer 10k lamports
350
+ * - Delegate doesn't need any SOL
351
+ *
352
+ * @param options - Spend options (destination, amount, optional feePayer)
353
+ * @returns Spend result with signature and remaining balances
354
+ */
355
+ async spend(options) {
356
+ if (!this.keypair) {
357
+ throw new Error("Cannot spend in owner mode - requires Agent Key");
358
+ }
359
+ const agentStatePda = this.agentStatePda;
360
+ const vaultPda = this.vaultPda;
361
+ // If feePayer provided, user pays directly (no relayer)
362
+ if (options.feePayer) {
363
+ return this.spendWithFeePayer(options, agentStatePda, vaultPda);
364
+ }
365
+ // No feePayer - use relayer
366
+ return this.spendViaRelayer(options, agentStatePda, vaultPda);
367
+ }
368
+ /**
369
+ * Spend with user-provided fee payer (no relayer)
370
+ */
371
+ async spendWithFeePayer(options, agentStatePda, vaultPda) {
372
+ const feePayer = options.feePayer;
373
+ const provider = new anchor_1.AnchorProvider(this.connection, feePayer, { commitment: "confirmed" });
374
+ const program = new anchor_1.Program(idl_json_1.default, provider);
375
+ const spendIx = await program.methods
376
+ .spend(new anchor_1.BN(options.amount))
377
+ .accounts({
378
+ cloakedAgentState: agentStatePda,
379
+ vault: vaultPda,
380
+ delegate: this.keypair.publicKey,
381
+ feePayer: feePayer.publicKey,
382
+ destination: options.destination,
383
+ systemProgram: web3_js_1.SystemProgram.programId,
384
+ })
385
+ .instruction();
386
+ const tx = new web3_js_1.Transaction();
387
+ tx.add(spendIx);
388
+ tx.feePayer = feePayer.publicKey;
389
+ tx.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
390
+ tx.partialSign(this.keypair);
391
+ const signature = await provider.sendAndConfirm(tx, [this.keypair]);
392
+ const state = await this.getState();
393
+ return {
394
+ signature,
395
+ remainingBalance: state.balance,
396
+ dailyRemaining: state.spending.dailyRemaining,
397
+ };
398
+ }
399
+ /**
400
+ * Spend via relayer (relayer pays, vault reimburses)
401
+ */
402
+ async spendViaRelayer(options, agentStatePda, vaultPda) {
403
+ const feePayerPubkey = await (0, relayer_1.getRelayerPublicKey)();
404
+ const dummyProvider = new anchor_1.AnchorProvider(this.connection, new anchor_1.Wallet(this.keypair), { commitment: "confirmed" });
405
+ const program = new anchor_1.Program(idl_json_1.default, dummyProvider);
406
+ const spendIx = await program.methods
407
+ .spend(new anchor_1.BN(options.amount))
408
+ .accounts({
409
+ cloakedAgentState: agentStatePda,
410
+ vault: vaultPda,
411
+ delegate: this.keypair.publicKey,
412
+ feePayer: feePayerPubkey,
413
+ destination: options.destination,
414
+ systemProgram: web3_js_1.SystemProgram.programId,
415
+ })
416
+ .instruction();
417
+ const tx = new web3_js_1.Transaction();
418
+ tx.add(spendIx);
419
+ tx.feePayer = feePayerPubkey;
420
+ tx.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
421
+ tx.partialSign(this.keypair);
422
+ const serializedTx = tx.serialize({ requireAllSignatures: false });
423
+ const txBase64 = serializedTx.toString("base64");
424
+ const signature = await (0, relayer_1.cosignSpendViaRelayer)(txBase64);
425
+ const state = await this.getState();
426
+ return {
427
+ signature,
428
+ remainingBalance: state.balance,
429
+ dailyRemaining: state.spending.dailyRemaining,
430
+ };
431
+ }
432
+ /**
433
+ * Deposit SOL to vault (anyone can call)
434
+ * @param depositor - Signer of the depositor (wallet adapter or wrapped Keypair)
435
+ * @param amount - Amount in lamports
436
+ * @returns Transaction signature
437
+ */
438
+ async deposit(depositor, amount) {
439
+ const provider = new anchor_1.AnchorProvider(this.connection, depositor, { commitment: "confirmed" });
440
+ const program = new anchor_1.Program(idl_json_1.default, provider);
441
+ const agentStatePda = this.agentStatePda;
442
+ const vaultPda = this.vaultPda;
443
+ const signature = await program.methods
444
+ .deposit(new anchor_1.BN(amount))
445
+ .accounts({
446
+ cloakedAgentState: agentStatePda,
447
+ vault: vaultPda,
448
+ depositor: depositor.publicKey,
449
+ systemProgram: web3_js_1.SystemProgram.programId,
450
+ })
451
+ .rpc();
452
+ return signature;
453
+ }
454
+ /**
455
+ * Freeze agent (owner only) - emergency stop
456
+ * @param owner - Owner signer (wallet adapter or wrapped Keypair)
457
+ * @returns Transaction signature
458
+ */
459
+ async freeze(owner) {
460
+ const provider = new anchor_1.AnchorProvider(this.connection, owner, { commitment: "confirmed" });
461
+ const program = new anchor_1.Program(idl_json_1.default, provider);
462
+ const agentStatePda = this.agentStatePda;
463
+ const signature = await program.methods
464
+ .freeze()
465
+ .accounts({
466
+ cloakedAgentState: agentStatePda,
467
+ owner: owner.publicKey,
468
+ })
469
+ .rpc();
470
+ return signature;
471
+ }
472
+ /**
473
+ * Unfreeze agent (owner only)
474
+ * @param owner - Owner signer (wallet adapter or wrapped Keypair)
475
+ * @returns Transaction signature
476
+ */
477
+ async unfreeze(owner) {
478
+ const provider = new anchor_1.AnchorProvider(this.connection, owner, { commitment: "confirmed" });
479
+ const program = new anchor_1.Program(idl_json_1.default, provider);
480
+ const agentStatePda = this.agentStatePda;
481
+ const signature = await program.methods
482
+ .unfreeze()
483
+ .accounts({
484
+ cloakedAgentState: agentStatePda,
485
+ owner: owner.publicKey,
486
+ })
487
+ .rpc();
488
+ return signature;
489
+ }
490
+ /**
491
+ * Update agent constraints (owner only)
492
+ * @param owner - Owner signer (wallet adapter or wrapped Keypair)
493
+ * @param options - New constraint values (null = no change)
494
+ * @returns Transaction signature
495
+ */
496
+ async updateConstraints(owner, options) {
497
+ const provider = new anchor_1.AnchorProvider(this.connection, owner, { commitment: "confirmed" });
498
+ const program = new anchor_1.Program(idl_json_1.default, provider);
499
+ const agentStatePda = this.agentStatePda;
500
+ const maxPerTx = options.maxPerTx !== undefined ? new anchor_1.BN(options.maxPerTx) : null;
501
+ const dailyLimit = options.dailyLimit !== undefined ? new anchor_1.BN(options.dailyLimit) : null;
502
+ const totalLimit = options.totalLimit !== undefined ? new anchor_1.BN(options.totalLimit) : null;
503
+ const expiresAt = options.expiresAt !== undefined
504
+ ? new anchor_1.BN(options.expiresAt ? Math.floor(options.expiresAt.getTime() / 1000) : 0)
505
+ : null;
506
+ const signature = await program.methods
507
+ .updateConstraints(maxPerTx, dailyLimit, totalLimit, expiresAt)
508
+ .accounts({
509
+ cloakedAgentState: agentStatePda,
510
+ owner: owner.publicKey,
511
+ })
512
+ .rpc();
513
+ return signature;
514
+ }
515
+ /**
516
+ * Close agent and reclaim all funds to owner (owner only)
517
+ * @param owner - Owner signer (wallet adapter or wrapped Keypair)
518
+ * @returns Transaction signature
519
+ */
520
+ async close(owner) {
521
+ const provider = new anchor_1.AnchorProvider(this.connection, owner, { commitment: "confirmed" });
522
+ const program = new anchor_1.Program(idl_json_1.default, provider);
523
+ const agentStatePda = this.agentStatePda;
524
+ const vaultPda = this.vaultPda;
525
+ const signature = await program.methods
526
+ .closeCloakedAgent()
527
+ .accounts({
528
+ cloakedAgentState: agentStatePda,
529
+ vault: vaultPda,
530
+ owner: owner.publicKey,
531
+ systemProgram: web3_js_1.SystemProgram.programId,
532
+ })
533
+ .rpc();
534
+ return signature;
535
+ }
536
+ /**
537
+ * Withdraw from vault to any destination (owner only, no constraints)
538
+ * Works even if agent is frozen or expired - owner has full control
539
+ * Preserves privacy by allowing withdrawal to any wallet
540
+ * @param owner - Owner signer (wallet adapter or wrapped Keypair)
541
+ * @param amount - Amount in lamports to withdraw
542
+ * @param destination - Destination wallet (any PublicKey)
543
+ * @returns Transaction signature
544
+ */
545
+ async withdraw(owner, amount, destination) {
546
+ const provider = new anchor_1.AnchorProvider(this.connection, owner, { commitment: "confirmed" });
547
+ const program = new anchor_1.Program(idl_json_1.default, provider);
548
+ const agentStatePda = this.agentStatePda;
549
+ const vaultPda = this.vaultPda;
550
+ const signature = await program.methods
551
+ .withdraw(new anchor_1.BN(amount))
552
+ .accounts({
553
+ cloakedAgentState: agentStatePda,
554
+ vault: vaultPda,
555
+ owner: owner.publicKey,
556
+ destination: destination,
557
+ systemProgram: web3_js_1.SystemProgram.programId,
558
+ })
559
+ .rpc();
560
+ return signature;
561
+ }
562
+ // ============================================
563
+ // Private Mode Methods (ZK Proof-Based)
564
+ // ============================================
565
+ /**
566
+ * Create a new Cloaked Agent in private mode (no wallet linked on-chain)
567
+ *
568
+ * @param connection - Solana connection
569
+ * @param payer - Payer for transaction fees (can be any signer)
570
+ * @param masterSecret - Master secret derived from wallet signature
571
+ * @param nonce - Agent index (0, 1, 2, ...)
572
+ * @param options - Agent creation options
573
+ * @returns New CloakedAgent instance (with private mode) and Agent Key
574
+ */
575
+ static async createPrivate(connection, payer, masterSecret, nonce, options) {
576
+ const rpcUrl = connection.rpcEndpoint;
577
+ // Derive commitment from master secret and nonce
578
+ const { agentSecret, commitment } = await (0, zk_1.deriveAgentSecrets)(masterSecret, nonce);
579
+ const commitmentBytes = (0, zk_1.commitmentToBytes)(commitment);
580
+ // Generate new delegate keypair
581
+ const delegate = web3_js_1.Keypair.generate();
582
+ const agentKey = bs58_1.default.encode(delegate.secretKey);
583
+ const provider = new anchor_1.AnchorProvider(connection, payer, { commitment: "confirmed" });
584
+ const program = new anchor_1.Program(idl_json_1.default, provider);
585
+ // Convert expiration to unix timestamp (0 = never)
586
+ const expiresAt = options.expiresAt
587
+ ? Math.floor(options.expiresAt.getTime() / 1000)
588
+ : 0;
589
+ // Derive PDAs
590
+ const agentStatePda = CloakedAgent.deriveAgentStatePda(delegate.publicKey);
591
+ const vaultPda = CloakedAgent.deriveVaultPda(agentStatePda);
592
+ // Call create_cloaked_agent_private instruction
593
+ const signature = await program.methods
594
+ .createCloakedAgentPrivate(Array.from(commitmentBytes), new anchor_1.BN(options.maxPerTx ?? 0), new anchor_1.BN(options.dailyLimit ?? 0), new anchor_1.BN(options.totalLimit ?? 0), new anchor_1.BN(expiresAt))
595
+ .accounts({
596
+ cloakedAgentState: agentStatePda,
597
+ vault: vaultPda,
598
+ delegate: delegate.publicKey,
599
+ payer: payer.publicKey,
600
+ systemProgram: web3_js_1.SystemProgram.programId,
601
+ })
602
+ .rpc();
603
+ // If initial deposit requested, deposit funds
604
+ if (options.initialDeposit && options.initialDeposit > 0) {
605
+ await program.methods
606
+ .deposit(new anchor_1.BN(options.initialDeposit))
607
+ .accounts({
608
+ cloakedAgentState: agentStatePda,
609
+ vault: vaultPda,
610
+ depositor: payer.publicKey,
611
+ systemProgram: web3_js_1.SystemProgram.programId,
612
+ })
613
+ .rpc();
614
+ }
615
+ const agent = new CloakedAgent(agentKey, rpcUrl);
616
+ agent._ownerCommitment = commitmentBytes;
617
+ agent._agentSecret = agentSecret;
618
+ agent._nonce = nonce;
619
+ return { agent, agentKey, signature };
620
+ }
621
+ /**
622
+ * Create a new Cloaked Agent via relayer (truly private - user wallet never signs on-chain)
623
+ *
624
+ * This is the most private mode of operation:
625
+ * 1. User signs message to derive master secret (client-side only)
626
+ * 2. User sends total (fee + funding) to relayer via Privacy Cash
627
+ * 3. Relayer keeps 0.01 SOL fee, forwards rest to vault
628
+ * 4. Relayer creates the agent on-chain (generates delegate)
629
+ * 5. Agent Key encrypted for user (only user can decrypt)
630
+ * 6. User's wallet NEVER appears in any on-chain transaction
631
+ *
632
+ * @param masterSecret - Master secret derived from wallet signature
633
+ * @param nonce - Agent index (0, 1, 2, ...)
634
+ * @param options - Agent creation options
635
+ * @param depositSignature - Privacy Cash tx signature to relayer
636
+ * @param depositAmount - Total lamports sent (fee + vault funding)
637
+ * @param rpcUrl - Solana RPC endpoint URL
638
+ * @param apiUrl - Optional backend API URL
639
+ * @returns New CloakedAgent instance (with private mode) and Agent Key
640
+ */
641
+ static async createPrivateViaRelayer(masterSecret, nonce, options, depositSignature, depositAmount, rpcUrl, apiUrl) {
642
+ // Call relayer API
643
+ const result = await (0, relayer_1.createPrivateAgentViaRelayer)(masterSecret, nonce, {
644
+ maxPerTx: options.maxPerTx,
645
+ dailyLimit: options.dailyLimit,
646
+ totalLimit: options.totalLimit,
647
+ expiresAt: options.expiresAt,
648
+ }, depositSignature, depositAmount, apiUrl);
649
+ // Derive agent secret for private mode operations
650
+ const { agentSecret, commitment } = await (0, zk_1.deriveAgentSecrets)(masterSecret, nonce);
651
+ const commitmentBytes = (0, zk_1.commitmentToBytes)(commitment);
652
+ // Create agent instance with private mode
653
+ const agent = new CloakedAgent(result.agentKey, rpcUrl);
654
+ agent._ownerCommitment = commitmentBytes;
655
+ agent._agentSecret = agentSecret;
656
+ agent._nonce = nonce;
657
+ return {
658
+ agent,
659
+ agentKey: result.agentKey,
660
+ signature: result.signature,
661
+ vaultPda: result.vaultPda,
662
+ };
663
+ }
664
+ /**
665
+ * Create a new Cloaked Agent using your own relayer keypair (no backend needed, no fees)
666
+ *
667
+ * This is for technical users who want to:
668
+ * - Use their own funded keypair as a relayer
669
+ * - Pay their own rent (no 0.01 SOL fee to our relayer)
670
+ * - Have full control over the creation process
671
+ * - Fund vault separately via Privacy Cash for anonymity
672
+ *
673
+ * Flow:
674
+ * 1. User signs message to derive master secret (client-side only)
675
+ * 2. User provides a funded Keypair to pay rent
676
+ * 3. Creates agent directly on-chain (relayerKeypair signs and pays)
677
+ * 4. User's main wallet NEVER appears in any on-chain transaction
678
+ * 5. Fund vault separately via Privacy Cash for complete anonymity
679
+ *
680
+ * @param masterSecret - Master secret derived from wallet signature
681
+ * @param nonce - Agent index (0, 1, 2, ...)
682
+ * @param options - Agent creation options
683
+ * @param relayerKeypair - User's own funded keypair (pays rent ~0.00138 SOL)
684
+ * @param rpcUrl - Solana RPC endpoint URL
685
+ * @returns New CloakedAgent instance (with private mode) and Agent Key
686
+ */
687
+ static async createPrivateWithRelayer(masterSecret, nonce, options, relayerKeypair, rpcUrl) {
688
+ const connection = new web3_js_1.Connection(rpcUrl, "confirmed");
689
+ // Derive commitment from master secret and nonce
690
+ const { agentSecret, commitment } = await (0, zk_1.deriveAgentSecrets)(masterSecret, nonce);
691
+ const commitmentBytes = (0, zk_1.commitmentToBytes)(commitment);
692
+ // Generate new delegate keypair
693
+ const delegate = web3_js_1.Keypair.generate();
694
+ const agentKey = bs58_1.default.encode(delegate.secretKey);
695
+ const provider = new anchor_1.AnchorProvider(connection, new anchor_1.Wallet(relayerKeypair), { commitment: "confirmed" });
696
+ const program = new anchor_1.Program(idl_json_1.default, provider);
697
+ // Convert expiration to unix timestamp (0 = never)
698
+ const expiresAt = options.expiresAt
699
+ ? Math.floor(options.expiresAt.getTime() / 1000)
700
+ : 0;
701
+ // Derive PDAs
702
+ const agentStatePda = CloakedAgent.deriveAgentStatePda(delegate.publicKey);
703
+ const vaultPda = CloakedAgent.deriveVaultPda(agentStatePda);
704
+ // Call create_cloaked_agent_private instruction
705
+ const signature = await program.methods
706
+ .createCloakedAgentPrivate(Array.from(commitmentBytes), new anchor_1.BN(options.maxPerTx ?? 0), new anchor_1.BN(options.dailyLimit ?? 0), new anchor_1.BN(options.totalLimit ?? 0), new anchor_1.BN(expiresAt))
707
+ .accounts({
708
+ cloakedAgentState: agentStatePda,
709
+ vault: vaultPda,
710
+ delegate: delegate.publicKey,
711
+ payer: relayerKeypair.publicKey,
712
+ systemProgram: web3_js_1.SystemProgram.programId,
713
+ })
714
+ .rpc();
715
+ const agent = new CloakedAgent(agentKey, rpcUrl);
716
+ agent._ownerCommitment = commitmentBytes;
717
+ agent._agentSecret = agentSecret;
718
+ agent._nonce = nonce;
719
+ return {
720
+ agent,
721
+ agentKey,
722
+ signature,
723
+ vaultPda,
724
+ };
725
+ }
726
+ /**
727
+ * Freeze agent using ZK proof (private mode only)
728
+ * Uses relayer to submit transaction - vault pays for fees
729
+ * @param apiUrl - Optional API URL for relayer
730
+ * @returns Transaction signature
731
+ */
732
+ async freezePrivate(apiUrl) {
733
+ if (!this._agentSecret || !this._ownerCommitment) {
734
+ throw new Error("Not in private mode - use freeze() with owner signer instead");
735
+ }
736
+ if (!(0, zk_1.isProverReady)()) {
737
+ throw new Error("ZK prover not initialized. Call initProver() first.");
738
+ }
739
+ const commitment = (0, zk_1.bytesToCommitment)(this._ownerCommitment);
740
+ const proof = await (0, zk_1.generateOwnershipProof)(this._agentSecret, commitment);
741
+ const proofArgs = (0, zk_1.proofToInstructionArgs)(proof);
742
+ const signature = await (0, relayer_1.freezePrivateViaRelayer)({
743
+ agentStatePda: this.agentStatePda.toBase58(),
744
+ proofBytes: Array.from(proofArgs.proofBytes),
745
+ witnessBytes: Array.from(proofArgs.witnessBytes),
746
+ }, apiUrl);
747
+ return signature;
748
+ }
749
+ /**
750
+ * Unfreeze agent using ZK proof (private mode only)
751
+ * Uses relayer to submit transaction - vault pays for fees
752
+ * @param apiUrl - Optional API URL for relayer
753
+ * @returns Transaction signature
754
+ */
755
+ async unfreezePrivate(apiUrl) {
756
+ if (!this._agentSecret || !this._ownerCommitment) {
757
+ throw new Error("Not in private mode - use unfreeze() with owner signer instead");
758
+ }
759
+ if (!(0, zk_1.isProverReady)()) {
760
+ throw new Error("ZK prover not initialized. Call initProver() first.");
761
+ }
762
+ const commitment = (0, zk_1.bytesToCommitment)(this._ownerCommitment);
763
+ const proof = await (0, zk_1.generateOwnershipProof)(this._agentSecret, commitment);
764
+ const proofArgs = (0, zk_1.proofToInstructionArgs)(proof);
765
+ const signature = await (0, relayer_1.unfreezePrivateViaRelayer)({
766
+ agentStatePda: this.agentStatePda.toBase58(),
767
+ proofBytes: Array.from(proofArgs.proofBytes),
768
+ witnessBytes: Array.from(proofArgs.witnessBytes),
769
+ }, apiUrl);
770
+ return signature;
771
+ }
772
+ /**
773
+ * Update constraints using ZK proof (private mode only)
774
+ * Uses relayer to submit transaction - vault pays for fees
775
+ * @param options - New constraint values (undefined = no change)
776
+ * @param apiUrl - Optional API URL for relayer
777
+ * @returns Transaction signature
778
+ */
779
+ async updateConstraintsPrivate(options, apiUrl) {
780
+ if (!this._agentSecret || !this._ownerCommitment) {
781
+ throw new Error("Not in private mode - use updateConstraints() with owner signer instead");
782
+ }
783
+ if (!(0, zk_1.isProverReady)()) {
784
+ throw new Error("ZK prover not initialized. Call initProver() first.");
785
+ }
786
+ const commitment = (0, zk_1.bytesToCommitment)(this._ownerCommitment);
787
+ const proof = await (0, zk_1.generateOwnershipProof)(this._agentSecret, commitment);
788
+ const proofArgs = (0, zk_1.proofToInstructionArgs)(proof);
789
+ const signature = await (0, relayer_1.updateConstraintsPrivateViaRelayer)({
790
+ agentStatePda: this.agentStatePda.toBase58(),
791
+ proofBytes: Array.from(proofArgs.proofBytes),
792
+ witnessBytes: Array.from(proofArgs.witnessBytes),
793
+ maxPerTx: options.maxPerTx !== undefined ? options.maxPerTx : null,
794
+ dailyLimit: options.dailyLimit !== undefined ? options.dailyLimit : null,
795
+ totalLimit: options.totalLimit !== undefined ? options.totalLimit : null,
796
+ expiresAt: options.expiresAt !== undefined
797
+ ? (options.expiresAt ? Math.floor(options.expiresAt.getTime() / 1000) : 0)
798
+ : null,
799
+ }, apiUrl);
800
+ return signature;
801
+ }
802
+ /**
803
+ * Close agent and reclaim funds using ZK proof (private mode only)
804
+ * Uses relayer to submit transaction - vault pays for fees
805
+ * @param destination - Destination for remaining funds
806
+ * @param apiUrl - Optional API URL for relayer
807
+ * @returns Transaction signature
808
+ */
809
+ async closePrivate(destination, apiUrl) {
810
+ if (!this._agentSecret || !this._ownerCommitment) {
811
+ throw new Error("Not in private mode - use close() with owner signer instead");
812
+ }
813
+ if (!(0, zk_1.isProverReady)()) {
814
+ throw new Error("ZK prover not initialized. Call initProver() first.");
815
+ }
816
+ const commitment = (0, zk_1.bytesToCommitment)(this._ownerCommitment);
817
+ const proof = await (0, zk_1.generateOwnershipProof)(this._agentSecret, commitment);
818
+ const proofArgs = (0, zk_1.proofToInstructionArgs)(proof);
819
+ const signature = await (0, relayer_1.closePrivateViaRelayer)({
820
+ agentStatePda: this.agentStatePda.toBase58(),
821
+ proofBytes: Array.from(proofArgs.proofBytes),
822
+ witnessBytes: Array.from(proofArgs.witnessBytes),
823
+ destination: destination.toBase58(),
824
+ }, apiUrl);
825
+ return signature;
826
+ }
827
+ /**
828
+ * Withdraw using ZK proof (private mode only)
829
+ * Uses relayer to submit transaction - vault pays for fees
830
+ * @param amount - Amount in lamports to withdraw
831
+ * @param destination - Destination for funds
832
+ * @param apiUrl - Optional API URL for relayer
833
+ * @returns Transaction signature
834
+ */
835
+ async withdrawPrivate(amount, destination, apiUrl) {
836
+ if (!this._agentSecret || !this._ownerCommitment) {
837
+ throw new Error("Not in private mode - use withdraw() with owner signer instead");
838
+ }
839
+ if (!(0, zk_1.isProverReady)()) {
840
+ throw new Error("ZK prover not initialized. Call initProver() first.");
841
+ }
842
+ const commitment = (0, zk_1.bytesToCommitment)(this._ownerCommitment);
843
+ const proof = await (0, zk_1.generateOwnershipProof)(this._agentSecret, commitment);
844
+ const proofArgs = (0, zk_1.proofToInstructionArgs)(proof);
845
+ const signature = await (0, relayer_1.withdrawPrivateViaRelayer)({
846
+ agentStatePda: this.agentStatePda.toBase58(),
847
+ proofBytes: Array.from(proofArgs.proofBytes),
848
+ witnessBytes: Array.from(proofArgs.witnessBytes),
849
+ amount,
850
+ destination: destination.toBase58(),
851
+ }, apiUrl);
852
+ return signature;
853
+ }
854
+ /**
855
+ * Securely destroy sensitive data in this agent instance
856
+ * Call this when done using the agent to clear secrets from memory
857
+ */
858
+ destroy() {
859
+ // Zero out keypair secret key
860
+ if (this.keypair) {
861
+ this.keypair.secretKey.fill(0);
862
+ this.keypair = null;
863
+ }
864
+ // Clear private mode secrets
865
+ if (this._agentSecret !== null) {
866
+ this._agentSecret = BigInt(0);
867
+ this._agentSecret = null;
868
+ }
869
+ if (this._ownerCommitment !== null) {
870
+ this._ownerCommitment.fill(0);
871
+ this._ownerCommitment = null;
872
+ }
873
+ this._nonce = null;
874
+ }
875
+ }
876
+ exports.CloakedAgent = CloakedAgent;
877
+ //# sourceMappingURL=agent.js.map