@exagent/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/dist/index.d.mts +2458 -0
- package/dist/index.d.ts +2458 -0
- package/dist/index.js +2767 -0
- package/dist/index.mjs +2738 -0
- package/package.json +37 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2738 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/client.ts
|
|
9
|
+
import {
|
|
10
|
+
createPublicClient,
|
|
11
|
+
createWalletClient,
|
|
12
|
+
http
|
|
13
|
+
} from "viem";
|
|
14
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
15
|
+
|
|
16
|
+
// src/contracts/registry.ts
|
|
17
|
+
var EXAGENT_REGISTRY_ABI = [
|
|
18
|
+
{
|
|
19
|
+
type: "function",
|
|
20
|
+
name: "registerAgent",
|
|
21
|
+
inputs: [
|
|
22
|
+
{ name: "name", type: "string" },
|
|
23
|
+
{ name: "metadataURI", type: "string" }
|
|
24
|
+
],
|
|
25
|
+
outputs: [{ name: "agentId", type: "uint256" }],
|
|
26
|
+
stateMutability: "nonpayable"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: "function",
|
|
30
|
+
name: "isNameAvailable",
|
|
31
|
+
inputs: [{ name: "name", type: "string" }],
|
|
32
|
+
outputs: [{ name: "available", type: "bool" }],
|
|
33
|
+
stateMutability: "view"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: "function",
|
|
37
|
+
name: "getAgentByName",
|
|
38
|
+
inputs: [{ name: "name", type: "string" }],
|
|
39
|
+
outputs: [{ name: "agentId", type: "uint256" }],
|
|
40
|
+
stateMutability: "view"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: "function",
|
|
44
|
+
name: "linkWallet",
|
|
45
|
+
inputs: [
|
|
46
|
+
{ name: "agentId", type: "uint256" },
|
|
47
|
+
{ name: "wallet", type: "address" },
|
|
48
|
+
{ name: "signature", type: "bytes" }
|
|
49
|
+
],
|
|
50
|
+
outputs: [],
|
|
51
|
+
stateMutability: "nonpayable"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: "function",
|
|
55
|
+
name: "linkOwnWallet",
|
|
56
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
57
|
+
outputs: [],
|
|
58
|
+
stateMutability: "nonpayable"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: "function",
|
|
62
|
+
name: "unlinkWallet",
|
|
63
|
+
inputs: [
|
|
64
|
+
{ name: "agentId", type: "uint256" },
|
|
65
|
+
{ name: "wallet", type: "address" }
|
|
66
|
+
],
|
|
67
|
+
outputs: [],
|
|
68
|
+
stateMutability: "nonpayable"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: "function",
|
|
72
|
+
name: "updateMetadata",
|
|
73
|
+
inputs: [
|
|
74
|
+
{ name: "agentId", type: "uint256" },
|
|
75
|
+
{ name: "newURI", type: "string" }
|
|
76
|
+
],
|
|
77
|
+
outputs: [],
|
|
78
|
+
stateMutability: "nonpayable"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: "function",
|
|
82
|
+
name: "agents",
|
|
83
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
84
|
+
outputs: [
|
|
85
|
+
{ name: "owner", type: "address" },
|
|
86
|
+
{ name: "name", type: "string" },
|
|
87
|
+
{ name: "metadataURI", type: "string" },
|
|
88
|
+
{ name: "registrationTime", type: "uint256" },
|
|
89
|
+
{ name: "verified", type: "bool" },
|
|
90
|
+
{ name: "linkedWalletCount", type: "uint256" }
|
|
91
|
+
],
|
|
92
|
+
stateMutability: "view"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: "function",
|
|
96
|
+
name: "getLinkedWallets",
|
|
97
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
98
|
+
outputs: [{ name: "", type: "address[]" }],
|
|
99
|
+
stateMutability: "view"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
type: "function",
|
|
103
|
+
name: "getAgentForWallet",
|
|
104
|
+
inputs: [{ name: "wallet", type: "address" }],
|
|
105
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
106
|
+
stateMutability: "view"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
type: "function",
|
|
110
|
+
name: "walletToAgent",
|
|
111
|
+
inputs: [{ name: "wallet", type: "address" }],
|
|
112
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
113
|
+
stateMutability: "view"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
type: "function",
|
|
117
|
+
name: "nonces",
|
|
118
|
+
inputs: [{ name: "wallet", type: "address" }],
|
|
119
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
120
|
+
stateMutability: "view"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: "function",
|
|
124
|
+
name: "nextAgentId",
|
|
125
|
+
inputs: [],
|
|
126
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
127
|
+
stateMutability: "view"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
type: "function",
|
|
131
|
+
name: "ownerOf",
|
|
132
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
133
|
+
outputs: [{ name: "", type: "address" }],
|
|
134
|
+
stateMutability: "view"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
type: "function",
|
|
138
|
+
name: "balanceOf",
|
|
139
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
140
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
141
|
+
stateMutability: "view"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
type: "event",
|
|
145
|
+
name: "AgentRegistered",
|
|
146
|
+
inputs: [
|
|
147
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
148
|
+
{ name: "owner", type: "address", indexed: true },
|
|
149
|
+
{ name: "name", type: "string", indexed: false },
|
|
150
|
+
{ name: "metadataURI", type: "string", indexed: false }
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
type: "event",
|
|
155
|
+
name: "WalletLinked",
|
|
156
|
+
inputs: [
|
|
157
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
158
|
+
{ name: "wallet", type: "address", indexed: true }
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
type: "event",
|
|
163
|
+
name: "WalletUnlinked",
|
|
164
|
+
inputs: [
|
|
165
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
166
|
+
{ name: "wallet", type: "address", indexed: true }
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
// V4: One agent per wallet
|
|
170
|
+
{
|
|
171
|
+
type: "function",
|
|
172
|
+
name: "ownerToAgentId",
|
|
173
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
174
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
175
|
+
stateMutability: "view"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
type: "function",
|
|
179
|
+
name: "getAgentByOwner",
|
|
180
|
+
inputs: [{ name: "wallet", type: "address" }],
|
|
181
|
+
outputs: [{ name: "agentId", type: "uint256" }],
|
|
182
|
+
stateMutability: "view"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: "function",
|
|
186
|
+
name: "canWalletRegister",
|
|
187
|
+
inputs: [{ name: "wallet", type: "address" }],
|
|
188
|
+
outputs: [
|
|
189
|
+
{ name: "canRegister", type: "bool" },
|
|
190
|
+
{ name: "existingAgentId", type: "uint256" }
|
|
191
|
+
],
|
|
192
|
+
stateMutability: "view"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
type: "function",
|
|
196
|
+
name: "version",
|
|
197
|
+
inputs: [],
|
|
198
|
+
outputs: [{ name: "", type: "string" }],
|
|
199
|
+
stateMutability: "pure"
|
|
200
|
+
},
|
|
201
|
+
// Config Epochs
|
|
202
|
+
{
|
|
203
|
+
type: "function",
|
|
204
|
+
name: "updateConfig",
|
|
205
|
+
inputs: [
|
|
206
|
+
{ name: "agentId", type: "uint256" },
|
|
207
|
+
{ name: "newConfigHash", type: "bytes32" }
|
|
208
|
+
],
|
|
209
|
+
outputs: [],
|
|
210
|
+
stateMutability: "nonpayable"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
type: "function",
|
|
214
|
+
name: "getConfigHash",
|
|
215
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
216
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
217
|
+
stateMutability: "view"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
type: "function",
|
|
221
|
+
name: "getCurrentEpoch",
|
|
222
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
223
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
224
|
+
stateMutability: "view"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
type: "function",
|
|
228
|
+
name: "getAgentConfig",
|
|
229
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
230
|
+
outputs: [
|
|
231
|
+
{
|
|
232
|
+
name: "",
|
|
233
|
+
type: "tuple",
|
|
234
|
+
components: [
|
|
235
|
+
{ name: "configHash", type: "bytes32" },
|
|
236
|
+
{ name: "epochId", type: "uint256" },
|
|
237
|
+
{ name: "epochStartBlock", type: "uint256" }
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
stateMutability: "view"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
type: "function",
|
|
245
|
+
name: "getEpochConfigHash",
|
|
246
|
+
inputs: [
|
|
247
|
+
{ name: "agentId", type: "uint256" },
|
|
248
|
+
{ name: "epochId", type: "uint256" }
|
|
249
|
+
],
|
|
250
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
251
|
+
stateMutability: "view"
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
type: "event",
|
|
255
|
+
name: "ConfigUpdated",
|
|
256
|
+
inputs: [
|
|
257
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
258
|
+
{ name: "oldConfigHash", type: "bytes32", indexed: false },
|
|
259
|
+
{ name: "newConfigHash", type: "bytes32", indexed: false },
|
|
260
|
+
{ name: "epochId", type: "uint256", indexed: false },
|
|
261
|
+
{ name: "blockNumber", type: "uint256", indexed: false }
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
];
|
|
265
|
+
var ExagentRegistry = class {
|
|
266
|
+
address;
|
|
267
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
268
|
+
publicClient;
|
|
269
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
270
|
+
walletClient;
|
|
271
|
+
chain;
|
|
272
|
+
account;
|
|
273
|
+
constructor(address, publicClient, walletClient, chain, account) {
|
|
274
|
+
this.address = address;
|
|
275
|
+
this.publicClient = publicClient;
|
|
276
|
+
this.walletClient = walletClient;
|
|
277
|
+
this.chain = chain;
|
|
278
|
+
this.account = account;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Register a new agent
|
|
282
|
+
* @param name Unique agent name (3-32 chars, alphanumeric + spaces/hyphens/underscores)
|
|
283
|
+
* @param metadataURI IPFS URI for agent metadata
|
|
284
|
+
* @returns Transaction hash
|
|
285
|
+
*/
|
|
286
|
+
async register(name, metadataURI) {
|
|
287
|
+
if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
|
|
288
|
+
const hash = await this.walletClient.writeContract({
|
|
289
|
+
account: this.account,
|
|
290
|
+
chain: this.chain,
|
|
291
|
+
address: this.address,
|
|
292
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
293
|
+
functionName: "registerAgent",
|
|
294
|
+
args: [name, metadataURI]
|
|
295
|
+
});
|
|
296
|
+
return hash;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Check if a name is available for registration
|
|
300
|
+
* @param name The name to check
|
|
301
|
+
* @returns True if the name can be used
|
|
302
|
+
*/
|
|
303
|
+
async isNameAvailable(name) {
|
|
304
|
+
const available = await this.publicClient.readContract({
|
|
305
|
+
address: this.address,
|
|
306
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
307
|
+
functionName: "isNameAvailable",
|
|
308
|
+
args: [name]
|
|
309
|
+
});
|
|
310
|
+
return available;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Get the agent ID that owns a specific name
|
|
314
|
+
* @param name The name to look up
|
|
315
|
+
* @returns Agent ID (0 if not taken)
|
|
316
|
+
*/
|
|
317
|
+
async getAgentByName(name) {
|
|
318
|
+
const agentId = await this.publicClient.readContract({
|
|
319
|
+
address: this.address,
|
|
320
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
321
|
+
functionName: "getAgentByName",
|
|
322
|
+
args: [name]
|
|
323
|
+
});
|
|
324
|
+
return agentId;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Link the wallet used by the agent to their agent ID
|
|
328
|
+
* @param agentId The agent's ID
|
|
329
|
+
* @returns Transaction hash
|
|
330
|
+
*/
|
|
331
|
+
async linkOwnWallet(agentId) {
|
|
332
|
+
if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
|
|
333
|
+
const hash = await this.walletClient.writeContract({
|
|
334
|
+
account: this.account,
|
|
335
|
+
chain: this.chain,
|
|
336
|
+
address: this.address,
|
|
337
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
338
|
+
functionName: "linkOwnWallet",
|
|
339
|
+
args: [agentId]
|
|
340
|
+
});
|
|
341
|
+
return hash;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Link an external wallet with signature proof
|
|
345
|
+
* @param agentId The agent's ID
|
|
346
|
+
* @param wallet The wallet to link
|
|
347
|
+
* @param signature Signature from the wallet proving ownership
|
|
348
|
+
* @returns Transaction hash
|
|
349
|
+
*/
|
|
350
|
+
async linkWallet(agentId, wallet, signature) {
|
|
351
|
+
if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
|
|
352
|
+
const hash = await this.walletClient.writeContract({
|
|
353
|
+
account: this.account,
|
|
354
|
+
chain: this.chain,
|
|
355
|
+
address: this.address,
|
|
356
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
357
|
+
functionName: "linkWallet",
|
|
358
|
+
args: [agentId, wallet, signature]
|
|
359
|
+
});
|
|
360
|
+
return hash;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Unlink a wallet from an agent
|
|
364
|
+
* @param agentId The agent's ID
|
|
365
|
+
* @param wallet The wallet to unlink
|
|
366
|
+
* @returns Transaction hash
|
|
367
|
+
*/
|
|
368
|
+
async unlinkWallet(agentId, wallet) {
|
|
369
|
+
if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
|
|
370
|
+
const hash = await this.walletClient.writeContract({
|
|
371
|
+
account: this.account,
|
|
372
|
+
chain: this.chain,
|
|
373
|
+
address: this.address,
|
|
374
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
375
|
+
functionName: "unlinkWallet",
|
|
376
|
+
args: [agentId, wallet]
|
|
377
|
+
});
|
|
378
|
+
return hash;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Update agent metadata
|
|
382
|
+
* @param agentId The agent's ID
|
|
383
|
+
* @param newURI New IPFS URI for metadata
|
|
384
|
+
* @returns Transaction hash
|
|
385
|
+
*/
|
|
386
|
+
async updateMetadata(agentId, newURI) {
|
|
387
|
+
if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
|
|
388
|
+
const hash = await this.walletClient.writeContract({
|
|
389
|
+
account: this.account,
|
|
390
|
+
chain: this.chain,
|
|
391
|
+
address: this.address,
|
|
392
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
393
|
+
functionName: "updateMetadata",
|
|
394
|
+
args: [agentId, newURI]
|
|
395
|
+
});
|
|
396
|
+
return hash;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get agent profile by ID
|
|
400
|
+
* @param agentId The agent's ID
|
|
401
|
+
* @returns Agent profile data
|
|
402
|
+
*/
|
|
403
|
+
async getAgent(agentId) {
|
|
404
|
+
const result = await this.publicClient.readContract({
|
|
405
|
+
address: this.address,
|
|
406
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
407
|
+
functionName: "agents",
|
|
408
|
+
args: [agentId]
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
agentId,
|
|
412
|
+
owner: result[0],
|
|
413
|
+
name: result[1],
|
|
414
|
+
metadataURI: result[2],
|
|
415
|
+
registrationTime: result[3],
|
|
416
|
+
verified: result[4],
|
|
417
|
+
linkedWalletCount: result[5]
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Get all linked wallets for an agent
|
|
422
|
+
* @param agentId The agent's ID
|
|
423
|
+
* @returns Array of linked wallet addresses
|
|
424
|
+
*/
|
|
425
|
+
async getLinkedWallets(agentId) {
|
|
426
|
+
const wallets = await this.publicClient.readContract({
|
|
427
|
+
address: this.address,
|
|
428
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
429
|
+
functionName: "getLinkedWallets",
|
|
430
|
+
args: [agentId]
|
|
431
|
+
});
|
|
432
|
+
return wallets;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Get agent ID for a wallet (for trade attribution)
|
|
436
|
+
* @param wallet The wallet address
|
|
437
|
+
* @returns Agent ID (0 if not linked)
|
|
438
|
+
*/
|
|
439
|
+
async getAgentForWallet(wallet) {
|
|
440
|
+
const agentId = await this.publicClient.readContract({
|
|
441
|
+
address: this.address,
|
|
442
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
443
|
+
functionName: "walletToAgent",
|
|
444
|
+
args: [wallet]
|
|
445
|
+
});
|
|
446
|
+
return agentId;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Check if a wallet is linked to a specific agent
|
|
450
|
+
* @param agentId The agent's ID
|
|
451
|
+
* @param wallet The wallet address to check
|
|
452
|
+
* @returns True if the wallet is linked to the agent
|
|
453
|
+
*/
|
|
454
|
+
async isLinkedWallet(agentId, wallet) {
|
|
455
|
+
const linkedWallets = await this.getLinkedWallets(agentId);
|
|
456
|
+
return linkedWallets.some((w) => w.toLowerCase() === wallet.toLowerCase());
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Get the nonce for wallet linking signature
|
|
460
|
+
* @param wallet The wallet address
|
|
461
|
+
* @returns Current nonce
|
|
462
|
+
*/
|
|
463
|
+
async getNonce(wallet) {
|
|
464
|
+
const nonce = await this.publicClient.readContract({
|
|
465
|
+
address: this.address,
|
|
466
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
467
|
+
functionName: "nonces",
|
|
468
|
+
args: [wallet]
|
|
469
|
+
});
|
|
470
|
+
return nonce;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Get the next agent ID that will be assigned
|
|
474
|
+
* @returns Next agent ID
|
|
475
|
+
*/
|
|
476
|
+
async getNextAgentId() {
|
|
477
|
+
const nextId = await this.publicClient.readContract({
|
|
478
|
+
address: this.address,
|
|
479
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
480
|
+
functionName: "nextAgentId"
|
|
481
|
+
});
|
|
482
|
+
return nextId;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Generate the message to sign for wallet linking
|
|
486
|
+
* @param wallet The wallet to link
|
|
487
|
+
* @param agentId The agent ID to link to
|
|
488
|
+
* @param nonce The current nonce for the wallet
|
|
489
|
+
* @returns Message to sign
|
|
490
|
+
*/
|
|
491
|
+
static generateLinkMessage(wallet, agentId, nonce) {
|
|
492
|
+
return `Link wallet ${wallet} to Exagent ${agentId} nonce ${nonce}`;
|
|
493
|
+
}
|
|
494
|
+
// ============ V4: One Agent Per Wallet ============
|
|
495
|
+
/**
|
|
496
|
+
* Get the agent owned by a wallet (not linked, owned)
|
|
497
|
+
* @param wallet The wallet to look up
|
|
498
|
+
* @returns Agent ID (0 if wallet doesn't own an agent)
|
|
499
|
+
*/
|
|
500
|
+
async getAgentByOwner(wallet) {
|
|
501
|
+
const agentId = await this.publicClient.readContract({
|
|
502
|
+
address: this.address,
|
|
503
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
504
|
+
functionName: "getAgentByOwner",
|
|
505
|
+
args: [wallet]
|
|
506
|
+
});
|
|
507
|
+
return agentId;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Check if a wallet can register a new agent
|
|
511
|
+
* @param wallet The wallet to check
|
|
512
|
+
* @returns Object with canRegister boolean and existingAgentId (0 if none)
|
|
513
|
+
*/
|
|
514
|
+
async canWalletRegister(wallet) {
|
|
515
|
+
const result = await this.publicClient.readContract({
|
|
516
|
+
address: this.address,
|
|
517
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
518
|
+
functionName: "canWalletRegister",
|
|
519
|
+
args: [wallet]
|
|
520
|
+
});
|
|
521
|
+
const [canRegister, existingAgentId] = result;
|
|
522
|
+
return { canRegister, existingAgentId };
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Get the registry contract version
|
|
526
|
+
* @returns Version string (e.g., "4.0.0")
|
|
527
|
+
*/
|
|
528
|
+
async getVersion() {
|
|
529
|
+
const version = await this.publicClient.readContract({
|
|
530
|
+
address: this.address,
|
|
531
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
532
|
+
functionName: "version"
|
|
533
|
+
});
|
|
534
|
+
return version;
|
|
535
|
+
}
|
|
536
|
+
// ============ Config Epochs ============
|
|
537
|
+
/**
|
|
538
|
+
* Update the agent's LLM config hash on-chain
|
|
539
|
+
* @param agentId The agent's ID
|
|
540
|
+
* @param configHash The keccak256 hash of (provider, model)
|
|
541
|
+
* @returns Transaction hash
|
|
542
|
+
*/
|
|
543
|
+
async updateConfig(agentId, configHash) {
|
|
544
|
+
if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
|
|
545
|
+
const hash = await this.walletClient.writeContract({
|
|
546
|
+
account: this.account,
|
|
547
|
+
chain: this.chain,
|
|
548
|
+
address: this.address,
|
|
549
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
550
|
+
functionName: "updateConfig",
|
|
551
|
+
args: [agentId, configHash]
|
|
552
|
+
});
|
|
553
|
+
return hash;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Get the current config hash for an agent
|
|
557
|
+
* @param agentId The agent's ID
|
|
558
|
+
* @returns Config hash (bytes32(0) if never set)
|
|
559
|
+
*/
|
|
560
|
+
async getConfigHash(agentId) {
|
|
561
|
+
const configHash = await this.publicClient.readContract({
|
|
562
|
+
address: this.address,
|
|
563
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
564
|
+
functionName: "getConfigHash",
|
|
565
|
+
args: [agentId]
|
|
566
|
+
});
|
|
567
|
+
return configHash;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Get the current epoch ID for an agent
|
|
571
|
+
* @param agentId The agent's ID
|
|
572
|
+
* @returns Current epoch (0 if never configured)
|
|
573
|
+
*/
|
|
574
|
+
async getCurrentEpoch(agentId) {
|
|
575
|
+
const epochId = await this.publicClient.readContract({
|
|
576
|
+
address: this.address,
|
|
577
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
578
|
+
functionName: "getCurrentEpoch",
|
|
579
|
+
args: [agentId]
|
|
580
|
+
});
|
|
581
|
+
return epochId;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Get full config info for an agent
|
|
585
|
+
* @param agentId The agent's ID
|
|
586
|
+
* @returns AgentConfig struct (configHash, epochId, epochStartBlock)
|
|
587
|
+
*/
|
|
588
|
+
async getAgentConfig(agentId) {
|
|
589
|
+
const config = await this.publicClient.readContract({
|
|
590
|
+
address: this.address,
|
|
591
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
592
|
+
functionName: "getAgentConfig",
|
|
593
|
+
args: [agentId]
|
|
594
|
+
});
|
|
595
|
+
const result = config;
|
|
596
|
+
return {
|
|
597
|
+
configHash: result.configHash,
|
|
598
|
+
epochId: result.epochId,
|
|
599
|
+
epochStartBlock: result.epochStartBlock
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Get the config hash for a specific epoch
|
|
604
|
+
* @param agentId The agent's ID
|
|
605
|
+
* @param epochId The epoch to look up
|
|
606
|
+
* @returns Config hash for that epoch
|
|
607
|
+
*/
|
|
608
|
+
async getEpochConfigHash(agentId, epochId) {
|
|
609
|
+
const configHash = await this.publicClient.readContract({
|
|
610
|
+
address: this.address,
|
|
611
|
+
abi: EXAGENT_REGISTRY_ABI,
|
|
612
|
+
functionName: "getEpochConfigHash",
|
|
613
|
+
args: [agentId, epochId]
|
|
614
|
+
});
|
|
615
|
+
return configHash;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Calculate the config hash for a provider and model
|
|
619
|
+
* @param provider The LLM provider name (e.g., "openai", "anthropic")
|
|
620
|
+
* @param model The model name (e.g., "gpt-4", "claude-opus-4.5")
|
|
621
|
+
* @returns keccak256 hash of the config
|
|
622
|
+
*/
|
|
623
|
+
static calculateConfigHash(provider, model) {
|
|
624
|
+
const { keccak256, encodePacked } = __require("viem");
|
|
625
|
+
return keccak256(encodePacked(["string", "string"], [provider, model]));
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
// src/contracts/vault.ts
|
|
630
|
+
var EXAGENT_VAULT_ABI = [
|
|
631
|
+
// ERC-4626 Standard
|
|
632
|
+
{ type: "function", name: "asset", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
|
|
633
|
+
{ type: "function", name: "totalAssets", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
634
|
+
{ type: "function", name: "convertToShares", inputs: [{ name: "assets", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
635
|
+
{ type: "function", name: "convertToAssets", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
636
|
+
{ type: "function", name: "maxDeposit", inputs: [{ name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
637
|
+
{ type: "function", name: "maxMint", inputs: [{ name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
638
|
+
{ type: "function", name: "maxWithdraw", inputs: [{ name: "owner", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
639
|
+
{ type: "function", name: "maxRedeem", inputs: [{ name: "owner", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
640
|
+
{ type: "function", name: "previewDeposit", inputs: [{ name: "assets", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
641
|
+
{ type: "function", name: "previewMint", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
642
|
+
{ type: "function", name: "previewWithdraw", inputs: [{ name: "assets", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
643
|
+
{ type: "function", name: "previewRedeem", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
644
|
+
{ type: "function", name: "deposit", inputs: [{ name: "assets", type: "uint256" }, { name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
|
|
645
|
+
{ type: "function", name: "mint", inputs: [{ name: "shares", type: "uint256" }, { name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
|
|
646
|
+
{ type: "function", name: "withdraw", inputs: [{ name: "assets", type: "uint256" }, { name: "receiver", type: "address" }, { name: "owner", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
|
|
647
|
+
{ type: "function", name: "redeem", inputs: [{ name: "shares", type: "uint256" }, { name: "receiver", type: "address" }, { name: "owner", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
|
|
648
|
+
// ERC-20
|
|
649
|
+
{ type: "function", name: "name", inputs: [], outputs: [{ type: "string" }], stateMutability: "view" },
|
|
650
|
+
{ type: "function", name: "symbol", inputs: [], outputs: [{ type: "string" }], stateMutability: "view" },
|
|
651
|
+
{ type: "function", name: "decimals", inputs: [], outputs: [{ type: "uint8" }], stateMutability: "view" },
|
|
652
|
+
{ type: "function", name: "totalSupply", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
653
|
+
{ type: "function", name: "balanceOf", inputs: [{ name: "account", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
654
|
+
{ type: "function", name: "allowance", inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
655
|
+
{ type: "function", name: "approve", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ type: "bool" }], stateMutability: "nonpayable" },
|
|
656
|
+
// Custom Exagent Vault
|
|
657
|
+
{ type: "function", name: "agentId", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
658
|
+
{ type: "function", name: "sharePrice", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
659
|
+
{ type: "function", name: "effectiveShares", inputs: [{ name: "user", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
660
|
+
{ type: "function", name: "userHighWaterMark", inputs: [{ name: "user", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
661
|
+
{ type: "function", name: "performanceFeeBps", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
662
|
+
{ type: "function", name: "managementFeeBps", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
663
|
+
{ type: "function", name: "feeRecipient", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
|
|
664
|
+
{ type: "function", name: "highWaterMark", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
665
|
+
{ type: "function", name: "depositsPaused", inputs: [], outputs: [{ type: "bool" }], stateMutability: "view" },
|
|
666
|
+
{ type: "function", name: "withdrawalsPaused", inputs: [], outputs: [{ type: "bool" }], stateMutability: "view" },
|
|
667
|
+
{ type: "function", name: "circuitBreakerActive", inputs: [], outputs: [{ type: "bool" }], stateMutability: "view" },
|
|
668
|
+
{ type: "function", name: "pendingWithdrawals", inputs: [{ name: "user", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
669
|
+
{ type: "function", name: "canWithdraw", inputs: [{ name: "user", type: "address" }], outputs: [{ type: "bool" }, { type: "uint256" }], stateMutability: "view" },
|
|
670
|
+
{ type: "function", name: "getRateLimitStatus", inputs: [], outputs: [{ name: "remaining", type: "uint256" }, { name: "periodEnds", type: "uint256" }], stateMutability: "view" },
|
|
671
|
+
{ type: "function", name: "getWithdrawalQueueLength", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
672
|
+
// Withdrawal Queue
|
|
673
|
+
{ type: "function", name: "requestWithdrawal", inputs: [{ name: "shares", type: "uint256" }, { name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
|
|
674
|
+
{ type: "function", name: "claimWithdrawal", inputs: [{ name: "requestId", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
|
|
675
|
+
{ type: "function", name: "cancelWithdrawal", inputs: [{ name: "requestId", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
|
|
676
|
+
{
|
|
677
|
+
type: "function",
|
|
678
|
+
name: "getPendingWithdrawal",
|
|
679
|
+
inputs: [{ name: "requestId", type: "uint256" }],
|
|
680
|
+
outputs: [
|
|
681
|
+
{ name: "owner", type: "address" },
|
|
682
|
+
{ name: "receiver", type: "address" },
|
|
683
|
+
{ name: "shares", type: "uint256" },
|
|
684
|
+
{ name: "requestTime", type: "uint256" },
|
|
685
|
+
{ name: "processed", type: "bool" },
|
|
686
|
+
{ name: "claimableAt", type: "uint256" }
|
|
687
|
+
],
|
|
688
|
+
stateMutability: "view"
|
|
689
|
+
},
|
|
690
|
+
// Emergency
|
|
691
|
+
{ type: "function", name: "emergencyWithdraw", inputs: [], outputs: [], stateMutability: "nonpayable" },
|
|
692
|
+
// Events
|
|
693
|
+
{ type: "event", name: "Deposit", inputs: [{ name: "sender", type: "address", indexed: true }, { name: "owner", type: "address", indexed: true }, { name: "assets", type: "uint256" }, { name: "shares", type: "uint256" }] },
|
|
694
|
+
{ type: "event", name: "Withdraw", inputs: [{ name: "sender", type: "address", indexed: true }, { name: "receiver", type: "address", indexed: true }, { name: "owner", type: "address", indexed: true }, { name: "assets", type: "uint256" }, { name: "shares", type: "uint256" }] },
|
|
695
|
+
{ type: "event", name: "PerformanceFeeCharged", inputs: [{ name: "user", type: "address", indexed: true }, { name: "feeShares", type: "uint256" }, { name: "profit", type: "uint256" }] },
|
|
696
|
+
{ type: "event", name: "WithdrawalRequested", inputs: [{ name: "requestId", type: "uint256", indexed: true }, { name: "owner", type: "address", indexed: true }, { name: "shares", type: "uint256" }, { name: "timestamp", type: "uint256" }] },
|
|
697
|
+
{ type: "event", name: "WithdrawalClaimed", inputs: [{ name: "requestId", type: "uint256", indexed: true }, { name: "receiver", type: "address", indexed: true }, { name: "assets", type: "uint256" }] },
|
|
698
|
+
{ type: "event", name: "WithdrawalCancelled", inputs: [{ name: "requestId", type: "uint256", indexed: true }] },
|
|
699
|
+
{ type: "event", name: "EmergencyWithdrawal", inputs: [{ name: "user", type: "address", indexed: true }, { name: "shares", type: "uint256" }, { name: "assets", type: "uint256" }] }
|
|
700
|
+
];
|
|
701
|
+
var ERC20_APPROVE_ABI = [
|
|
702
|
+
{ type: "function", name: "approve", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ type: "bool" }], stateMutability: "nonpayable" }
|
|
703
|
+
];
|
|
704
|
+
var ExagentVault = class {
|
|
705
|
+
address;
|
|
706
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
707
|
+
publicClient;
|
|
708
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
709
|
+
walletClient;
|
|
710
|
+
chain;
|
|
711
|
+
account;
|
|
712
|
+
constructor(vaultAddress, publicClient, walletClient, chain, account) {
|
|
713
|
+
this.address = vaultAddress;
|
|
714
|
+
this.publicClient = publicClient;
|
|
715
|
+
this.walletClient = walletClient;
|
|
716
|
+
this.chain = chain;
|
|
717
|
+
this.account = account;
|
|
718
|
+
}
|
|
719
|
+
// ============ Read Functions ============
|
|
720
|
+
/**
|
|
721
|
+
* Get comprehensive vault info
|
|
722
|
+
*/
|
|
723
|
+
async getVaultInfo() {
|
|
724
|
+
const [
|
|
725
|
+
name,
|
|
726
|
+
symbol,
|
|
727
|
+
asset,
|
|
728
|
+
agentId,
|
|
729
|
+
totalAssets,
|
|
730
|
+
totalSupply,
|
|
731
|
+
sharePrice,
|
|
732
|
+
highWaterMark,
|
|
733
|
+
performanceFeeBps,
|
|
734
|
+
managementFeeBps,
|
|
735
|
+
feeRecipient,
|
|
736
|
+
depositsPaused,
|
|
737
|
+
withdrawalsPaused,
|
|
738
|
+
circuitBreakerActive
|
|
739
|
+
] = await Promise.all([
|
|
740
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "name" }),
|
|
741
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "symbol" }),
|
|
742
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "asset" }),
|
|
743
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "agentId" }),
|
|
744
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "totalAssets" }),
|
|
745
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "totalSupply" }),
|
|
746
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "sharePrice" }),
|
|
747
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "highWaterMark" }),
|
|
748
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "performanceFeeBps" }),
|
|
749
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "managementFeeBps" }),
|
|
750
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "feeRecipient" }),
|
|
751
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "depositsPaused" }),
|
|
752
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "withdrawalsPaused" }),
|
|
753
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "circuitBreakerActive" })
|
|
754
|
+
]);
|
|
755
|
+
return {
|
|
756
|
+
address: this.address,
|
|
757
|
+
name,
|
|
758
|
+
symbol,
|
|
759
|
+
asset,
|
|
760
|
+
agentId,
|
|
761
|
+
totalAssets,
|
|
762
|
+
totalSupply,
|
|
763
|
+
sharePrice,
|
|
764
|
+
highWaterMark,
|
|
765
|
+
performanceFeeBps,
|
|
766
|
+
managementFeeBps,
|
|
767
|
+
feeRecipient,
|
|
768
|
+
depositsPaused,
|
|
769
|
+
withdrawalsPaused,
|
|
770
|
+
circuitBreakerActive
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Get user's position in the vault
|
|
775
|
+
*/
|
|
776
|
+
async getPosition(user) {
|
|
777
|
+
const [
|
|
778
|
+
shares,
|
|
779
|
+
effectiveShares,
|
|
780
|
+
pendingWithdrawals,
|
|
781
|
+
userHighWaterMark,
|
|
782
|
+
canWithdrawResult
|
|
783
|
+
] = await Promise.all([
|
|
784
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "balanceOf", args: [user] }),
|
|
785
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "effectiveShares", args: [user] }),
|
|
786
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "pendingWithdrawals", args: [user] }),
|
|
787
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "userHighWaterMark", args: [user] }),
|
|
788
|
+
this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "canWithdraw", args: [user] })
|
|
789
|
+
]);
|
|
790
|
+
const assetsValue = await this.publicClient.readContract({
|
|
791
|
+
address: this.address,
|
|
792
|
+
abi: EXAGENT_VAULT_ABI,
|
|
793
|
+
functionName: "convertToAssets",
|
|
794
|
+
args: [effectiveShares]
|
|
795
|
+
});
|
|
796
|
+
return {
|
|
797
|
+
shares,
|
|
798
|
+
effectiveShares,
|
|
799
|
+
pendingWithdrawals,
|
|
800
|
+
assetsValue,
|
|
801
|
+
userHighWaterMark,
|
|
802
|
+
canWithdraw: canWithdrawResult[0],
|
|
803
|
+
cooldownRemaining: canWithdrawResult[1]
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Get current share price (assets per share, scaled to 1e18)
|
|
808
|
+
*/
|
|
809
|
+
async getSharePrice() {
|
|
810
|
+
return this.publicClient.readContract({
|
|
811
|
+
address: this.address,
|
|
812
|
+
abi: EXAGENT_VAULT_ABI,
|
|
813
|
+
functionName: "sharePrice"
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Preview deposit - get shares for given assets
|
|
818
|
+
*/
|
|
819
|
+
async previewDeposit(assets) {
|
|
820
|
+
return this.publicClient.readContract({
|
|
821
|
+
address: this.address,
|
|
822
|
+
abi: EXAGENT_VAULT_ABI,
|
|
823
|
+
functionName: "previewDeposit",
|
|
824
|
+
args: [assets]
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Preview withdrawal - get assets for given shares
|
|
829
|
+
*/
|
|
830
|
+
async previewRedeem(shares) {
|
|
831
|
+
return this.publicClient.readContract({
|
|
832
|
+
address: this.address,
|
|
833
|
+
abi: EXAGENT_VAULT_ABI,
|
|
834
|
+
functionName: "previewRedeem",
|
|
835
|
+
args: [shares]
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Get max deposit amount
|
|
840
|
+
*/
|
|
841
|
+
async maxDeposit(receiver) {
|
|
842
|
+
return this.publicClient.readContract({
|
|
843
|
+
address: this.address,
|
|
844
|
+
abi: EXAGENT_VAULT_ABI,
|
|
845
|
+
functionName: "maxDeposit",
|
|
846
|
+
args: [receiver]
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Get max withdrawal amount
|
|
851
|
+
*/
|
|
852
|
+
async maxWithdraw(owner) {
|
|
853
|
+
return this.publicClient.readContract({
|
|
854
|
+
address: this.address,
|
|
855
|
+
abi: EXAGENT_VAULT_ABI,
|
|
856
|
+
functionName: "maxWithdraw",
|
|
857
|
+
args: [owner]
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Get rate limit status
|
|
862
|
+
*/
|
|
863
|
+
async getRateLimitStatus() {
|
|
864
|
+
const result = await this.publicClient.readContract({
|
|
865
|
+
address: this.address,
|
|
866
|
+
abi: EXAGENT_VAULT_ABI,
|
|
867
|
+
functionName: "getRateLimitStatus"
|
|
868
|
+
});
|
|
869
|
+
return { remaining: result[0], periodEnds: result[1] };
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Get pending withdrawal request
|
|
873
|
+
*/
|
|
874
|
+
async getPendingWithdrawal(requestId) {
|
|
875
|
+
const result = await this.publicClient.readContract({
|
|
876
|
+
address: this.address,
|
|
877
|
+
abi: EXAGENT_VAULT_ABI,
|
|
878
|
+
functionName: "getPendingWithdrawal",
|
|
879
|
+
args: [requestId]
|
|
880
|
+
});
|
|
881
|
+
return {
|
|
882
|
+
requestId,
|
|
883
|
+
owner: result[0],
|
|
884
|
+
receiver: result[1],
|
|
885
|
+
shares: result[2],
|
|
886
|
+
requestTime: result[3],
|
|
887
|
+
processed: result[4],
|
|
888
|
+
claimableAt: result[5]
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
// ============ Write Functions ============
|
|
892
|
+
/**
|
|
893
|
+
* Deposit assets into the vault
|
|
894
|
+
* @param assets Amount of underlying asset to deposit
|
|
895
|
+
* @param receiver Address to receive vault shares
|
|
896
|
+
* @returns Transaction hash
|
|
897
|
+
*/
|
|
898
|
+
async deposit(assets, receiver) {
|
|
899
|
+
if (!this.walletClient || !this.account) {
|
|
900
|
+
throw new Error("Wallet client required for write operations");
|
|
901
|
+
}
|
|
902
|
+
const to = receiver ?? this.account.address;
|
|
903
|
+
const hash = await this.walletClient.writeContract({
|
|
904
|
+
address: this.address,
|
|
905
|
+
abi: EXAGENT_VAULT_ABI,
|
|
906
|
+
functionName: "deposit",
|
|
907
|
+
args: [assets, to],
|
|
908
|
+
account: this.account,
|
|
909
|
+
chain: this.chain
|
|
910
|
+
});
|
|
911
|
+
return hash;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Withdraw assets from the vault
|
|
915
|
+
* @param assets Amount of underlying asset to withdraw
|
|
916
|
+
* @param receiver Address to receive assets
|
|
917
|
+
* @param owner Address whose shares to burn (defaults to caller)
|
|
918
|
+
* @returns Transaction hash
|
|
919
|
+
*/
|
|
920
|
+
async withdraw(assets, receiver, owner) {
|
|
921
|
+
if (!this.walletClient || !this.account) {
|
|
922
|
+
throw new Error("Wallet client required for write operations");
|
|
923
|
+
}
|
|
924
|
+
const to = receiver ?? this.account.address;
|
|
925
|
+
const from = owner ?? this.account.address;
|
|
926
|
+
const hash = await this.walletClient.writeContract({
|
|
927
|
+
address: this.address,
|
|
928
|
+
abi: EXAGENT_VAULT_ABI,
|
|
929
|
+
functionName: "withdraw",
|
|
930
|
+
args: [assets, to, from],
|
|
931
|
+
account: this.account,
|
|
932
|
+
chain: this.chain
|
|
933
|
+
});
|
|
934
|
+
return hash;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Redeem shares for assets
|
|
938
|
+
* @param shares Amount of shares to redeem
|
|
939
|
+
* @param receiver Address to receive assets
|
|
940
|
+
* @param owner Address whose shares to burn (defaults to caller)
|
|
941
|
+
* @returns Transaction hash
|
|
942
|
+
*/
|
|
943
|
+
async redeem(shares, receiver, owner) {
|
|
944
|
+
if (!this.walletClient || !this.account) {
|
|
945
|
+
throw new Error("Wallet client required for write operations");
|
|
946
|
+
}
|
|
947
|
+
const to = receiver ?? this.account.address;
|
|
948
|
+
const from = owner ?? this.account.address;
|
|
949
|
+
const hash = await this.walletClient.writeContract({
|
|
950
|
+
address: this.address,
|
|
951
|
+
abi: EXAGENT_VAULT_ABI,
|
|
952
|
+
functionName: "redeem",
|
|
953
|
+
args: [shares, to, from],
|
|
954
|
+
account: this.account,
|
|
955
|
+
chain: this.chain
|
|
956
|
+
});
|
|
957
|
+
return hash;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Request a queued withdrawal (for large amounts)
|
|
961
|
+
* @param shares Amount of shares to withdraw
|
|
962
|
+
* @param receiver Address to receive assets
|
|
963
|
+
* @returns Transaction hash
|
|
964
|
+
*/
|
|
965
|
+
async requestWithdrawal(shares, receiver) {
|
|
966
|
+
if (!this.walletClient || !this.account) {
|
|
967
|
+
throw new Error("Wallet client required for write operations");
|
|
968
|
+
}
|
|
969
|
+
const to = receiver ?? this.account.address;
|
|
970
|
+
const hash = await this.walletClient.writeContract({
|
|
971
|
+
address: this.address,
|
|
972
|
+
abi: EXAGENT_VAULT_ABI,
|
|
973
|
+
functionName: "requestWithdrawal",
|
|
974
|
+
args: [shares, to],
|
|
975
|
+
account: this.account,
|
|
976
|
+
chain: this.chain
|
|
977
|
+
});
|
|
978
|
+
return hash;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Claim a pending withdrawal request
|
|
982
|
+
* @param requestId The withdrawal request ID
|
|
983
|
+
* @returns Transaction hash
|
|
984
|
+
*/
|
|
985
|
+
async claimWithdrawal(requestId) {
|
|
986
|
+
if (!this.walletClient || !this.account) {
|
|
987
|
+
throw new Error("Wallet client required for write operations");
|
|
988
|
+
}
|
|
989
|
+
const hash = await this.walletClient.writeContract({
|
|
990
|
+
address: this.address,
|
|
991
|
+
abi: EXAGENT_VAULT_ABI,
|
|
992
|
+
functionName: "claimWithdrawal",
|
|
993
|
+
args: [requestId],
|
|
994
|
+
account: this.account,
|
|
995
|
+
chain: this.chain
|
|
996
|
+
});
|
|
997
|
+
return hash;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Cancel a pending withdrawal request
|
|
1001
|
+
* @param requestId The withdrawal request ID
|
|
1002
|
+
* @returns Transaction hash
|
|
1003
|
+
*/
|
|
1004
|
+
async cancelWithdrawal(requestId) {
|
|
1005
|
+
if (!this.walletClient || !this.account) {
|
|
1006
|
+
throw new Error("Wallet client required for write operations");
|
|
1007
|
+
}
|
|
1008
|
+
const hash = await this.walletClient.writeContract({
|
|
1009
|
+
address: this.address,
|
|
1010
|
+
abi: EXAGENT_VAULT_ABI,
|
|
1011
|
+
functionName: "cancelWithdrawal",
|
|
1012
|
+
args: [requestId],
|
|
1013
|
+
account: this.account,
|
|
1014
|
+
chain: this.chain
|
|
1015
|
+
});
|
|
1016
|
+
return hash;
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Emergency withdrawal (bypasses cooldown, forfeits pending rewards)
|
|
1020
|
+
* @returns Transaction hash
|
|
1021
|
+
*/
|
|
1022
|
+
async emergencyWithdraw() {
|
|
1023
|
+
if (!this.walletClient || !this.account) {
|
|
1024
|
+
throw new Error("Wallet client required for write operations");
|
|
1025
|
+
}
|
|
1026
|
+
const hash = await this.walletClient.writeContract({
|
|
1027
|
+
address: this.address,
|
|
1028
|
+
abi: EXAGENT_VAULT_ABI,
|
|
1029
|
+
functionName: "emergencyWithdraw",
|
|
1030
|
+
args: [],
|
|
1031
|
+
account: this.account,
|
|
1032
|
+
chain: this.chain
|
|
1033
|
+
});
|
|
1034
|
+
return hash;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Approve vault to spend underlying asset
|
|
1038
|
+
* @param assetAddress The asset token address
|
|
1039
|
+
* @param amount Amount to approve
|
|
1040
|
+
* @returns Transaction hash
|
|
1041
|
+
*/
|
|
1042
|
+
async approveAsset(assetAddress, amount) {
|
|
1043
|
+
if (!this.walletClient || !this.account) {
|
|
1044
|
+
throw new Error("Wallet client required for write operations");
|
|
1045
|
+
}
|
|
1046
|
+
const hash = await this.walletClient.writeContract({
|
|
1047
|
+
address: assetAddress,
|
|
1048
|
+
abi: ERC20_APPROVE_ABI,
|
|
1049
|
+
functionName: "approve",
|
|
1050
|
+
args: [this.address, amount],
|
|
1051
|
+
account: this.account,
|
|
1052
|
+
chain: this.chain
|
|
1053
|
+
});
|
|
1054
|
+
return hash;
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
// src/contracts/staking.ts
|
|
1059
|
+
var EXAGENT_STAKING_ABI = [
|
|
1060
|
+
// ============ State Variables ============
|
|
1061
|
+
{ type: "function", name: "exaToken", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
|
|
1062
|
+
{ type: "function", name: "totalStaked", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
1063
|
+
{ type: "function", name: "totalVeEXA", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
1064
|
+
{ type: "function", name: "registry", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
|
|
1065
|
+
// ============ Constants ============
|
|
1066
|
+
{ type: "function", name: "MIN_LOCK_DURATION", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
1067
|
+
{ type: "function", name: "MAX_LOCK_DURATION", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
1068
|
+
{ type: "function", name: "PRECISION", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
1069
|
+
// ============ Staking Functions ============
|
|
1070
|
+
{
|
|
1071
|
+
type: "function",
|
|
1072
|
+
name: "stake",
|
|
1073
|
+
inputs: [
|
|
1074
|
+
{ name: "amount", type: "uint256" },
|
|
1075
|
+
{ name: "lockDuration", type: "uint256" }
|
|
1076
|
+
],
|
|
1077
|
+
outputs: [],
|
|
1078
|
+
stateMutability: "nonpayable"
|
|
1079
|
+
},
|
|
1080
|
+
{ type: "function", name: "unstake", inputs: [], outputs: [], stateMutability: "nonpayable" },
|
|
1081
|
+
{
|
|
1082
|
+
type: "function",
|
|
1083
|
+
name: "extendLock",
|
|
1084
|
+
inputs: [{ name: "newLockDuration", type: "uint256" }],
|
|
1085
|
+
outputs: [],
|
|
1086
|
+
stateMutability: "nonpayable"
|
|
1087
|
+
},
|
|
1088
|
+
// ============ Reward Functions ============
|
|
1089
|
+
{ type: "function", name: "claimRewards", inputs: [], outputs: [], stateMutability: "nonpayable" },
|
|
1090
|
+
{ type: "function", name: "claimRewardsMulti", inputs: [], outputs: [], stateMutability: "nonpayable" },
|
|
1091
|
+
{
|
|
1092
|
+
type: "function",
|
|
1093
|
+
name: "claimRewardsToken",
|
|
1094
|
+
inputs: [{ name: "token", type: "address" }],
|
|
1095
|
+
outputs: [],
|
|
1096
|
+
stateMutability: "nonpayable"
|
|
1097
|
+
},
|
|
1098
|
+
// ============ Delegation Functions ============
|
|
1099
|
+
{
|
|
1100
|
+
type: "function",
|
|
1101
|
+
name: "delegate",
|
|
1102
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
1103
|
+
outputs: [],
|
|
1104
|
+
stateMutability: "nonpayable"
|
|
1105
|
+
},
|
|
1106
|
+
{ type: "function", name: "undelegate", inputs: [], outputs: [], stateMutability: "nonpayable" },
|
|
1107
|
+
{
|
|
1108
|
+
type: "function",
|
|
1109
|
+
name: "redelegate",
|
|
1110
|
+
inputs: [{ name: "newAgentId", type: "uint256" }],
|
|
1111
|
+
outputs: [],
|
|
1112
|
+
stateMutability: "nonpayable"
|
|
1113
|
+
},
|
|
1114
|
+
// ============ View Functions ============
|
|
1115
|
+
{
|
|
1116
|
+
type: "function",
|
|
1117
|
+
name: "stakes",
|
|
1118
|
+
inputs: [{ name: "user", type: "address" }],
|
|
1119
|
+
outputs: [
|
|
1120
|
+
{ name: "amount", type: "uint256" },
|
|
1121
|
+
{ name: "unlockTime", type: "uint256" },
|
|
1122
|
+
{ name: "lockDuration", type: "uint256" },
|
|
1123
|
+
{ name: "vEXABalance", type: "uint256" }
|
|
1124
|
+
],
|
|
1125
|
+
stateMutability: "view"
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
type: "function",
|
|
1129
|
+
name: "getVeEXABalance",
|
|
1130
|
+
inputs: [{ name: "user", type: "address" }],
|
|
1131
|
+
outputs: [{ type: "uint256" }],
|
|
1132
|
+
stateMutability: "view"
|
|
1133
|
+
},
|
|
1134
|
+
{
|
|
1135
|
+
type: "function",
|
|
1136
|
+
name: "calculateVeEXA",
|
|
1137
|
+
inputs: [
|
|
1138
|
+
{ name: "amount", type: "uint256" },
|
|
1139
|
+
{ name: "lockDuration", type: "uint256" }
|
|
1140
|
+
],
|
|
1141
|
+
outputs: [{ type: "uint256" }],
|
|
1142
|
+
stateMutability: "pure"
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
type: "function",
|
|
1146
|
+
name: "pendingRewards",
|
|
1147
|
+
inputs: [{ name: "user", type: "address" }],
|
|
1148
|
+
outputs: [{ type: "uint256" }],
|
|
1149
|
+
stateMutability: "view"
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
type: "function",
|
|
1153
|
+
name: "pendingRewardsForToken",
|
|
1154
|
+
inputs: [
|
|
1155
|
+
{ name: "user", type: "address" },
|
|
1156
|
+
{ name: "token", type: "address" }
|
|
1157
|
+
],
|
|
1158
|
+
outputs: [{ type: "uint256" }],
|
|
1159
|
+
stateMutability: "view"
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
type: "function",
|
|
1163
|
+
name: "getDelegatedAgent",
|
|
1164
|
+
inputs: [{ name: "user", type: "address" }],
|
|
1165
|
+
outputs: [{ type: "uint256" }],
|
|
1166
|
+
stateMutability: "view"
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
type: "function",
|
|
1170
|
+
name: "getAgentDelegation",
|
|
1171
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
1172
|
+
outputs: [
|
|
1173
|
+
{ name: "vexa", type: "uint256" },
|
|
1174
|
+
{ name: "count", type: "uint256" }
|
|
1175
|
+
],
|
|
1176
|
+
stateMutability: "view"
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
type: "function",
|
|
1180
|
+
name: "getDiscountTier",
|
|
1181
|
+
inputs: [{ name: "user", type: "address" }],
|
|
1182
|
+
outputs: [{ name: "discountBps", type: "uint256" }],
|
|
1183
|
+
stateMutability: "view"
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
type: "function",
|
|
1187
|
+
name: "getRewardTokens",
|
|
1188
|
+
inputs: [],
|
|
1189
|
+
outputs: [{ type: "address[]" }],
|
|
1190
|
+
stateMutability: "view"
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
type: "function",
|
|
1194
|
+
name: "delegatedTo",
|
|
1195
|
+
inputs: [{ name: "user", type: "address" }],
|
|
1196
|
+
outputs: [{ type: "uint256" }],
|
|
1197
|
+
stateMutability: "view"
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
type: "function",
|
|
1201
|
+
name: "userDelegatedVeEXA",
|
|
1202
|
+
inputs: [{ name: "user", type: "address" }],
|
|
1203
|
+
outputs: [{ type: "uint256" }],
|
|
1204
|
+
stateMutability: "view"
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
type: "function",
|
|
1208
|
+
name: "agentDelegatedVeEXA",
|
|
1209
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
1210
|
+
outputs: [{ type: "uint256" }],
|
|
1211
|
+
stateMutability: "view"
|
|
1212
|
+
},
|
|
1213
|
+
{
|
|
1214
|
+
type: "function",
|
|
1215
|
+
name: "agentDelegatorCount",
|
|
1216
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
1217
|
+
outputs: [{ type: "uint256" }],
|
|
1218
|
+
stateMutability: "view"
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
type: "function",
|
|
1222
|
+
name: "isRewardToken",
|
|
1223
|
+
inputs: [{ name: "token", type: "address" }],
|
|
1224
|
+
outputs: [{ type: "bool" }],
|
|
1225
|
+
stateMutability: "view"
|
|
1226
|
+
},
|
|
1227
|
+
// ============ Events ============
|
|
1228
|
+
{
|
|
1229
|
+
type: "event",
|
|
1230
|
+
name: "Staked",
|
|
1231
|
+
inputs: [
|
|
1232
|
+
{ name: "user", type: "address", indexed: true },
|
|
1233
|
+
{ name: "amount", type: "uint256", indexed: false },
|
|
1234
|
+
{ name: "lockDuration", type: "uint256", indexed: false },
|
|
1235
|
+
{ name: "unlockTime", type: "uint256", indexed: false },
|
|
1236
|
+
{ name: "vEXABalance", type: "uint256", indexed: false }
|
|
1237
|
+
]
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
type: "event",
|
|
1241
|
+
name: "Unstaked",
|
|
1242
|
+
inputs: [
|
|
1243
|
+
{ name: "user", type: "address", indexed: true },
|
|
1244
|
+
{ name: "amount", type: "uint256", indexed: false }
|
|
1245
|
+
]
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
type: "event",
|
|
1249
|
+
name: "LockExtended",
|
|
1250
|
+
inputs: [
|
|
1251
|
+
{ name: "user", type: "address", indexed: true },
|
|
1252
|
+
{ name: "newUnlockTime", type: "uint256", indexed: false },
|
|
1253
|
+
{ name: "newVeEXABalance", type: "uint256", indexed: false }
|
|
1254
|
+
]
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
type: "event",
|
|
1258
|
+
name: "RewardsClaimed",
|
|
1259
|
+
inputs: [
|
|
1260
|
+
{ name: "user", type: "address", indexed: true },
|
|
1261
|
+
{ name: "amount", type: "uint256", indexed: false }
|
|
1262
|
+
]
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
type: "event",
|
|
1266
|
+
name: "Delegated",
|
|
1267
|
+
inputs: [
|
|
1268
|
+
{ name: "delegator", type: "address", indexed: true },
|
|
1269
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
1270
|
+
{ name: "veEXAAmount", type: "uint256", indexed: false }
|
|
1271
|
+
]
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
type: "event",
|
|
1275
|
+
name: "Undelegated",
|
|
1276
|
+
inputs: [
|
|
1277
|
+
{ name: "delegator", type: "address", indexed: true },
|
|
1278
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
1279
|
+
{ name: "veEXAAmount", type: "uint256", indexed: false }
|
|
1280
|
+
]
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
type: "event",
|
|
1284
|
+
name: "MultiTokenRewardsClaimed",
|
|
1285
|
+
inputs: [
|
|
1286
|
+
{ name: "user", type: "address", indexed: true },
|
|
1287
|
+
{ name: "token", type: "address", indexed: true },
|
|
1288
|
+
{ name: "amount", type: "uint256", indexed: false }
|
|
1289
|
+
]
|
|
1290
|
+
}
|
|
1291
|
+
];
|
|
1292
|
+
var ERC20_APPROVE_ABI2 = [
|
|
1293
|
+
{
|
|
1294
|
+
type: "function",
|
|
1295
|
+
name: "approve",
|
|
1296
|
+
inputs: [
|
|
1297
|
+
{ name: "spender", type: "address" },
|
|
1298
|
+
{ name: "amount", type: "uint256" }
|
|
1299
|
+
],
|
|
1300
|
+
outputs: [{ type: "bool" }],
|
|
1301
|
+
stateMutability: "nonpayable"
|
|
1302
|
+
}
|
|
1303
|
+
];
|
|
1304
|
+
var DISCOUNT_TIERS = {
|
|
1305
|
+
none: 0,
|
|
1306
|
+
bronze: 1e3,
|
|
1307
|
+
// 10% discount at 10,000 vEXA
|
|
1308
|
+
silver: 2500,
|
|
1309
|
+
// 25% discount at 50,000 vEXA
|
|
1310
|
+
gold: 5e3
|
|
1311
|
+
// 50% discount at 100,000 vEXA
|
|
1312
|
+
};
|
|
1313
|
+
var ExagentStaking = class {
|
|
1314
|
+
address;
|
|
1315
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1316
|
+
publicClient;
|
|
1317
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1318
|
+
walletClient;
|
|
1319
|
+
chain;
|
|
1320
|
+
account;
|
|
1321
|
+
constructor(stakingAddress, publicClient, walletClient, chain, account) {
|
|
1322
|
+
this.address = stakingAddress;
|
|
1323
|
+
this.publicClient = publicClient;
|
|
1324
|
+
this.walletClient = walletClient;
|
|
1325
|
+
this.chain = chain;
|
|
1326
|
+
this.account = account;
|
|
1327
|
+
}
|
|
1328
|
+
// ============ Staking Write Functions ============
|
|
1329
|
+
/**
|
|
1330
|
+
* Stake EXA tokens to receive vEXA voting power
|
|
1331
|
+
* @param amount Amount of EXA to stake (in wei)
|
|
1332
|
+
* @param lockDuration Lock duration in seconds (30 days to 2 years)
|
|
1333
|
+
* @returns Transaction hash
|
|
1334
|
+
*
|
|
1335
|
+
* @example
|
|
1336
|
+
* ```typescript
|
|
1337
|
+
* // Stake 1000 EXA for 6 months
|
|
1338
|
+
* const tx = await staking.stake(
|
|
1339
|
+
* parseEther('1000'),
|
|
1340
|
+
* BigInt(180 * 24 * 60 * 60) // 180 days in seconds
|
|
1341
|
+
* );
|
|
1342
|
+
* ```
|
|
1343
|
+
*/
|
|
1344
|
+
async stake(amount, lockDuration) {
|
|
1345
|
+
if (!this.walletClient || !this.account) {
|
|
1346
|
+
throw new Error("Wallet client required for write operations");
|
|
1347
|
+
}
|
|
1348
|
+
const hash = await this.walletClient.writeContract({
|
|
1349
|
+
address: this.address,
|
|
1350
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1351
|
+
functionName: "stake",
|
|
1352
|
+
args: [amount, lockDuration],
|
|
1353
|
+
account: this.account,
|
|
1354
|
+
chain: this.chain
|
|
1355
|
+
});
|
|
1356
|
+
return hash;
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Unstake EXA tokens after lock expires
|
|
1360
|
+
* @returns Transaction hash
|
|
1361
|
+
* @throws Error if lock has not expired
|
|
1362
|
+
*/
|
|
1363
|
+
async unstake() {
|
|
1364
|
+
if (!this.walletClient || !this.account) {
|
|
1365
|
+
throw new Error("Wallet client required for write operations");
|
|
1366
|
+
}
|
|
1367
|
+
const hash = await this.walletClient.writeContract({
|
|
1368
|
+
address: this.address,
|
|
1369
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1370
|
+
functionName: "unstake",
|
|
1371
|
+
args: [],
|
|
1372
|
+
account: this.account,
|
|
1373
|
+
chain: this.chain
|
|
1374
|
+
});
|
|
1375
|
+
return hash;
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Extend lock duration for additional voting power
|
|
1379
|
+
* @param newLockDuration New lock duration in seconds (must be longer than remaining)
|
|
1380
|
+
* @returns Transaction hash
|
|
1381
|
+
*/
|
|
1382
|
+
async extendLock(newLockDuration) {
|
|
1383
|
+
if (!this.walletClient || !this.account) {
|
|
1384
|
+
throw new Error("Wallet client required for write operations");
|
|
1385
|
+
}
|
|
1386
|
+
const hash = await this.walletClient.writeContract({
|
|
1387
|
+
address: this.address,
|
|
1388
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1389
|
+
functionName: "extendLock",
|
|
1390
|
+
args: [newLockDuration],
|
|
1391
|
+
account: this.account,
|
|
1392
|
+
chain: this.chain
|
|
1393
|
+
});
|
|
1394
|
+
return hash;
|
|
1395
|
+
}
|
|
1396
|
+
// ============ Reward Write Functions ============
|
|
1397
|
+
/**
|
|
1398
|
+
* Claim accumulated EXA rewards
|
|
1399
|
+
* @returns Transaction hash
|
|
1400
|
+
*/
|
|
1401
|
+
async claimRewards() {
|
|
1402
|
+
if (!this.walletClient || !this.account) {
|
|
1403
|
+
throw new Error("Wallet client required for write operations");
|
|
1404
|
+
}
|
|
1405
|
+
const hash = await this.walletClient.writeContract({
|
|
1406
|
+
address: this.address,
|
|
1407
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1408
|
+
functionName: "claimRewards",
|
|
1409
|
+
args: [],
|
|
1410
|
+
account: this.account,
|
|
1411
|
+
chain: this.chain
|
|
1412
|
+
});
|
|
1413
|
+
return hash;
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Claim all pending multi-token rewards (ETH, USDC, etc.)
|
|
1417
|
+
* @returns Transaction hash
|
|
1418
|
+
*/
|
|
1419
|
+
async claimRewardsMulti() {
|
|
1420
|
+
if (!this.walletClient || !this.account) {
|
|
1421
|
+
throw new Error("Wallet client required for write operations");
|
|
1422
|
+
}
|
|
1423
|
+
const hash = await this.walletClient.writeContract({
|
|
1424
|
+
address: this.address,
|
|
1425
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1426
|
+
functionName: "claimRewardsMulti",
|
|
1427
|
+
args: [],
|
|
1428
|
+
account: this.account,
|
|
1429
|
+
chain: this.chain
|
|
1430
|
+
});
|
|
1431
|
+
return hash;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Claim pending rewards for a specific token
|
|
1435
|
+
* @param token The reward token address to claim
|
|
1436
|
+
* @returns Transaction hash
|
|
1437
|
+
*/
|
|
1438
|
+
async claimRewardsToken(token) {
|
|
1439
|
+
if (!this.walletClient || !this.account) {
|
|
1440
|
+
throw new Error("Wallet client required for write operations");
|
|
1441
|
+
}
|
|
1442
|
+
const hash = await this.walletClient.writeContract({
|
|
1443
|
+
address: this.address,
|
|
1444
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1445
|
+
functionName: "claimRewardsToken",
|
|
1446
|
+
args: [token],
|
|
1447
|
+
account: this.account,
|
|
1448
|
+
chain: this.chain
|
|
1449
|
+
});
|
|
1450
|
+
return hash;
|
|
1451
|
+
}
|
|
1452
|
+
// ============ Delegation Write Functions ============
|
|
1453
|
+
/**
|
|
1454
|
+
* Delegate vEXA voting power to an agent ("Agent Wars")
|
|
1455
|
+
* @param agentId The agent ID to delegate to
|
|
1456
|
+
* @returns Transaction hash
|
|
1457
|
+
*/
|
|
1458
|
+
async delegate(agentId) {
|
|
1459
|
+
if (!this.walletClient || !this.account) {
|
|
1460
|
+
throw new Error("Wallet client required for write operations");
|
|
1461
|
+
}
|
|
1462
|
+
const hash = await this.walletClient.writeContract({
|
|
1463
|
+
address: this.address,
|
|
1464
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1465
|
+
functionName: "delegate",
|
|
1466
|
+
args: [agentId],
|
|
1467
|
+
account: this.account,
|
|
1468
|
+
chain: this.chain
|
|
1469
|
+
});
|
|
1470
|
+
return hash;
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Remove delegation from current agent
|
|
1474
|
+
* @returns Transaction hash
|
|
1475
|
+
*/
|
|
1476
|
+
async undelegate() {
|
|
1477
|
+
if (!this.walletClient || !this.account) {
|
|
1478
|
+
throw new Error("Wallet client required for write operations");
|
|
1479
|
+
}
|
|
1480
|
+
const hash = await this.walletClient.writeContract({
|
|
1481
|
+
address: this.address,
|
|
1482
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1483
|
+
functionName: "undelegate",
|
|
1484
|
+
args: [],
|
|
1485
|
+
account: this.account,
|
|
1486
|
+
chain: this.chain
|
|
1487
|
+
});
|
|
1488
|
+
return hash;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Re-delegate to a different agent (undelegate + delegate in one tx)
|
|
1492
|
+
* @param newAgentId The new agent ID to delegate to
|
|
1493
|
+
* @returns Transaction hash
|
|
1494
|
+
*/
|
|
1495
|
+
async redelegate(newAgentId) {
|
|
1496
|
+
if (!this.walletClient || !this.account) {
|
|
1497
|
+
throw new Error("Wallet client required for write operations");
|
|
1498
|
+
}
|
|
1499
|
+
const hash = await this.walletClient.writeContract({
|
|
1500
|
+
address: this.address,
|
|
1501
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1502
|
+
functionName: "redelegate",
|
|
1503
|
+
args: [newAgentId],
|
|
1504
|
+
account: this.account,
|
|
1505
|
+
chain: this.chain
|
|
1506
|
+
});
|
|
1507
|
+
return hash;
|
|
1508
|
+
}
|
|
1509
|
+
// ============ Read Functions ============
|
|
1510
|
+
/**
|
|
1511
|
+
* Get comprehensive staking info for a user
|
|
1512
|
+
* @param userAddress Address to check (defaults to connected wallet)
|
|
1513
|
+
* @returns Staking info with amount, unlock time, and vEXA balance
|
|
1514
|
+
*/
|
|
1515
|
+
async getStakeInfo(userAddress) {
|
|
1516
|
+
const user = userAddress ?? this.account?.address;
|
|
1517
|
+
if (!user) {
|
|
1518
|
+
throw new Error("User address required");
|
|
1519
|
+
}
|
|
1520
|
+
const [stakeResult, currentVeEXA] = await Promise.all([
|
|
1521
|
+
this.publicClient.readContract({
|
|
1522
|
+
address: this.address,
|
|
1523
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1524
|
+
functionName: "stakes",
|
|
1525
|
+
args: [user]
|
|
1526
|
+
}),
|
|
1527
|
+
this.publicClient.readContract({
|
|
1528
|
+
address: this.address,
|
|
1529
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1530
|
+
functionName: "getVeEXABalance",
|
|
1531
|
+
args: [user]
|
|
1532
|
+
})
|
|
1533
|
+
]);
|
|
1534
|
+
const stake = stakeResult;
|
|
1535
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
1536
|
+
const isUnlocked = stake[1] <= now;
|
|
1537
|
+
const remainingLockTime = isUnlocked ? 0n : stake[1] - now;
|
|
1538
|
+
return {
|
|
1539
|
+
amount: stake[0],
|
|
1540
|
+
unlockTime: stake[1],
|
|
1541
|
+
lockDuration: stake[2],
|
|
1542
|
+
vEXABalance: stake[3],
|
|
1543
|
+
currentVeEXA,
|
|
1544
|
+
isUnlocked,
|
|
1545
|
+
remainingLockTime
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Get current vEXA balance (with time decay applied)
|
|
1550
|
+
* @param userAddress Address to check (defaults to connected wallet)
|
|
1551
|
+
* @returns Current vEXA balance
|
|
1552
|
+
*/
|
|
1553
|
+
async getVeEXABalance(userAddress) {
|
|
1554
|
+
const user = userAddress ?? this.account?.address;
|
|
1555
|
+
if (!user) {
|
|
1556
|
+
throw new Error("User address required");
|
|
1557
|
+
}
|
|
1558
|
+
return this.publicClient.readContract({
|
|
1559
|
+
address: this.address,
|
|
1560
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1561
|
+
functionName: "getVeEXABalance",
|
|
1562
|
+
args: [user]
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Get the agent ID a user is delegating to
|
|
1567
|
+
* @param userAddress Address to check (defaults to connected wallet)
|
|
1568
|
+
* @returns Agent ID (0 if not delegating)
|
|
1569
|
+
*/
|
|
1570
|
+
async getDelegatedAgent(userAddress) {
|
|
1571
|
+
const user = userAddress ?? this.account?.address;
|
|
1572
|
+
if (!user) {
|
|
1573
|
+
throw new Error("User address required");
|
|
1574
|
+
}
|
|
1575
|
+
return this.publicClient.readContract({
|
|
1576
|
+
address: this.address,
|
|
1577
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1578
|
+
functionName: "getDelegatedAgent",
|
|
1579
|
+
args: [user]
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Get delegation stats for an agent
|
|
1584
|
+
* @param agentId The agent ID to check
|
|
1585
|
+
* @returns Delegation info with total vEXA and delegator count
|
|
1586
|
+
*/
|
|
1587
|
+
async getAgentDelegation(agentId) {
|
|
1588
|
+
const result = await this.publicClient.readContract({
|
|
1589
|
+
address: this.address,
|
|
1590
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1591
|
+
functionName: "getAgentDelegation",
|
|
1592
|
+
args: [agentId]
|
|
1593
|
+
});
|
|
1594
|
+
return {
|
|
1595
|
+
totalVeEXA: result[0],
|
|
1596
|
+
delegatorCount: result[1]
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Get trading fee discount tier for a user
|
|
1601
|
+
* @param userAddress Address to check (defaults to connected wallet)
|
|
1602
|
+
* @returns Discount tier name and basis points
|
|
1603
|
+
*/
|
|
1604
|
+
async getDiscountTier(userAddress) {
|
|
1605
|
+
const user = userAddress ?? this.account?.address;
|
|
1606
|
+
if (!user) {
|
|
1607
|
+
throw new Error("User address required");
|
|
1608
|
+
}
|
|
1609
|
+
const discountBps = await this.publicClient.readContract({
|
|
1610
|
+
address: this.address,
|
|
1611
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1612
|
+
functionName: "getDiscountTier",
|
|
1613
|
+
args: [user]
|
|
1614
|
+
});
|
|
1615
|
+
let tier = "none";
|
|
1616
|
+
if (discountBps >= 5000n) {
|
|
1617
|
+
tier = "gold";
|
|
1618
|
+
} else if (discountBps >= 2500n) {
|
|
1619
|
+
tier = "silver";
|
|
1620
|
+
} else if (discountBps >= 1000n) {
|
|
1621
|
+
tier = "bronze";
|
|
1622
|
+
}
|
|
1623
|
+
return { tier, discountBps };
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Get pending EXA rewards for a user
|
|
1627
|
+
* @param userAddress Address to check (defaults to connected wallet)
|
|
1628
|
+
* @returns Pending reward amount in wei
|
|
1629
|
+
*/
|
|
1630
|
+
async pendingRewards(userAddress) {
|
|
1631
|
+
const user = userAddress ?? this.account?.address;
|
|
1632
|
+
if (!user) {
|
|
1633
|
+
throw new Error("User address required");
|
|
1634
|
+
}
|
|
1635
|
+
return this.publicClient.readContract({
|
|
1636
|
+
address: this.address,
|
|
1637
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1638
|
+
functionName: "pendingRewards",
|
|
1639
|
+
args: [user]
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Get pending rewards for a specific token
|
|
1644
|
+
* @param token The reward token address
|
|
1645
|
+
* @param userAddress Address to check (defaults to connected wallet)
|
|
1646
|
+
* @returns Pending reward amount in wei
|
|
1647
|
+
*/
|
|
1648
|
+
async pendingRewardsMulti(token, userAddress) {
|
|
1649
|
+
const user = userAddress ?? this.account?.address;
|
|
1650
|
+
if (!user) {
|
|
1651
|
+
throw new Error("User address required");
|
|
1652
|
+
}
|
|
1653
|
+
return this.publicClient.readContract({
|
|
1654
|
+
address: this.address,
|
|
1655
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1656
|
+
functionName: "pendingRewardsForToken",
|
|
1657
|
+
args: [user, token]
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Get list of whitelisted reward tokens
|
|
1662
|
+
* @returns Array of reward token addresses
|
|
1663
|
+
*/
|
|
1664
|
+
async getRewardTokens() {
|
|
1665
|
+
return this.publicClient.readContract({
|
|
1666
|
+
address: this.address,
|
|
1667
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1668
|
+
functionName: "getRewardTokens"
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
* Check if a token is whitelisted for rewards
|
|
1673
|
+
* @param token The token address to check
|
|
1674
|
+
* @returns True if token is whitelisted
|
|
1675
|
+
*/
|
|
1676
|
+
async isRewardToken(token) {
|
|
1677
|
+
return this.publicClient.readContract({
|
|
1678
|
+
address: this.address,
|
|
1679
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1680
|
+
functionName: "isRewardToken",
|
|
1681
|
+
args: [token]
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Get total staked EXA across all users
|
|
1686
|
+
* @returns Total staked amount in wei
|
|
1687
|
+
*/
|
|
1688
|
+
async getTotalStaked() {
|
|
1689
|
+
return this.publicClient.readContract({
|
|
1690
|
+
address: this.address,
|
|
1691
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1692
|
+
functionName: "totalStaked"
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* Get total vEXA supply across all users
|
|
1697
|
+
* @returns Total vEXA supply
|
|
1698
|
+
*/
|
|
1699
|
+
async getTotalVeEXA() {
|
|
1700
|
+
return this.publicClient.readContract({
|
|
1701
|
+
address: this.address,
|
|
1702
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1703
|
+
functionName: "totalVeEXA"
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Calculate vEXA balance for a given stake (preview)
|
|
1708
|
+
* @param amount Amount of EXA to stake
|
|
1709
|
+
* @param lockDuration Lock duration in seconds
|
|
1710
|
+
* @returns Expected vEXA balance
|
|
1711
|
+
*/
|
|
1712
|
+
async calculateVeEXA(amount, lockDuration) {
|
|
1713
|
+
return this.publicClient.readContract({
|
|
1714
|
+
address: this.address,
|
|
1715
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1716
|
+
functionName: "calculateVeEXA",
|
|
1717
|
+
args: [amount, lockDuration]
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Get the EXA token address
|
|
1722
|
+
* @returns EXA token contract address
|
|
1723
|
+
*/
|
|
1724
|
+
async getExaTokenAddress() {
|
|
1725
|
+
return this.publicClient.readContract({
|
|
1726
|
+
address: this.address,
|
|
1727
|
+
abi: EXAGENT_STAKING_ABI,
|
|
1728
|
+
functionName: "exaToken"
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
// ============ Helper Functions ============
|
|
1732
|
+
/**
|
|
1733
|
+
* Approve EXA token spending for staking
|
|
1734
|
+
* @param amount Amount to approve
|
|
1735
|
+
* @returns Transaction hash
|
|
1736
|
+
*/
|
|
1737
|
+
async approveExa(amount) {
|
|
1738
|
+
if (!this.walletClient || !this.account) {
|
|
1739
|
+
throw new Error("Wallet client required for write operations");
|
|
1740
|
+
}
|
|
1741
|
+
const exaToken = await this.getExaTokenAddress();
|
|
1742
|
+
const hash = await this.walletClient.writeContract({
|
|
1743
|
+
address: exaToken,
|
|
1744
|
+
abi: ERC20_APPROVE_ABI2,
|
|
1745
|
+
functionName: "approve",
|
|
1746
|
+
args: [this.address, amount],
|
|
1747
|
+
account: this.account,
|
|
1748
|
+
chain: this.chain
|
|
1749
|
+
});
|
|
1750
|
+
return hash;
|
|
1751
|
+
}
|
|
1752
|
+
// ============ Lock Duration Constants ============
|
|
1753
|
+
/** Minimum lock duration: 30 days in seconds */
|
|
1754
|
+
static MIN_LOCK_DURATION = 30n * 24n * 60n * 60n;
|
|
1755
|
+
/** Maximum lock duration: 2 years in seconds */
|
|
1756
|
+
static MAX_LOCK_DURATION = 730n * 24n * 60n * 60n;
|
|
1757
|
+
/** 1 month lock duration in seconds */
|
|
1758
|
+
static LOCK_1_MONTH = 30n * 24n * 60n * 60n;
|
|
1759
|
+
/** 3 months lock duration in seconds */
|
|
1760
|
+
static LOCK_3_MONTHS = 90n * 24n * 60n * 60n;
|
|
1761
|
+
/** 6 months lock duration in seconds */
|
|
1762
|
+
static LOCK_6_MONTHS = 180n * 24n * 60n * 60n;
|
|
1763
|
+
/** 1 year lock duration in seconds */
|
|
1764
|
+
static LOCK_1_YEAR = 365n * 24n * 60n * 60n;
|
|
1765
|
+
/** 2 years lock duration in seconds */
|
|
1766
|
+
static LOCK_2_YEARS = 730n * 24n * 60n * 60n;
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
// src/constants.ts
|
|
1770
|
+
import { base, baseSepolia } from "viem/chains";
|
|
1771
|
+
var CHAIN_CONFIG = {
|
|
1772
|
+
mainnet: base,
|
|
1773
|
+
testnet: baseSepolia
|
|
1774
|
+
};
|
|
1775
|
+
var CONTRACT_ADDRESSES = {
|
|
1776
|
+
mainnet: {
|
|
1777
|
+
agentRegistry: "0x0000000000000000000000000000000000000000",
|
|
1778
|
+
// To be deployed
|
|
1779
|
+
exaToken: "0x0000000000000000000000000000000000000000",
|
|
1780
|
+
staking: "0x0000000000000000000000000000000000000000",
|
|
1781
|
+
router: "0x0000000000000000000000000000000000000000",
|
|
1782
|
+
vaultFactory: "0x0000000000000000000000000000000000000000",
|
|
1783
|
+
feeCollector: "0x0000000000000000000000000000000000000000",
|
|
1784
|
+
buyback: "0x0000000000000000000000000000000000000000",
|
|
1785
|
+
serviceEscrow: "0x0000000000000000000000000000000000000000"
|
|
1786
|
+
},
|
|
1787
|
+
testnet: {
|
|
1788
|
+
agentRegistry: "0xCF48C341e3FebeCA5ECB7eb2535f61A2Ba855d9C",
|
|
1789
|
+
// V4 UUPS proxy, one-agent-per-wallet
|
|
1790
|
+
exaToken: "0x66c39b0ad96B3f5eE198Fef913c6636353a48A87",
|
|
1791
|
+
staking: "0x439441468e1b1b616E9D36b80969C241F261A011",
|
|
1792
|
+
// V2 with delegation
|
|
1793
|
+
router: "0xc0c27eEE047E414CD716D06C2444CF2073113d5C",
|
|
1794
|
+
// V3 with config epochs
|
|
1795
|
+
vaultFactory: "0x5c099daaE33801a907Bb57011c6749655b55dc75",
|
|
1796
|
+
// V2 with requirements
|
|
1797
|
+
feeCollector: "0xcB57b03a50df054b9C738Df1324C17A4fDe4fe46",
|
|
1798
|
+
buyback: "0x35cdEa810A130A846265682e5c71A68A507aB895",
|
|
1799
|
+
serviceEscrow: "0x74a3496b148DEE735ac388299aF9Ac2F7C4EdCBf"
|
|
1800
|
+
}
|
|
1801
|
+
};
|
|
1802
|
+
var DEX_ADDRESSES = {
|
|
1803
|
+
// Aerodrome
|
|
1804
|
+
aerodromeRouter: "0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43",
|
|
1805
|
+
aerodromeSlipstream: "0xBE6D8f0d05cC4be24d5167a3eF062215bE6D18a5",
|
|
1806
|
+
// Uniswap
|
|
1807
|
+
uniswapUniversalRouter: "0x198EF79F1F515F02dFE9e3115eD9fC07183f02fC",
|
|
1808
|
+
uniswapV4Router: "0x6ff5693b99212da76ad316178a184ab56d299b43",
|
|
1809
|
+
// Common tokens
|
|
1810
|
+
WETH: "0x4200000000000000000000000000000000000006",
|
|
1811
|
+
USDC: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
|
|
1812
|
+
};
|
|
1813
|
+
var ZERO_X_CONFIG = {
|
|
1814
|
+
baseUrl: "https://api.0x.org",
|
|
1815
|
+
chainId: 8453
|
|
1816
|
+
// Base
|
|
1817
|
+
};
|
|
1818
|
+
var EXAGENT_API_CONFIG = {
|
|
1819
|
+
mainnet: "https://api.exagent.io",
|
|
1820
|
+
testnet: "https://api.testnet.exagent.io"
|
|
1821
|
+
};
|
|
1822
|
+
|
|
1823
|
+
// src/client.ts
|
|
1824
|
+
var ExagentClient = class {
|
|
1825
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1826
|
+
publicClient;
|
|
1827
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1828
|
+
walletClient;
|
|
1829
|
+
account;
|
|
1830
|
+
network;
|
|
1831
|
+
apiKey;
|
|
1832
|
+
// Contract interfaces
|
|
1833
|
+
registry;
|
|
1834
|
+
staking;
|
|
1835
|
+
// Cached agent ID
|
|
1836
|
+
_agentId;
|
|
1837
|
+
constructor(config) {
|
|
1838
|
+
this.network = config.network ?? "testnet";
|
|
1839
|
+
this.apiKey = config.apiKey;
|
|
1840
|
+
const chain = CHAIN_CONFIG[this.network];
|
|
1841
|
+
this.publicClient = createPublicClient({
|
|
1842
|
+
chain,
|
|
1843
|
+
transport: http(config.rpcUrl)
|
|
1844
|
+
});
|
|
1845
|
+
if (config.walletClient) {
|
|
1846
|
+
this.walletClient = config.walletClient;
|
|
1847
|
+
this.account = config.walletClient.account;
|
|
1848
|
+
} else if (config.privateKey) {
|
|
1849
|
+
this.account = privateKeyToAccount(config.privateKey);
|
|
1850
|
+
this.walletClient = createWalletClient({
|
|
1851
|
+
account: this.account,
|
|
1852
|
+
chain,
|
|
1853
|
+
transport: http(config.rpcUrl)
|
|
1854
|
+
});
|
|
1855
|
+
} else {
|
|
1856
|
+
throw new Error("Either privateKey or walletClient must be provided");
|
|
1857
|
+
}
|
|
1858
|
+
const addresses = CONTRACT_ADDRESSES[this.network];
|
|
1859
|
+
this.registry = new ExagentRegistry(
|
|
1860
|
+
addresses.agentRegistry,
|
|
1861
|
+
this.publicClient,
|
|
1862
|
+
this.walletClient,
|
|
1863
|
+
chain,
|
|
1864
|
+
this.account
|
|
1865
|
+
);
|
|
1866
|
+
this.staking = new ExagentStaking(
|
|
1867
|
+
addresses.staking,
|
|
1868
|
+
this.publicClient,
|
|
1869
|
+
this.walletClient,
|
|
1870
|
+
chain,
|
|
1871
|
+
this.account
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1874
|
+
// ============ Agent Registration ============
|
|
1875
|
+
/**
|
|
1876
|
+
* Register as a new agent on Exagent
|
|
1877
|
+
* @param metadata Agent metadata (will be uploaded to IPFS)
|
|
1878
|
+
* @returns The assigned agent ID
|
|
1879
|
+
* @throws Error if wallet already owns an agent (one agent per wallet rule)
|
|
1880
|
+
*/
|
|
1881
|
+
async register(metadata) {
|
|
1882
|
+
const { canRegister, existingAgentId } = await this.registry.canWalletRegister(this.account.address);
|
|
1883
|
+
if (!canRegister) {
|
|
1884
|
+
throw new Error(
|
|
1885
|
+
`This wallet already owns agent #${existingAgentId}. Each wallet can only own one agent. Use a different wallet to create a new agent.`
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
const nameAvailable = await this.registry.isNameAvailable(metadata.name);
|
|
1889
|
+
if (!nameAvailable) {
|
|
1890
|
+
throw new Error(`Agent name "${metadata.name}" is already taken`);
|
|
1891
|
+
}
|
|
1892
|
+
const metadataURI = await this.uploadMetadata(metadata);
|
|
1893
|
+
const hash = await this.registry.register(metadata.name, metadataURI);
|
|
1894
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
1895
|
+
const agentId = this.parseAgentIdFromReceipt(receipt);
|
|
1896
|
+
this._agentId = agentId;
|
|
1897
|
+
await this.registry.linkOwnWallet(agentId);
|
|
1898
|
+
return agentId;
|
|
1899
|
+
}
|
|
1900
|
+
/**
|
|
1901
|
+
* Check if an agent name is available for registration
|
|
1902
|
+
* @param name The name to check
|
|
1903
|
+
* @returns True if the name is available
|
|
1904
|
+
*/
|
|
1905
|
+
async isNameAvailable(name) {
|
|
1906
|
+
return this.registry.isNameAvailable(name);
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1909
|
+
* Get agent by name (returns 0 if not found)
|
|
1910
|
+
* @param name The agent name to look up
|
|
1911
|
+
* @returns Agent ID or 0 if not found
|
|
1912
|
+
*/
|
|
1913
|
+
async getAgentByName(name) {
|
|
1914
|
+
return this.registry.getAgentByName(name);
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Get the current agent's ID (if registered)
|
|
1918
|
+
*/
|
|
1919
|
+
async getAgentId() {
|
|
1920
|
+
if (this._agentId) return this._agentId;
|
|
1921
|
+
const agentId = await this.registry.getAgentForWallet(this.account.address);
|
|
1922
|
+
if (agentId > 0n) {
|
|
1923
|
+
this._agentId = agentId;
|
|
1924
|
+
return agentId;
|
|
1925
|
+
}
|
|
1926
|
+
return void 0;
|
|
1927
|
+
}
|
|
1928
|
+
/**
|
|
1929
|
+
* Get agent profile
|
|
1930
|
+
* @param agentId Optional agent ID (defaults to current agent)
|
|
1931
|
+
*/
|
|
1932
|
+
async getAgent(agentId) {
|
|
1933
|
+
const id = agentId ?? await this.getAgentId();
|
|
1934
|
+
if (!id) throw new Error("Agent not registered");
|
|
1935
|
+
return this.registry.getAgent(id);
|
|
1936
|
+
}
|
|
1937
|
+
// ============ Wallet Management ============
|
|
1938
|
+
/**
|
|
1939
|
+
* Link an additional wallet to this agent
|
|
1940
|
+
* @param wallet Wallet address to link
|
|
1941
|
+
* @param signMessage Function to sign a message with the wallet
|
|
1942
|
+
*/
|
|
1943
|
+
async linkWallet(wallet, signMessage) {
|
|
1944
|
+
const agentId = await this.getAgentId();
|
|
1945
|
+
if (!agentId) throw new Error("Agent not registered");
|
|
1946
|
+
const nonce = await this.registry.getNonce(wallet);
|
|
1947
|
+
const message = ExagentRegistry.generateLinkMessage(wallet, agentId, nonce);
|
|
1948
|
+
const signature = await signMessage(message);
|
|
1949
|
+
return this.registry.linkWallet(agentId, wallet, signature);
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* Get all wallets linked to this agent
|
|
1953
|
+
*/
|
|
1954
|
+
async getLinkedWallets() {
|
|
1955
|
+
const agentId = await this.getAgentId();
|
|
1956
|
+
if (!agentId) throw new Error("Agent not registered");
|
|
1957
|
+
return this.registry.getLinkedWallets(agentId);
|
|
1958
|
+
}
|
|
1959
|
+
// ============ Trading ============
|
|
1960
|
+
/**
|
|
1961
|
+
* Get optimal DEX route for a trade
|
|
1962
|
+
* Uses 0x API for aggregation across Aerodrome, Uniswap, etc.
|
|
1963
|
+
*/
|
|
1964
|
+
async getRoute(intent) {
|
|
1965
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
1966
|
+
const response = await fetch(`${apiUrl}/v1/routes/quote`, {
|
|
1967
|
+
method: "POST",
|
|
1968
|
+
headers: {
|
|
1969
|
+
"Content-Type": "application/json",
|
|
1970
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
1971
|
+
},
|
|
1972
|
+
body: JSON.stringify({
|
|
1973
|
+
tokenIn: intent.tokenIn,
|
|
1974
|
+
tokenOut: intent.tokenOut,
|
|
1975
|
+
amountIn: intent.amountIn.toString(),
|
|
1976
|
+
slippageBps: intent.maxSlippageBps ?? 50,
|
|
1977
|
+
taker: this.account.address
|
|
1978
|
+
})
|
|
1979
|
+
});
|
|
1980
|
+
if (!response.ok) {
|
|
1981
|
+
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
1982
|
+
throw new Error(`Failed to get route: ${error.error}`);
|
|
1983
|
+
}
|
|
1984
|
+
return response.json();
|
|
1985
|
+
}
|
|
1986
|
+
/**
|
|
1987
|
+
* Get price quote without transaction data (faster)
|
|
1988
|
+
*/
|
|
1989
|
+
async getPrice(tokenIn, tokenOut, amountIn) {
|
|
1990
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
1991
|
+
const response = await fetch(`${apiUrl}/v1/routes/price`, {
|
|
1992
|
+
method: "POST",
|
|
1993
|
+
headers: {
|
|
1994
|
+
"Content-Type": "application/json",
|
|
1995
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
1996
|
+
},
|
|
1997
|
+
body: JSON.stringify({
|
|
1998
|
+
tokenIn,
|
|
1999
|
+
tokenOut,
|
|
2000
|
+
amountIn: amountIn.toString()
|
|
2001
|
+
})
|
|
2002
|
+
});
|
|
2003
|
+
if (!response.ok) {
|
|
2004
|
+
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
2005
|
+
throw new Error(`Failed to get price: ${error.error}`);
|
|
2006
|
+
}
|
|
2007
|
+
return response.json();
|
|
2008
|
+
}
|
|
2009
|
+
/**
|
|
2010
|
+
* Execute a trade using a pre-fetched route
|
|
2011
|
+
* @param route Route quote from getRoute()
|
|
2012
|
+
* @param options Trade execution options
|
|
2013
|
+
* @returns Transaction hash
|
|
2014
|
+
*/
|
|
2015
|
+
async executeTrade(route, options) {
|
|
2016
|
+
if (route.validUntil && Date.now() > route.validUntil) {
|
|
2017
|
+
throw new Error("Quote expired, please fetch a new route");
|
|
2018
|
+
}
|
|
2019
|
+
if (route.issues?.allowance) {
|
|
2020
|
+
throw new Error(
|
|
2021
|
+
`Insufficient allowance for ${route.issues.allowance.spender}. Need ${route.issues.allowance.expected}, have ${route.issues.allowance.actual}`
|
|
2022
|
+
);
|
|
2023
|
+
}
|
|
2024
|
+
if (options?.validateSlippage !== false) {
|
|
2025
|
+
const freshPrice = await this.getPrice(
|
|
2026
|
+
route.tokenIn,
|
|
2027
|
+
route.tokenOut,
|
|
2028
|
+
BigInt(route.amountIn)
|
|
2029
|
+
);
|
|
2030
|
+
const quotedOutput = BigInt(route.amountOut);
|
|
2031
|
+
const freshOutput = BigInt(freshPrice.amountOut);
|
|
2032
|
+
if (freshOutput < quotedOutput * 99n / 100n) {
|
|
2033
|
+
console.warn("Price has moved significantly since quote was fetched");
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
const hash = await this.walletClient.sendTransaction({
|
|
2037
|
+
account: this.account,
|
|
2038
|
+
chain: CHAIN_CONFIG[this.network],
|
|
2039
|
+
to: route.transaction.to,
|
|
2040
|
+
data: route.transaction.data,
|
|
2041
|
+
value: BigInt(route.transaction.value)
|
|
2042
|
+
});
|
|
2043
|
+
return hash;
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Get and execute a trade in one call
|
|
2047
|
+
* Convenience method that fetches route and executes
|
|
2048
|
+
*/
|
|
2049
|
+
async swap(intent) {
|
|
2050
|
+
const route = await this.getRoute(intent);
|
|
2051
|
+
const hash = await this.executeTrade(route);
|
|
2052
|
+
return { hash, route };
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Broadcast a trade intent to the network
|
|
2056
|
+
* Other agents can see this and potentially provide better execution
|
|
2057
|
+
*/
|
|
2058
|
+
async broadcastIntent(intent) {
|
|
2059
|
+
const agentId = await this.getAgentId();
|
|
2060
|
+
if (!agentId) throw new Error("Agent not registered");
|
|
2061
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2062
|
+
await fetch(`${apiUrl}/v1/intents`, {
|
|
2063
|
+
method: "POST",
|
|
2064
|
+
headers: {
|
|
2065
|
+
"Content-Type": "application/json",
|
|
2066
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
2067
|
+
},
|
|
2068
|
+
body: JSON.stringify({
|
|
2069
|
+
agentId: agentId.toString(),
|
|
2070
|
+
intent,
|
|
2071
|
+
signature: await this.signIntent(intent)
|
|
2072
|
+
})
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
// ============ Router Trading (Attributed) ============
|
|
2076
|
+
/**
|
|
2077
|
+
* Execute a trade through ExagentRouter for full attribution
|
|
2078
|
+
*
|
|
2079
|
+
* This is the recommended way for agents to trade. All trades routed through
|
|
2080
|
+
* the ExagentRouter emit TradeExecuted events that are captured by the indexer
|
|
2081
|
+
* for accurate performance tracking and rankings.
|
|
2082
|
+
*
|
|
2083
|
+
* @param intent Trade parameters
|
|
2084
|
+
* @returns Trade result with transaction hash
|
|
2085
|
+
*
|
|
2086
|
+
* @example
|
|
2087
|
+
* ```typescript
|
|
2088
|
+
* const result = await exagent.trade({
|
|
2089
|
+
* tokenIn: WETH,
|
|
2090
|
+
* tokenOut: USDC,
|
|
2091
|
+
* amountIn: parseEther('1'),
|
|
2092
|
+
* maxSlippageBps: 50,
|
|
2093
|
+
* });
|
|
2094
|
+
* console.log('Trade submitted:', result.hash);
|
|
2095
|
+
* ```
|
|
2096
|
+
*/
|
|
2097
|
+
async trade(intent) {
|
|
2098
|
+
const agentId = await this.getAgentId();
|
|
2099
|
+
if (!agentId) throw new Error("Agent not registered");
|
|
2100
|
+
const routerTrade = await this.buildRouterTrade(intent, agentId);
|
|
2101
|
+
for (const approval of routerTrade.approvals) {
|
|
2102
|
+
await this.approveToken(
|
|
2103
|
+
approval.token,
|
|
2104
|
+
approval.spender,
|
|
2105
|
+
BigInt(approval.amount)
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
const hash = await this.walletClient.sendTransaction({
|
|
2109
|
+
account: this.account,
|
|
2110
|
+
chain: CHAIN_CONFIG[this.network],
|
|
2111
|
+
to: routerTrade.transaction.to,
|
|
2112
|
+
data: routerTrade.transaction.data,
|
|
2113
|
+
value: BigInt(routerTrade.transaction.value)
|
|
2114
|
+
});
|
|
2115
|
+
return {
|
|
2116
|
+
hash,
|
|
2117
|
+
agentId,
|
|
2118
|
+
tokenIn: routerTrade.tokenIn,
|
|
2119
|
+
tokenOut: routerTrade.tokenOut,
|
|
2120
|
+
amountIn: routerTrade.amountIn,
|
|
2121
|
+
expectedAmountOut: routerTrade.amountOut
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
/**
|
|
2125
|
+
* Build a router trade transaction without executing
|
|
2126
|
+
* Useful for simulation or multi-step workflows
|
|
2127
|
+
*/
|
|
2128
|
+
async buildRouterTrade(intent, agentId) {
|
|
2129
|
+
const id = agentId ?? await this.getAgentId();
|
|
2130
|
+
if (!id) throw new Error("Agent not registered");
|
|
2131
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2132
|
+
const response = await fetch(`${apiUrl}/v1/router/trade`, {
|
|
2133
|
+
method: "POST",
|
|
2134
|
+
headers: {
|
|
2135
|
+
"Content-Type": "application/json",
|
|
2136
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
2137
|
+
},
|
|
2138
|
+
body: JSON.stringify({
|
|
2139
|
+
agentId: id.toString(),
|
|
2140
|
+
tokenIn: intent.tokenIn,
|
|
2141
|
+
tokenOut: intent.tokenOut,
|
|
2142
|
+
amountIn: intent.amountIn.toString(),
|
|
2143
|
+
slippageBps: intent.maxSlippageBps ?? 50,
|
|
2144
|
+
taker: this.account.address
|
|
2145
|
+
})
|
|
2146
|
+
});
|
|
2147
|
+
if (!response.ok) {
|
|
2148
|
+
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
2149
|
+
throw new Error(`Failed to build trade: ${error.error || error.message}`);
|
|
2150
|
+
}
|
|
2151
|
+
return response.json();
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Approve token spending for router
|
|
2155
|
+
*/
|
|
2156
|
+
async approveToken(token, spender, amount) {
|
|
2157
|
+
const approveData = this.encodeApprove(spender, amount);
|
|
2158
|
+
const hash = await this.walletClient.sendTransaction({
|
|
2159
|
+
account: this.account,
|
|
2160
|
+
chain: CHAIN_CONFIG[this.network],
|
|
2161
|
+
to: token,
|
|
2162
|
+
data: approveData
|
|
2163
|
+
});
|
|
2164
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
2165
|
+
return hash;
|
|
2166
|
+
}
|
|
2167
|
+
/**
|
|
2168
|
+
* Encode ERC20 approve calldata
|
|
2169
|
+
*/
|
|
2170
|
+
encodeApprove(spender, amount) {
|
|
2171
|
+
const selector = "0x095ea7b3";
|
|
2172
|
+
const paddedSpender = spender.slice(2).padStart(64, "0");
|
|
2173
|
+
const paddedAmount = amount.toString(16).padStart(64, "0");
|
|
2174
|
+
return `${selector}${paddedSpender}${paddedAmount}`;
|
|
2175
|
+
}
|
|
2176
|
+
// ============ M2M Services ============
|
|
2177
|
+
/**
|
|
2178
|
+
* Request a service from another agent
|
|
2179
|
+
*/
|
|
2180
|
+
async requestService(request) {
|
|
2181
|
+
const agentId = await this.getAgentId();
|
|
2182
|
+
if (!agentId) throw new Error("Agent not registered");
|
|
2183
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2184
|
+
const response = await fetch(`${apiUrl}/v1/services/request`, {
|
|
2185
|
+
method: "POST",
|
|
2186
|
+
headers: {
|
|
2187
|
+
"Content-Type": "application/json",
|
|
2188
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
2189
|
+
},
|
|
2190
|
+
body: JSON.stringify({
|
|
2191
|
+
requesterId: agentId.toString(),
|
|
2192
|
+
...request
|
|
2193
|
+
})
|
|
2194
|
+
});
|
|
2195
|
+
if (!response.ok) {
|
|
2196
|
+
throw new Error(`Failed to request service: ${response.statusText}`);
|
|
2197
|
+
}
|
|
2198
|
+
return response.json();
|
|
2199
|
+
}
|
|
2200
|
+
/**
|
|
2201
|
+
* List available services
|
|
2202
|
+
*/
|
|
2203
|
+
async listServices(serviceType) {
|
|
2204
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2205
|
+
const params = new URLSearchParams();
|
|
2206
|
+
if (serviceType) params.set("type", serviceType);
|
|
2207
|
+
const response = await fetch(`${apiUrl}/v1/services?${params}`);
|
|
2208
|
+
return response.json();
|
|
2209
|
+
}
|
|
2210
|
+
// ============ Leaderboards ============
|
|
2211
|
+
/**
|
|
2212
|
+
* Get agent leaderboard
|
|
2213
|
+
*/
|
|
2214
|
+
async getLeaderboard(options) {
|
|
2215
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2216
|
+
const params = new URLSearchParams();
|
|
2217
|
+
if (options?.category) params.set("category", options.category);
|
|
2218
|
+
if (options?.limit) params.set("limit", options.limit.toString());
|
|
2219
|
+
if (options?.offset) params.set("offset", options.offset.toString());
|
|
2220
|
+
const response = await fetch(`${apiUrl}/v1/rankings/leaderboard?${params}`);
|
|
2221
|
+
return response.json();
|
|
2222
|
+
}
|
|
2223
|
+
// ============ Vault Functions (Phase 4: Copy Trading) ============
|
|
2224
|
+
/**
|
|
2225
|
+
* Get a vault interface for interacting with an ExagentVault
|
|
2226
|
+
* @param vaultAddress The vault contract address
|
|
2227
|
+
* @returns ExagentVault instance
|
|
2228
|
+
*/
|
|
2229
|
+
getVault(vaultAddress) {
|
|
2230
|
+
return new ExagentVault(
|
|
2231
|
+
vaultAddress,
|
|
2232
|
+
this.publicClient,
|
|
2233
|
+
this.walletClient,
|
|
2234
|
+
CHAIN_CONFIG[this.network],
|
|
2235
|
+
this.account
|
|
2236
|
+
);
|
|
2237
|
+
}
|
|
2238
|
+
/**
|
|
2239
|
+
* List all vaults for a given agent
|
|
2240
|
+
* @param agentId Agent ID to get vaults for
|
|
2241
|
+
* @returns Array of vault summaries
|
|
2242
|
+
*/
|
|
2243
|
+
async getAgentVaults(agentId) {
|
|
2244
|
+
const id = agentId ?? await this.getAgentId();
|
|
2245
|
+
if (!id) throw new Error("Agent not registered");
|
|
2246
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2247
|
+
const response = await fetch(`${apiUrl}/v1/vaults?agentId=${id}`, {
|
|
2248
|
+
headers: {
|
|
2249
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
2250
|
+
}
|
|
2251
|
+
});
|
|
2252
|
+
if (!response.ok) {
|
|
2253
|
+
throw new Error(`Failed to get vaults: ${response.statusText}`);
|
|
2254
|
+
}
|
|
2255
|
+
return response.json();
|
|
2256
|
+
}
|
|
2257
|
+
/**
|
|
2258
|
+
* List all vaults the user has positions in
|
|
2259
|
+
* @param userAddress User address (defaults to connected wallet)
|
|
2260
|
+
* @returns Array of vault summaries with user position info
|
|
2261
|
+
*/
|
|
2262
|
+
async getUserVaults(userAddress) {
|
|
2263
|
+
const user = userAddress ?? this.account.address;
|
|
2264
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2265
|
+
const response = await fetch(`${apiUrl}/v1/vaults?depositor=${user}`, {
|
|
2266
|
+
headers: {
|
|
2267
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
if (!response.ok) {
|
|
2271
|
+
throw new Error(`Failed to get user vaults: ${response.statusText}`);
|
|
2272
|
+
}
|
|
2273
|
+
return response.json();
|
|
2274
|
+
}
|
|
2275
|
+
/**
|
|
2276
|
+
* List top performing vaults
|
|
2277
|
+
* @param options Filter options
|
|
2278
|
+
* @returns Array of vault summaries sorted by performance
|
|
2279
|
+
*/
|
|
2280
|
+
async getTopVaults(options) {
|
|
2281
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2282
|
+
const params = new URLSearchParams();
|
|
2283
|
+
if (options?.asset) params.set("asset", options.asset);
|
|
2284
|
+
if (options?.minTvl) params.set("minTvl", options.minTvl.toString());
|
|
2285
|
+
if (options?.period) params.set("period", options.period);
|
|
2286
|
+
if (options?.limit) params.set("limit", options.limit.toString());
|
|
2287
|
+
const response = await fetch(`${apiUrl}/v1/vaults/top?${params}`, {
|
|
2288
|
+
headers: {
|
|
2289
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
2290
|
+
}
|
|
2291
|
+
});
|
|
2292
|
+
if (!response.ok) {
|
|
2293
|
+
throw new Error(`Failed to get top vaults: ${response.statusText}`);
|
|
2294
|
+
}
|
|
2295
|
+
return response.json();
|
|
2296
|
+
}
|
|
2297
|
+
/**
|
|
2298
|
+
* Deposit into a vault
|
|
2299
|
+
* @param vaultAddress Vault contract address
|
|
2300
|
+
* @param amount Amount of underlying asset to deposit
|
|
2301
|
+
* @returns Transaction hash
|
|
2302
|
+
*/
|
|
2303
|
+
async depositToVault(vaultAddress, amount) {
|
|
2304
|
+
const vault = this.getVault(vaultAddress);
|
|
2305
|
+
const info = await vault.getVaultInfo();
|
|
2306
|
+
await vault.approveAsset(info.asset, amount);
|
|
2307
|
+
return vault.deposit(amount);
|
|
2308
|
+
}
|
|
2309
|
+
/**
|
|
2310
|
+
* Withdraw from a vault
|
|
2311
|
+
* @param vaultAddress Vault contract address
|
|
2312
|
+
* @param assets Amount of underlying asset to withdraw
|
|
2313
|
+
* @returns Transaction hash
|
|
2314
|
+
*/
|
|
2315
|
+
async withdrawFromVault(vaultAddress, assets) {
|
|
2316
|
+
const vault = this.getVault(vaultAddress);
|
|
2317
|
+
return vault.withdraw(assets);
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Redeem shares from a vault
|
|
2321
|
+
* @param vaultAddress Vault contract address
|
|
2322
|
+
* @param shares Amount of shares to redeem
|
|
2323
|
+
* @returns Transaction hash
|
|
2324
|
+
*/
|
|
2325
|
+
async redeemFromVault(vaultAddress, shares) {
|
|
2326
|
+
const vault = this.getVault(vaultAddress);
|
|
2327
|
+
return vault.redeem(shares);
|
|
2328
|
+
}
|
|
2329
|
+
// ============ Staking Functions ============
|
|
2330
|
+
/**
|
|
2331
|
+
* Stake EXA tokens to receive vEXA voting power
|
|
2332
|
+
* @param amount Amount of EXA to stake (in wei)
|
|
2333
|
+
* @param lockDuration Lock duration in seconds (30 days to 2 years)
|
|
2334
|
+
* @returns Transaction hash
|
|
2335
|
+
*
|
|
2336
|
+
* @example
|
|
2337
|
+
* ```typescript
|
|
2338
|
+
* // Stake 1000 EXA for 6 months
|
|
2339
|
+
* const tx = await exagent.stakeExa(
|
|
2340
|
+
* parseEther('1000'),
|
|
2341
|
+
* ExagentStaking.LOCK_6_MONTHS
|
|
2342
|
+
* );
|
|
2343
|
+
* ```
|
|
2344
|
+
*/
|
|
2345
|
+
async stakeExa(amount, lockDuration) {
|
|
2346
|
+
await this.staking.approveExa(amount);
|
|
2347
|
+
return this.staking.stake(amount, lockDuration);
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Unstake EXA tokens after lock expires
|
|
2351
|
+
* @returns Transaction hash
|
|
2352
|
+
*/
|
|
2353
|
+
async unstakeExa() {
|
|
2354
|
+
return this.staking.unstake();
|
|
2355
|
+
}
|
|
2356
|
+
/**
|
|
2357
|
+
* Get staking info for the connected wallet
|
|
2358
|
+
* @returns Staking info including amount, unlock time, and vEXA balance
|
|
2359
|
+
*/
|
|
2360
|
+
async getStakingInfo() {
|
|
2361
|
+
return this.staking.getStakeInfo();
|
|
2362
|
+
}
|
|
2363
|
+
/**
|
|
2364
|
+
* Get current vEXA balance for the connected wallet
|
|
2365
|
+
* @returns Current vEXA balance (with time decay applied)
|
|
2366
|
+
*/
|
|
2367
|
+
async getVeEXABalance() {
|
|
2368
|
+
return this.staking.getVeEXABalance();
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* Delegate vEXA voting power to an agent
|
|
2372
|
+
* @param agentId The agent ID to delegate to
|
|
2373
|
+
* @returns Transaction hash
|
|
2374
|
+
*/
|
|
2375
|
+
async delegateToAgent(agentId) {
|
|
2376
|
+
return this.staking.delegate(agentId);
|
|
2377
|
+
}
|
|
2378
|
+
/**
|
|
2379
|
+
* Get delegation stats for an agent
|
|
2380
|
+
* @param agentId The agent ID to check
|
|
2381
|
+
* @returns Delegation info with total vEXA and delegator count
|
|
2382
|
+
*/
|
|
2383
|
+
async getAgentDelegation(agentId) {
|
|
2384
|
+
return this.staking.getAgentDelegation(agentId);
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Claim all pending staking rewards (EXA + multi-token)
|
|
2388
|
+
* @returns Transaction hashes for EXA and multi-token claims
|
|
2389
|
+
*/
|
|
2390
|
+
async claimAllRewards() {
|
|
2391
|
+
const exaRewards = await this.staking.claimRewards();
|
|
2392
|
+
const multiTokenRewards = await this.staking.claimRewardsMulti();
|
|
2393
|
+
return { exaRewards, multiTokenRewards };
|
|
2394
|
+
}
|
|
2395
|
+
// ============ Helpers ============
|
|
2396
|
+
/**
|
|
2397
|
+
* Get the agent's wallet address
|
|
2398
|
+
*/
|
|
2399
|
+
get address() {
|
|
2400
|
+
return this.account.address;
|
|
2401
|
+
}
|
|
2402
|
+
async uploadMetadata(metadata) {
|
|
2403
|
+
const apiUrl = EXAGENT_API_CONFIG[this.network];
|
|
2404
|
+
const response = await fetch(`${apiUrl}/v1/metadata/upload`, {
|
|
2405
|
+
method: "POST",
|
|
2406
|
+
headers: {
|
|
2407
|
+
"Content-Type": "application/json",
|
|
2408
|
+
...this.apiKey && { "X-API-Key": this.apiKey }
|
|
2409
|
+
},
|
|
2410
|
+
body: JSON.stringify(metadata)
|
|
2411
|
+
});
|
|
2412
|
+
if (!response.ok) {
|
|
2413
|
+
throw new Error(`Failed to upload metadata: ${response.statusText}`);
|
|
2414
|
+
}
|
|
2415
|
+
const result = await response.json();
|
|
2416
|
+
return result.uri;
|
|
2417
|
+
}
|
|
2418
|
+
async signIntent(intent) {
|
|
2419
|
+
const message = JSON.stringify(intent);
|
|
2420
|
+
return this.walletClient.signMessage({ account: this.account, message });
|
|
2421
|
+
}
|
|
2422
|
+
parseAgentIdFromReceipt(receipt) {
|
|
2423
|
+
return 1n;
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
|
|
2427
|
+
// src/contracts/vault-factory.ts
|
|
2428
|
+
var EXAGENT_VAULT_FACTORY_ABI = [
|
|
2429
|
+
// Read functions
|
|
2430
|
+
{ type: "function", name: "owner", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
|
|
2431
|
+
{ type: "function", name: "registry", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
|
|
2432
|
+
{ type: "function", name: "router", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
|
|
2433
|
+
{ type: "function", name: "minimumVeEXARequired", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
2434
|
+
{ type: "function", name: "eXABurnFee", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
2435
|
+
{ type: "function", name: "allowedAssets", inputs: [{ name: "asset", type: "address" }], outputs: [{ type: "bool" }], stateMutability: "view" },
|
|
2436
|
+
{ type: "function", name: "vaults", inputs: [{ name: "agentId", type: "uint256" }, { name: "asset", type: "address" }], outputs: [{ type: "address" }], stateMutability: "view" },
|
|
2437
|
+
{ type: "function", name: "agentVaultCount", inputs: [{ name: "agentId", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
2438
|
+
{
|
|
2439
|
+
type: "function",
|
|
2440
|
+
name: "canCreateVault",
|
|
2441
|
+
inputs: [{ name: "creator", type: "address" }],
|
|
2442
|
+
outputs: [{ name: "canCreate", type: "bool" }, { name: "reason", type: "string" }],
|
|
2443
|
+
stateMutability: "view"
|
|
2444
|
+
},
|
|
2445
|
+
{
|
|
2446
|
+
type: "function",
|
|
2447
|
+
name: "getVaultCreationRequirements",
|
|
2448
|
+
inputs: [],
|
|
2449
|
+
outputs: [
|
|
2450
|
+
{ name: "veXARequired", type: "uint256" },
|
|
2451
|
+
{ name: "burnFee", type: "uint256" },
|
|
2452
|
+
{ name: "stakingContract", type: "address" },
|
|
2453
|
+
{ name: "exaToken", type: "address" }
|
|
2454
|
+
],
|
|
2455
|
+
stateMutability: "view"
|
|
2456
|
+
},
|
|
2457
|
+
// Write functions
|
|
2458
|
+
{
|
|
2459
|
+
type: "function",
|
|
2460
|
+
name: "createVault",
|
|
2461
|
+
inputs: [
|
|
2462
|
+
{ name: "agentId", type: "uint256" },
|
|
2463
|
+
{ name: "asset", type: "address" },
|
|
2464
|
+
{ name: "name", type: "string" },
|
|
2465
|
+
{ name: "symbol", type: "string" },
|
|
2466
|
+
{ name: "feeRecipient", type: "address" }
|
|
2467
|
+
],
|
|
2468
|
+
outputs: [{ type: "address" }],
|
|
2469
|
+
stateMutability: "nonpayable"
|
|
2470
|
+
},
|
|
2471
|
+
// Events
|
|
2472
|
+
{
|
|
2473
|
+
type: "event",
|
|
2474
|
+
name: "VaultCreated",
|
|
2475
|
+
inputs: [
|
|
2476
|
+
{ name: "vault", type: "address", indexed: true },
|
|
2477
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
2478
|
+
{ name: "asset", type: "address", indexed: true },
|
|
2479
|
+
{ name: "creator", type: "address", indexed: false },
|
|
2480
|
+
{ name: "name", type: "string", indexed: false },
|
|
2481
|
+
{ name: "symbol", type: "string", indexed: false }
|
|
2482
|
+
]
|
|
2483
|
+
},
|
|
2484
|
+
{
|
|
2485
|
+
type: "event",
|
|
2486
|
+
name: "AllowedAssetUpdated",
|
|
2487
|
+
inputs: [
|
|
2488
|
+
{ name: "asset", type: "address", indexed: true },
|
|
2489
|
+
{ name: "allowed", type: "bool", indexed: false }
|
|
2490
|
+
]
|
|
2491
|
+
}
|
|
2492
|
+
];
|
|
2493
|
+
var ExagentVaultFactory = class {
|
|
2494
|
+
address;
|
|
2495
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2496
|
+
publicClient;
|
|
2497
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2498
|
+
walletClient;
|
|
2499
|
+
chain;
|
|
2500
|
+
account;
|
|
2501
|
+
constructor(factoryAddress, publicClient, walletClient, chain, account) {
|
|
2502
|
+
this.address = factoryAddress;
|
|
2503
|
+
this.publicClient = publicClient;
|
|
2504
|
+
this.walletClient = walletClient;
|
|
2505
|
+
this.chain = chain;
|
|
2506
|
+
this.account = account;
|
|
2507
|
+
}
|
|
2508
|
+
// ============ Read Functions ============
|
|
2509
|
+
/**
|
|
2510
|
+
* Get vault creation requirements
|
|
2511
|
+
*/
|
|
2512
|
+
async getRequirements() {
|
|
2513
|
+
try {
|
|
2514
|
+
const result = await this.publicClient.readContract({
|
|
2515
|
+
address: this.address,
|
|
2516
|
+
abi: EXAGENT_VAULT_FACTORY_ABI,
|
|
2517
|
+
functionName: "getVaultCreationRequirements"
|
|
2518
|
+
});
|
|
2519
|
+
const isBypassed = result[0] === BigInt(0) && result[1] === BigInt(0) && (result[2] === "0x0000000000000000000000000000000000000000" || result[3] === "0x0000000000000000000000000000000000000000");
|
|
2520
|
+
return {
|
|
2521
|
+
veXARequired: result[0],
|
|
2522
|
+
burnFee: result[1],
|
|
2523
|
+
stakingContract: result[2],
|
|
2524
|
+
exaToken: result[3],
|
|
2525
|
+
isBypassed
|
|
2526
|
+
};
|
|
2527
|
+
} catch {
|
|
2528
|
+
const [veXARequired, burnFee] = await Promise.all([
|
|
2529
|
+
this.publicClient.readContract({
|
|
2530
|
+
address: this.address,
|
|
2531
|
+
abi: EXAGENT_VAULT_FACTORY_ABI,
|
|
2532
|
+
functionName: "minimumVeEXARequired"
|
|
2533
|
+
}),
|
|
2534
|
+
this.publicClient.readContract({
|
|
2535
|
+
address: this.address,
|
|
2536
|
+
abi: EXAGENT_VAULT_FACTORY_ABI,
|
|
2537
|
+
functionName: "eXABurnFee"
|
|
2538
|
+
})
|
|
2539
|
+
]);
|
|
2540
|
+
const isBypassed = veXARequired === BigInt(0) && burnFee === BigInt(0);
|
|
2541
|
+
return {
|
|
2542
|
+
veXARequired,
|
|
2543
|
+
burnFee,
|
|
2544
|
+
stakingContract: "0x0000000000000000000000000000000000000000",
|
|
2545
|
+
exaToken: "0x0000000000000000000000000000000000000000",
|
|
2546
|
+
isBypassed
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
/**
|
|
2551
|
+
* Check if an address can create a vault
|
|
2552
|
+
* @param creator Address to check
|
|
2553
|
+
*/
|
|
2554
|
+
async canCreateVault(creator) {
|
|
2555
|
+
const result = await this.publicClient.readContract({
|
|
2556
|
+
address: this.address,
|
|
2557
|
+
abi: EXAGENT_VAULT_FACTORY_ABI,
|
|
2558
|
+
functionName: "canCreateVault",
|
|
2559
|
+
args: [creator]
|
|
2560
|
+
});
|
|
2561
|
+
return {
|
|
2562
|
+
canCreate: result[0],
|
|
2563
|
+
reason: result[1]
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
/**
|
|
2567
|
+
* Check if an asset is whitelisted for vault creation
|
|
2568
|
+
* @param asset Asset address to check
|
|
2569
|
+
*/
|
|
2570
|
+
async isAssetAllowed(asset) {
|
|
2571
|
+
return this.publicClient.readContract({
|
|
2572
|
+
address: this.address,
|
|
2573
|
+
abi: EXAGENT_VAULT_FACTORY_ABI,
|
|
2574
|
+
functionName: "allowedAssets",
|
|
2575
|
+
args: [asset]
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
/**
|
|
2579
|
+
* Get existing vault for an agent and asset
|
|
2580
|
+
* @param agentId Agent ID
|
|
2581
|
+
* @param asset Asset address
|
|
2582
|
+
* @returns Vault address or zero address if none exists
|
|
2583
|
+
*/
|
|
2584
|
+
async getVault(agentId, asset) {
|
|
2585
|
+
return this.publicClient.readContract({
|
|
2586
|
+
address: this.address,
|
|
2587
|
+
abi: EXAGENT_VAULT_FACTORY_ABI,
|
|
2588
|
+
functionName: "vaults",
|
|
2589
|
+
args: [agentId, asset]
|
|
2590
|
+
});
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* Get count of vaults for an agent
|
|
2594
|
+
* @param agentId Agent ID
|
|
2595
|
+
*/
|
|
2596
|
+
async getAgentVaultCount(agentId) {
|
|
2597
|
+
return this.publicClient.readContract({
|
|
2598
|
+
address: this.address,
|
|
2599
|
+
abi: EXAGENT_VAULT_FACTORY_ABI,
|
|
2600
|
+
functionName: "agentVaultCount",
|
|
2601
|
+
args: [agentId]
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
/**
|
|
2605
|
+
* Check if agent already has a vault for an asset
|
|
2606
|
+
* @param agentId Agent ID
|
|
2607
|
+
* @param asset Asset address
|
|
2608
|
+
*/
|
|
2609
|
+
async hasVault(agentId, asset) {
|
|
2610
|
+
const vault = await this.getVault(agentId, asset);
|
|
2611
|
+
return vault !== "0x0000000000000000000000000000000000000000";
|
|
2612
|
+
}
|
|
2613
|
+
// ============ Write Functions ============
|
|
2614
|
+
/**
|
|
2615
|
+
* Create a new vault for an agent
|
|
2616
|
+
* @param options Vault creation options
|
|
2617
|
+
* @returns Transaction hash
|
|
2618
|
+
*/
|
|
2619
|
+
async createVault(options) {
|
|
2620
|
+
if (!this.walletClient || !this.account) {
|
|
2621
|
+
throw new Error("Wallet client required for vault creation");
|
|
2622
|
+
}
|
|
2623
|
+
const feeRecipient = options.feeRecipient ?? this.account.address;
|
|
2624
|
+
const hash = await this.walletClient.writeContract({
|
|
2625
|
+
address: this.address,
|
|
2626
|
+
abi: EXAGENT_VAULT_FACTORY_ABI,
|
|
2627
|
+
functionName: "createVault",
|
|
2628
|
+
args: [options.agentId, options.asset, options.name, options.symbol, feeRecipient],
|
|
2629
|
+
account: this.account,
|
|
2630
|
+
chain: this.chain
|
|
2631
|
+
});
|
|
2632
|
+
return hash;
|
|
2633
|
+
}
|
|
2634
|
+
// ============ Convenience Methods ============
|
|
2635
|
+
/**
|
|
2636
|
+
* Check all requirements and create a vault if possible
|
|
2637
|
+
* Returns detailed status about each requirement
|
|
2638
|
+
*/
|
|
2639
|
+
async checkAndCreateVault(agentId, asset, name, symbol, feeRecipient) {
|
|
2640
|
+
const [canCreateResult, assetAllowed, hasExisting] = await Promise.all([
|
|
2641
|
+
this.canCreateVault(this.account?.address ?? "0x0000000000000000000000000000000000000000"),
|
|
2642
|
+
this.isAssetAllowed(asset),
|
|
2643
|
+
this.hasVault(agentId, asset)
|
|
2644
|
+
]);
|
|
2645
|
+
const checks = {
|
|
2646
|
+
canCreate: canCreateResult.canCreate,
|
|
2647
|
+
canCreateReason: canCreateResult.reason,
|
|
2648
|
+
assetAllowed,
|
|
2649
|
+
hasExistingVault: hasExisting
|
|
2650
|
+
};
|
|
2651
|
+
if (!canCreateResult.canCreate) {
|
|
2652
|
+
return {
|
|
2653
|
+
success: false,
|
|
2654
|
+
error: `Cannot create vault: ${canCreateResult.reason}`,
|
|
2655
|
+
checks
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
if (!assetAllowed) {
|
|
2659
|
+
return {
|
|
2660
|
+
success: false,
|
|
2661
|
+
error: `Asset ${asset} is not whitelisted for vault creation`,
|
|
2662
|
+
checks
|
|
2663
|
+
};
|
|
2664
|
+
}
|
|
2665
|
+
if (hasExisting) {
|
|
2666
|
+
const existingVault = await this.getVault(agentId, asset);
|
|
2667
|
+
return {
|
|
2668
|
+
success: false,
|
|
2669
|
+
error: `Vault already exists for this agent and asset`,
|
|
2670
|
+
vaultAddress: existingVault,
|
|
2671
|
+
checks
|
|
2672
|
+
};
|
|
2673
|
+
}
|
|
2674
|
+
try {
|
|
2675
|
+
const txHash = await this.createVault({
|
|
2676
|
+
agentId,
|
|
2677
|
+
asset,
|
|
2678
|
+
name,
|
|
2679
|
+
symbol,
|
|
2680
|
+
feeRecipient
|
|
2681
|
+
});
|
|
2682
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
2683
|
+
if (receipt.status !== "success") {
|
|
2684
|
+
return {
|
|
2685
|
+
success: false,
|
|
2686
|
+
error: "Transaction failed",
|
|
2687
|
+
txHash,
|
|
2688
|
+
checks
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
const vaultAddress = await this.getVault(agentId, asset);
|
|
2692
|
+
return {
|
|
2693
|
+
success: true,
|
|
2694
|
+
vaultAddress,
|
|
2695
|
+
txHash,
|
|
2696
|
+
checks
|
|
2697
|
+
};
|
|
2698
|
+
} catch (error) {
|
|
2699
|
+
return {
|
|
2700
|
+
success: false,
|
|
2701
|
+
error: error instanceof Error ? error.message : "Unknown error creating vault",
|
|
2702
|
+
checks
|
|
2703
|
+
};
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
};
|
|
2707
|
+
|
|
2708
|
+
// src/index.ts
|
|
2709
|
+
var TESTNET_ADDRESSES = {
|
|
2710
|
+
registry: "0xCF48C341e3FebeCA5ECB7eb2535f61A2Ba855d9C",
|
|
2711
|
+
// V4 UUPS proxy
|
|
2712
|
+
token: "0x66c39b0ad96B3f5eE198Fef913c6636353a48A87",
|
|
2713
|
+
staking: "0x439441468e1b1b616E9D36b80969C241F261A011",
|
|
2714
|
+
router: "0xc0c27eEE047E414CD716D06C2444CF2073113d5C",
|
|
2715
|
+
// V3 with config epochs
|
|
2716
|
+
vaultFactory: "0x5c099daaE33801a907Bb57011c6749655b55dc75",
|
|
2717
|
+
feeCollector: "0xcB57b03a50df054b9C738Df1324C17A4fDe4fe46",
|
|
2718
|
+
buyback: "0x35cdEa810A130A846265682e5c71A68A507aB895",
|
|
2719
|
+
serviceEscrow: "0x74a3496b148DEE735ac388299aF9Ac2F7C4EdCBf",
|
|
2720
|
+
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
2721
|
+
// Circle's Base Sepolia USDC (only vault asset)
|
|
2722
|
+
};
|
|
2723
|
+
export {
|
|
2724
|
+
CHAIN_CONFIG,
|
|
2725
|
+
CONTRACT_ADDRESSES,
|
|
2726
|
+
DEX_ADDRESSES,
|
|
2727
|
+
DISCOUNT_TIERS,
|
|
2728
|
+
EXAGENT_API_CONFIG,
|
|
2729
|
+
EXAGENT_STAKING_ABI,
|
|
2730
|
+
EXAGENT_VAULT_FACTORY_ABI,
|
|
2731
|
+
ExagentClient,
|
|
2732
|
+
ExagentRegistry,
|
|
2733
|
+
ExagentStaking,
|
|
2734
|
+
ExagentVault,
|
|
2735
|
+
ExagentVaultFactory,
|
|
2736
|
+
TESTNET_ADDRESSES,
|
|
2737
|
+
ZERO_X_CONFIG
|
|
2738
|
+
};
|