@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.
- package/README.md +90 -0
- package/dist/agent.d.ts +321 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +877 -0
- package/dist/agent.js.map +1 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +64 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +9 -0
- package/dist/constants.js.map +1 -0
- package/dist/idl.json +1347 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +374 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/tools.d.ts +26 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +320 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/types.d.ts +61 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +4 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/relayer.d.ts +130 -0
- package/dist/relayer.d.ts.map +1 -0
- package/dist/relayer.js +225 -0
- package/dist/relayer.js.map +1 -0
- package/dist/signer.d.ts +18 -0
- package/dist/signer.d.ts.map +1 -0
- package/dist/signer.js +34 -0
- package/dist/signer.js.map +1 -0
- package/dist/token.d.ts +320 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +896 -0
- package/dist/token.js.map +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/zk/browser-prover.d.ts +85 -0
- package/dist/zk/browser-prover.d.ts.map +1 -0
- package/dist/zk/browser-prover.js +260 -0
- package/dist/zk/browser-prover.js.map +1 -0
- package/dist/zk/discovery.d.ts +65 -0
- package/dist/zk/discovery.d.ts.map +1 -0
- package/dist/zk/discovery.js +143 -0
- package/dist/zk/discovery.js.map +1 -0
- package/dist/zk/index.d.ts +14 -0
- package/dist/zk/index.d.ts.map +1 -0
- package/dist/zk/index.js +47 -0
- package/dist/zk/index.js.map +1 -0
- package/dist/zk/ownership_proof.json +1 -0
- package/dist/zk/poseidon.d.ts +31 -0
- package/dist/zk/poseidon.d.ts.map +1 -0
- package/dist/zk/poseidon.js +103 -0
- package/dist/zk/poseidon.js.map +1 -0
- package/dist/zk/prover.d.ts +49 -0
- package/dist/zk/prover.d.ts.map +1 -0
- package/dist/zk/prover.js +120 -0
- package/dist/zk/prover.js.map +1 -0
- package/dist/zk/secrets.d.ts +62 -0
- package/dist/zk/secrets.d.ts.map +1 -0
- package/dist/zk/secrets.js +98 -0
- package/dist/zk/secrets.js.map +1 -0
- 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
|