@exagent/sdk 0.1.21 → 0.2.1

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.mjs DELETED
@@ -1,2136 +0,0 @@
1
- // src/client.ts
2
- import {
3
- createPublicClient,
4
- createWalletClient,
5
- http,
6
- decodeErrorResult,
7
- maxUint256,
8
- keccak256 as keccak2562,
9
- toHex
10
- } from "viem";
11
- import { privateKeyToAccount } from "viem/accounts";
12
-
13
- // src/contracts/registry.ts
14
- import {
15
- encodePacked,
16
- keccak256
17
- } from "viem";
18
- var EXAGENT_REGISTRY_ABI = [
19
- {
20
- type: "function",
21
- name: "registerAgent",
22
- inputs: [
23
- { name: "name", type: "string" },
24
- { name: "metadataURI", type: "string" },
25
- { name: "riskUniverse", type: "uint8" },
26
- { name: "maxPositionSizeBps", type: "uint256" },
27
- { name: "maxDailyLossBps", type: "uint256" }
28
- ],
29
- outputs: [{ name: "agentId", type: "uint256" }],
30
- stateMutability: "nonpayable"
31
- },
32
- {
33
- type: "function",
34
- name: "isNameAvailable",
35
- inputs: [{ name: "name", type: "string" }],
36
- outputs: [{ name: "available", type: "bool" }],
37
- stateMutability: "view"
38
- },
39
- {
40
- type: "function",
41
- name: "getAgentByName",
42
- inputs: [{ name: "name", type: "string" }],
43
- outputs: [{ name: "agentId", type: "uint256" }],
44
- stateMutability: "view"
45
- },
46
- {
47
- type: "function",
48
- name: "linkWallet",
49
- inputs: [
50
- { name: "agentId", type: "uint256" },
51
- { name: "wallet", type: "address" },
52
- { name: "signature", type: "bytes" }
53
- ],
54
- outputs: [],
55
- stateMutability: "nonpayable"
56
- },
57
- {
58
- type: "function",
59
- name: "linkOwnWallet",
60
- inputs: [{ name: "agentId", type: "uint256" }],
61
- outputs: [],
62
- stateMutability: "nonpayable"
63
- },
64
- {
65
- type: "function",
66
- name: "unlinkWallet",
67
- inputs: [
68
- { name: "agentId", type: "uint256" },
69
- { name: "wallet", type: "address" }
70
- ],
71
- outputs: [],
72
- stateMutability: "nonpayable"
73
- },
74
- {
75
- type: "function",
76
- name: "updateMetadata",
77
- inputs: [
78
- { name: "agentId", type: "uint256" },
79
- { name: "newURI", type: "string" }
80
- ],
81
- outputs: [],
82
- stateMutability: "nonpayable"
83
- },
84
- {
85
- type: "function",
86
- name: "agents",
87
- inputs: [{ name: "agentId", type: "uint256" }],
88
- outputs: [
89
- { name: "owner", type: "address" },
90
- { name: "name", type: "string" },
91
- { name: "metadataURI", type: "string" },
92
- { name: "registrationTime", type: "uint256" },
93
- { name: "verified", type: "bool" },
94
- { name: "linkedWalletCount", type: "uint256" }
95
- ],
96
- stateMutability: "view"
97
- },
98
- {
99
- type: "function",
100
- name: "getLinkedWallets",
101
- inputs: [{ name: "agentId", type: "uint256" }],
102
- outputs: [{ name: "", type: "address[]" }],
103
- stateMutability: "view"
104
- },
105
- {
106
- type: "function",
107
- name: "getAgentForWallet",
108
- inputs: [{ name: "wallet", type: "address" }],
109
- outputs: [{ name: "", type: "uint256" }],
110
- stateMutability: "view"
111
- },
112
- {
113
- type: "function",
114
- name: "walletToAgent",
115
- inputs: [{ name: "wallet", type: "address" }],
116
- outputs: [{ name: "", type: "uint256" }],
117
- stateMutability: "view"
118
- },
119
- {
120
- type: "function",
121
- name: "nonces",
122
- inputs: [{ name: "wallet", type: "address" }],
123
- outputs: [{ name: "", type: "uint256" }],
124
- stateMutability: "view"
125
- },
126
- {
127
- type: "function",
128
- name: "ownerOf",
129
- inputs: [{ name: "tokenId", type: "uint256" }],
130
- outputs: [{ name: "", type: "address" }],
131
- stateMutability: "view"
132
- },
133
- {
134
- type: "function",
135
- name: "balanceOf",
136
- inputs: [{ name: "owner", type: "address" }],
137
- outputs: [{ name: "", type: "uint256" }],
138
- stateMutability: "view"
139
- },
140
- {
141
- type: "event",
142
- name: "AgentRegistered",
143
- inputs: [
144
- { name: "agentId", type: "uint256", indexed: true },
145
- { name: "owner", type: "address", indexed: true },
146
- { name: "name", type: "string", indexed: false },
147
- { name: "metadataURI", type: "string", indexed: false }
148
- ]
149
- },
150
- {
151
- type: "event",
152
- name: "WalletLinked",
153
- inputs: [
154
- { name: "agentId", type: "uint256", indexed: true },
155
- { name: "wallet", type: "address", indexed: true }
156
- ]
157
- },
158
- {
159
- type: "event",
160
- name: "WalletUnlinked",
161
- inputs: [
162
- { name: "agentId", type: "uint256", indexed: true },
163
- { name: "wallet", type: "address", indexed: true }
164
- ]
165
- },
166
- // V4: One agent per wallet
167
- {
168
- type: "function",
169
- name: "ownerToAgentId",
170
- inputs: [{ name: "owner", type: "address" }],
171
- outputs: [{ name: "", type: "uint256" }],
172
- stateMutability: "view"
173
- },
174
- {
175
- type: "function",
176
- name: "getAgentByOwner",
177
- inputs: [{ name: "wallet", type: "address" }],
178
- outputs: [{ name: "agentId", type: "uint256" }],
179
- stateMutability: "view"
180
- },
181
- {
182
- type: "function",
183
- name: "canWalletRegister",
184
- inputs: [{ name: "wallet", type: "address" }],
185
- outputs: [
186
- { name: "canRegister", type: "bool" },
187
- { name: "existingAgentId", type: "uint256" }
188
- ],
189
- stateMutability: "view"
190
- },
191
- // Config Epochs
192
- {
193
- type: "function",
194
- name: "setConfig",
195
- inputs: [
196
- { name: "agentId", type: "uint256" },
197
- { name: "configHash", type: "bytes32" }
198
- ],
199
- outputs: [],
200
- stateMutability: "nonpayable"
201
- },
202
- {
203
- type: "function",
204
- name: "getConfigHash",
205
- inputs: [{ name: "agentId", type: "uint256" }],
206
- outputs: [{ name: "", type: "bytes32" }],
207
- stateMutability: "view"
208
- },
209
- // Mainnet: Agent retirement
210
- {
211
- type: "function",
212
- name: "retireAgent",
213
- inputs: [{ name: "agentId", type: "uint256" }],
214
- outputs: [],
215
- stateMutability: "nonpayable"
216
- },
217
- {
218
- type: "function",
219
- name: "retired",
220
- inputs: [{ name: "agentId", type: "uint256" }],
221
- outputs: [{ type: "bool" }],
222
- stateMutability: "view"
223
- },
224
- {
225
- type: "function",
226
- name: "isRetired",
227
- inputs: [{ name: "agentId", type: "uint256" }],
228
- outputs: [{ type: "bool" }],
229
- stateMutability: "view"
230
- },
231
- // Mainnet: Risk universe token whitelists
232
- {
233
- type: "function",
234
- name: "isTradeAllowed",
235
- inputs: [
236
- { name: "agentId", type: "uint256" },
237
- { name: "token", type: "address" },
238
- { name: "aggregator", type: "address" }
239
- ],
240
- outputs: [{ type: "bool" }],
241
- stateMutability: "view"
242
- },
243
- {
244
- type: "function",
245
- name: "getRiskUniverse",
246
- inputs: [{ name: "agentId", type: "uint256" }],
247
- outputs: [{ type: "uint8" }],
248
- stateMutability: "view"
249
- },
250
- {
251
- type: "function",
252
- name: "tradeCount",
253
- inputs: [{ name: "agentId", type: "uint256" }],
254
- outputs: [{ type: "uint256" }],
255
- stateMutability: "view"
256
- },
257
- // Events
258
- {
259
- type: "event",
260
- name: "AgentRetired",
261
- inputs: [
262
- { name: "agentId", type: "uint256", indexed: true }
263
- ]
264
- },
265
- {
266
- type: "event",
267
- name: "ConfigUpdated",
268
- inputs: [
269
- { name: "agentId", type: "uint256", indexed: true },
270
- { name: "oldConfigHash", type: "bytes32", indexed: false },
271
- { name: "newConfigHash", type: "bytes32", indexed: false },
272
- { name: "epochId", type: "uint256", indexed: false },
273
- { name: "blockNumber", type: "uint256", indexed: false }
274
- ]
275
- },
276
- // Custom errors — must match ExagentRegistry.sol for viem to decode reverts
277
- { type: "error", name: "AgentNotOwner", inputs: [] },
278
- { type: "error", name: "WalletAlreadyLinked", inputs: [] },
279
- { type: "error", name: "WalletNotLinked", inputs: [] },
280
- { type: "error", name: "InvalidSignature", inputs: [] },
281
- { type: "error", name: "TransferDisabled", inputs: [] },
282
- { type: "error", name: "InvalidMetadataURI", inputs: [] },
283
- { type: "error", name: "AgentDoesNotExist", inputs: [] },
284
- { type: "error", name: "NameAlreadyTaken", inputs: [] },
285
- { type: "error", name: "InvalidName", inputs: [] },
286
- { type: "error", name: "OwnerAlreadyHasAgent", inputs: [{ name: "existingAgentId", type: "uint256" }] },
287
- { type: "error", name: "InvalidRiskUniverse", inputs: [] },
288
- { type: "error", name: "InvalidTradingConfig", inputs: [] },
289
- { type: "error", name: "NotAuthorizedCaller", inputs: [] },
290
- { type: "error", name: "MetadataValueTooLarge", inputs: [] },
291
- { type: "error", name: "AgentIsRetired", inputs: [] },
292
- { type: "error", name: "InvalidRiskUniverseForWhitelist", inputs: [] }
293
- ];
294
- var ExagentRegistry = class {
295
- address;
296
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
297
- publicClient;
298
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
299
- walletClient;
300
- chain;
301
- account;
302
- constructor(address, publicClient, walletClient, chain, account) {
303
- this.address = address;
304
- this.publicClient = publicClient;
305
- this.walletClient = walletClient;
306
- this.chain = chain;
307
- this.account = account;
308
- }
309
- /**
310
- * Register a new agent
311
- * @param name Unique agent name (3-32 chars, alphanumeric + spaces/hyphens/underscores)
312
- * @param metadataURI IPFS URI for agent metadata
313
- * @param riskUniverse Risk tier: 0=Core, 1=Established, 2=Derivatives, 3=Emerging, 4=Frontier
314
- * @param maxPositionSizeBps Maximum position size in basis points (1-10000)
315
- * @param maxDailyLossBps Maximum daily loss in basis points (1-10000)
316
- * @returns Transaction hash
317
- */
318
- async register(name, metadataURI, riskUniverse = 1, maxPositionSizeBps = 1000n, maxDailyLossBps = 500n) {
319
- if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
320
- const hash = await this.walletClient.writeContract({
321
- account: this.account,
322
- chain: this.chain,
323
- address: this.address,
324
- abi: EXAGENT_REGISTRY_ABI,
325
- functionName: "registerAgent",
326
- args: [name, metadataURI, riskUniverse, maxPositionSizeBps, maxDailyLossBps]
327
- });
328
- return hash;
329
- }
330
- /**
331
- * Check if a name is available for registration
332
- * @param name The name to check
333
- * @returns True if the name can be used
334
- */
335
- async isNameAvailable(name) {
336
- const available = await this.publicClient.readContract({
337
- address: this.address,
338
- abi: EXAGENT_REGISTRY_ABI,
339
- functionName: "isNameAvailable",
340
- args: [name]
341
- });
342
- return available;
343
- }
344
- /**
345
- * Get the agent ID that owns a specific name
346
- * @param name The name to look up
347
- * @returns Agent ID (0 if not taken)
348
- */
349
- async getAgentByName(name) {
350
- const agentId = await this.publicClient.readContract({
351
- address: this.address,
352
- abi: EXAGENT_REGISTRY_ABI,
353
- functionName: "getAgentByName",
354
- args: [name]
355
- });
356
- return agentId;
357
- }
358
- /**
359
- * Link the wallet used by the agent to their agent ID
360
- * @param agentId The agent's ID
361
- * @returns Transaction hash
362
- */
363
- async linkOwnWallet(agentId) {
364
- if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
365
- const hash = await this.walletClient.writeContract({
366
- account: this.account,
367
- chain: this.chain,
368
- address: this.address,
369
- abi: EXAGENT_REGISTRY_ABI,
370
- functionName: "linkOwnWallet",
371
- args: [agentId]
372
- });
373
- return hash;
374
- }
375
- /**
376
- * Link an external wallet with signature proof
377
- * @param agentId The agent's ID
378
- * @param wallet The wallet to link
379
- * @param signature Signature from the wallet proving ownership
380
- * @returns Transaction hash
381
- */
382
- async linkWallet(agentId, wallet, signature) {
383
- if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
384
- const hash = await this.walletClient.writeContract({
385
- account: this.account,
386
- chain: this.chain,
387
- address: this.address,
388
- abi: EXAGENT_REGISTRY_ABI,
389
- functionName: "linkWallet",
390
- args: [agentId, wallet, signature]
391
- });
392
- return hash;
393
- }
394
- /**
395
- * Unlink a wallet from an agent
396
- * @param agentId The agent's ID
397
- * @param wallet The wallet to unlink
398
- * @returns Transaction hash
399
- */
400
- async unlinkWallet(agentId, wallet) {
401
- if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
402
- const hash = await this.walletClient.writeContract({
403
- account: this.account,
404
- chain: this.chain,
405
- address: this.address,
406
- abi: EXAGENT_REGISTRY_ABI,
407
- functionName: "unlinkWallet",
408
- args: [agentId, wallet]
409
- });
410
- return hash;
411
- }
412
- /**
413
- * Update agent metadata
414
- * @param agentId The agent's ID
415
- * @param newURI New IPFS URI for metadata
416
- * @returns Transaction hash
417
- */
418
- async updateMetadata(agentId, newURI) {
419
- if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
420
- const hash = await this.walletClient.writeContract({
421
- account: this.account,
422
- chain: this.chain,
423
- address: this.address,
424
- abi: EXAGENT_REGISTRY_ABI,
425
- functionName: "updateMetadata",
426
- args: [agentId, newURI]
427
- });
428
- return hash;
429
- }
430
- /**
431
- * Get agent profile by ID
432
- * @param agentId The agent's ID
433
- * @returns Agent profile data
434
- */
435
- async getAgent(agentId) {
436
- const result = await this.publicClient.readContract({
437
- address: this.address,
438
- abi: EXAGENT_REGISTRY_ABI,
439
- functionName: "agents",
440
- args: [agentId]
441
- });
442
- return {
443
- agentId,
444
- owner: result[0],
445
- name: result[1],
446
- metadataURI: result[2],
447
- registrationTime: result[3],
448
- verified: result[4],
449
- linkedWalletCount: result[5]
450
- };
451
- }
452
- /**
453
- * Get all linked wallets for an agent
454
- * @param agentId The agent's ID
455
- * @returns Array of linked wallet addresses
456
- */
457
- async getLinkedWallets(agentId) {
458
- const wallets = await this.publicClient.readContract({
459
- address: this.address,
460
- abi: EXAGENT_REGISTRY_ABI,
461
- functionName: "getLinkedWallets",
462
- args: [agentId]
463
- });
464
- return wallets;
465
- }
466
- /**
467
- * Get agent ID for a wallet (for trade attribution)
468
- * @param wallet The wallet address
469
- * @returns Agent ID (0 if not linked)
470
- */
471
- async getAgentForWallet(wallet) {
472
- const agentId = await this.publicClient.readContract({
473
- address: this.address,
474
- abi: EXAGENT_REGISTRY_ABI,
475
- functionName: "walletToAgent",
476
- args: [wallet]
477
- });
478
- return agentId;
479
- }
480
- /**
481
- * Check if a wallet is linked to a specific agent
482
- * @param agentId The agent's ID
483
- * @param wallet The wallet address to check
484
- * @returns True if the wallet is linked to the agent
485
- */
486
- async isLinkedWallet(agentId, wallet) {
487
- const linkedWallets = await this.getLinkedWallets(agentId);
488
- return linkedWallets.some((w) => w.toLowerCase() === wallet.toLowerCase());
489
- }
490
- /**
491
- * Get the nonce for wallet linking signature
492
- * @param wallet The wallet address
493
- * @returns Current nonce
494
- */
495
- async getNonce(wallet) {
496
- const nonce = await this.publicClient.readContract({
497
- address: this.address,
498
- abi: EXAGENT_REGISTRY_ABI,
499
- functionName: "nonces",
500
- args: [wallet]
501
- });
502
- return nonce;
503
- }
504
- /**
505
- * Generate the message hash for wallet linking.
506
- * Matches the contract's keccak256(abi.encodePacked(...)) exactly.
507
- * Sign the returned hash with signMessage({ raw: hash }) to produce a valid signature.
508
- * @param wallet The wallet to link
509
- * @param agentId The agent ID to link to
510
- * @param nonce The current nonce for the wallet
511
- * @returns keccak256 hash of the packed message (32 bytes)
512
- */
513
- static generateLinkMessage(wallet, agentId, nonce) {
514
- return keccak256(encodePacked(
515
- ["string", "address", "string", "uint256", "string", "uint256"],
516
- ["Link wallet ", wallet, " to Exagent ", agentId, " nonce ", nonce]
517
- ));
518
- }
519
- // ============ V4: One Agent Per Wallet ============
520
- /**
521
- * Get the agent owned by a wallet (not linked, owned)
522
- * @param wallet The wallet to look up
523
- * @returns Agent ID (0 if wallet doesn't own an agent)
524
- */
525
- async getAgentByOwner(wallet) {
526
- const agentId = await this.publicClient.readContract({
527
- address: this.address,
528
- abi: EXAGENT_REGISTRY_ABI,
529
- functionName: "getAgentByOwner",
530
- args: [wallet]
531
- });
532
- return agentId;
533
- }
534
- /**
535
- * Check if a wallet can register a new agent
536
- * @param wallet The wallet to check
537
- * @returns Object with canRegister boolean and existingAgentId (0 if none)
538
- */
539
- async canWalletRegister(wallet) {
540
- const result = await this.publicClient.readContract({
541
- address: this.address,
542
- abi: EXAGENT_REGISTRY_ABI,
543
- functionName: "canWalletRegister",
544
- args: [wallet]
545
- });
546
- const [canRegister, existingAgentId] = result;
547
- return { canRegister, existingAgentId };
548
- }
549
- // ============ Config Epochs ============
550
- /**
551
- * Update the agent's LLM config hash on-chain
552
- * @param agentId The agent's ID
553
- * @param configHash The keccak256 hash of (provider, model)
554
- * @returns Transaction hash
555
- */
556
- async updateConfig(agentId, configHash) {
557
- if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
558
- const hash = await this.walletClient.writeContract({
559
- account: this.account,
560
- chain: this.chain,
561
- address: this.address,
562
- abi: EXAGENT_REGISTRY_ABI,
563
- functionName: "setConfig",
564
- args: [agentId, configHash]
565
- });
566
- return hash;
567
- }
568
- /**
569
- * Get the current config hash for an agent
570
- * @param agentId The agent's ID
571
- * @returns Config hash (bytes32(0) if never set)
572
- */
573
- async getConfigHash(agentId) {
574
- const configHash = await this.publicClient.readContract({
575
- address: this.address,
576
- abi: EXAGENT_REGISTRY_ABI,
577
- functionName: "getConfigHash",
578
- args: [agentId]
579
- });
580
- return configHash;
581
- }
582
- /**
583
- * Calculate the config hash for a provider and model
584
- * @param provider The LLM provider name (e.g., "openai", "anthropic")
585
- * @param model The model name (e.g., "gpt-4", "claude-opus-4.5")
586
- * @returns keccak256 hash of the config
587
- */
588
- static calculateConfigHash(provider, model) {
589
- return keccak256(encodePacked(["string", "string"], [provider, model]));
590
- }
591
- // ============ Agent Retirement ============
592
- /**
593
- * Retire an agent — marks it as retired, unlinks all wallets, allows new agent registration
594
- * @param agentId The agent's ID
595
- * @returns Transaction hash
596
- */
597
- async retireAgent(agentId) {
598
- if (!this.walletClient || !this.account) throw new Error("Wallet client and account required for write operations");
599
- const hash = await this.walletClient.writeContract({
600
- account: this.account,
601
- chain: this.chain,
602
- address: this.address,
603
- abi: EXAGENT_REGISTRY_ABI,
604
- functionName: "retireAgent",
605
- args: [agentId]
606
- });
607
- return hash;
608
- }
609
- /**
610
- * Check if an agent is retired
611
- * @param agentId The agent's ID
612
- * @returns True if agent is retired
613
- */
614
- async isRetired(agentId) {
615
- const result = await this.publicClient.readContract({
616
- address: this.address,
617
- abi: EXAGENT_REGISTRY_ABI,
618
- functionName: "isRetired",
619
- args: [agentId]
620
- });
621
- return result;
622
- }
623
- // ============ Risk Universe Token Eligibility ============
624
- /**
625
- * Check if a token trade is allowed for an agent's risk universe
626
- * @param agentId The agent's ID
627
- * @param token The token address to check
628
- * @param aggregator The aggregator being used (for trusted aggregator bypass)
629
- * @returns True if the trade is allowed
630
- */
631
- async isTradeAllowed(agentId, token, aggregator) {
632
- const result = await this.publicClient.readContract({
633
- address: this.address,
634
- abi: EXAGENT_REGISTRY_ABI,
635
- functionName: "isTradeAllowed",
636
- args: [agentId, token, aggregator]
637
- });
638
- return result;
639
- }
640
- /**
641
- * Get the risk universe for an agent
642
- * @param agentId The agent's ID
643
- * @returns Risk universe (0=Core, 1=Established, 2=Derivatives, 3=Emerging, 4=Frontier)
644
- */
645
- async getRiskUniverse(agentId) {
646
- const result = await this.publicClient.readContract({
647
- address: this.address,
648
- abi: EXAGENT_REGISTRY_ABI,
649
- functionName: "getRiskUniverse",
650
- args: [agentId]
651
- });
652
- return Number(result);
653
- }
654
- /**
655
- * Get trade count for an agent
656
- * @param agentId The agent's ID
657
- * @returns Number of recorded trades
658
- */
659
- async getTradeCount(agentId) {
660
- const result = await this.publicClient.readContract({
661
- address: this.address,
662
- abi: EXAGENT_REGISTRY_ABI,
663
- functionName: "tradeCount",
664
- args: [agentId]
665
- });
666
- return result;
667
- }
668
- };
669
-
670
- // src/contracts/router.ts
671
- var EXAGENT_ROUTER_ABI = [
672
- // Custom errors — must match ExagentRouterV2.sol for viem to decode reverts
673
- { type: "error", name: "InvalidAgentId", inputs: [] },
674
- { type: "error", name: "SwapFailed", inputs: [] },
675
- { type: "error", name: "InsufficientOutput", inputs: [] },
676
- { type: "error", name: "ZeroAddress", inputs: [] },
677
- { type: "error", name: "ZeroAmount", inputs: [] },
678
- { type: "error", name: "AggregatorNotWhitelisted", inputs: [] },
679
- { type: "error", name: "ETHTransferFailed", inputs: [] },
680
- { type: "error", name: "FeeBpsTooHigh", inputs: [] },
681
- { type: "error", name: "NotAuthorizedForAgent", inputs: [{ name: "agentId", type: "uint256" }, { name: "caller", type: "address" }] },
682
- { type: "error", name: "ConfigMismatch", inputs: [{ name: "provided", type: "bytes32" }, { name: "onChain", type: "bytes32" }] },
683
- { type: "error", name: "MsgValueMismatch", inputs: [] },
684
- { type: "error", name: "FeeCollectorNotSet", inputs: [] },
685
- { type: "error", name: "TokenNotAllowedForAgent", inputs: [{ name: "agentId", type: "uint256" }, { name: "token", type: "address" }] },
686
- { type: "error", name: "NotAuthorized", inputs: [] },
687
- { type: "error", name: "RiskUniverseTooLow", inputs: [{ name: "agentId", type: "uint256" }, { name: "riskUniverse", type: "uint256" }] },
688
- { type: "error", name: "FillAlreadyProcessed", inputs: [{ name: "fillId", type: "bytes32" }] }
689
- ];
690
-
691
- // src/contracts/vault.ts
692
- var EXAGENT_VAULT_ABI = [
693
- // ERC-4626 Standard
694
- { type: "function", name: "asset", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
695
- { type: "function", name: "totalAssets", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
696
- { type: "function", name: "convertToShares", inputs: [{ name: "assets", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
697
- { type: "function", name: "convertToAssets", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
698
- { type: "function", name: "maxDeposit", inputs: [{ name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
699
- { type: "function", name: "maxMint", inputs: [{ name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
700
- { type: "function", name: "maxWithdraw", inputs: [{ name: "owner", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
701
- { type: "function", name: "maxRedeem", inputs: [{ name: "owner", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
702
- { type: "function", name: "previewDeposit", inputs: [{ name: "assets", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
703
- { type: "function", name: "previewMint", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
704
- { type: "function", name: "previewWithdraw", inputs: [{ name: "assets", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
705
- { type: "function", name: "previewRedeem", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
706
- { type: "function", name: "deposit", inputs: [{ name: "assets", type: "uint256" }, { name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
707
- { type: "function", name: "mint", inputs: [{ name: "shares", type: "uint256" }, { name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
708
- { type: "function", name: "withdraw", inputs: [{ name: "assets", type: "uint256" }, { name: "receiver", type: "address" }, { name: "owner", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
709
- { type: "function", name: "redeem", inputs: [{ name: "shares", type: "uint256" }, { name: "receiver", type: "address" }, { name: "owner", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
710
- // ERC-20
711
- { type: "function", name: "name", inputs: [], outputs: [{ type: "string" }], stateMutability: "view" },
712
- { type: "function", name: "symbol", inputs: [], outputs: [{ type: "string" }], stateMutability: "view" },
713
- { type: "function", name: "decimals", inputs: [], outputs: [{ type: "uint8" }], stateMutability: "view" },
714
- { type: "function", name: "totalSupply", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
715
- { type: "function", name: "balanceOf", inputs: [{ name: "account", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
716
- { type: "function", name: "allowance", inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
717
- { type: "function", name: "approve", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ type: "bool" }], stateMutability: "nonpayable" },
718
- // Custom Exagent Vault
719
- { type: "function", name: "agentId", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
720
- { type: "function", name: "sharePrice", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
721
- { type: "function", name: "effectiveShares", inputs: [{ name: "user", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
722
- { type: "function", name: "userHighWaterMark", inputs: [{ name: "user", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
723
- { type: "function", name: "performanceFeeBps", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
724
- { type: "function", name: "managementFeeBps", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
725
- { type: "function", name: "feeRecipient", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
726
- { type: "function", name: "highWaterMark", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
727
- { type: "function", name: "depositsPaused", inputs: [], outputs: [{ type: "bool" }], stateMutability: "view" },
728
- { type: "function", name: "withdrawalsPaused", inputs: [], outputs: [{ type: "bool" }], stateMutability: "view" },
729
- { type: "function", name: "circuitBreakerActive", inputs: [], outputs: [{ type: "bool" }], stateMutability: "view" },
730
- { type: "function", name: "pendingWithdrawals", inputs: [{ name: "user", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
731
- { type: "function", name: "getPendingWithdrawals", inputs: [{ name: "owner", type: "address" }], outputs: [{ name: "requestIds", type: "uint256[]" }], stateMutability: "view" },
732
- { type: "function", name: "getClaimableAmount", inputs: [{ name: "requestId", type: "uint256" }], outputs: [{ name: "assets", type: "uint256" }], stateMutability: "view" },
733
- { type: "function", name: "getRateLimitStatus", inputs: [], outputs: [{ name: "remaining", type: "uint256" }, { name: "periodEnds", type: "uint256" }], stateMutability: "view" },
734
- { type: "function", name: "getWithdrawalQueueLength", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
735
- // Withdrawal Queue
736
- { type: "function", name: "requestWithdrawal", inputs: [{ name: "shares", type: "uint256" }, { name: "receiver", type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
737
- { type: "function", name: "claimWithdrawal", inputs: [{ name: "requestId", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "nonpayable" },
738
- { type: "function", name: "cancelWithdrawal", inputs: [{ name: "requestId", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
739
- {
740
- type: "function",
741
- name: "getPendingWithdrawal",
742
- inputs: [{ name: "requestId", type: "uint256" }],
743
- outputs: [
744
- { name: "owner", type: "address" },
745
- { name: "receiver", type: "address" },
746
- { name: "shares", type: "uint256" },
747
- { name: "requestTime", type: "uint256" },
748
- { name: "processed", type: "bool" },
749
- { name: "claimableAt", type: "uint256" }
750
- ],
751
- stateMutability: "view"
752
- },
753
- // V2: Safe "withdraw all"
754
- { type: "function", name: "redeemMax", inputs: [{ name: "receiver", type: "address" }, { name: "owner", type: "address" }], outputs: [{ name: "assets", type: "uint256" }], stateMutability: "nonpayable" },
755
- { type: "function", name: "withdrawMax", inputs: [{ name: "receiver", type: "address" }, { name: "owner", type: "address" }], outputs: [{ name: "assets", type: "uint256" }], stateMutability: "nonpayable" },
756
- // V2: Emergency exit with penalty
757
- { type: "function", name: "emergencyWithdraw", inputs: [], outputs: [{ name: "assets", type: "uint256" }], stateMutability: "nonpayable" },
758
- // Events
759
- { 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" }] },
760
- { 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" }] },
761
- { type: "event", name: "PerformanceFeeCharged", inputs: [{ name: "user", type: "address", indexed: true }, { name: "feeShares", type: "uint256" }, { name: "profit", type: "uint256" }] },
762
- { 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" }] },
763
- { type: "event", name: "WithdrawalClaimed", inputs: [{ name: "requestId", type: "uint256", indexed: true }, { name: "receiver", type: "address", indexed: true }, { name: "assets", type: "uint256" }] },
764
- { type: "event", name: "WithdrawalCancelled", inputs: [{ name: "requestId", type: "uint256", indexed: true }] },
765
- { type: "event", name: "EmergencyWithdrawal", inputs: [{ name: "user", type: "address", indexed: true }, { name: "shares", type: "uint256" }, { name: "assets", type: "uint256" }] }
766
- ];
767
- var ERC20_APPROVE_ABI = [
768
- { type: "function", name: "approve", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ type: "bool" }], stateMutability: "nonpayable" }
769
- ];
770
- var ExagentVault = class {
771
- address;
772
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
773
- publicClient;
774
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
775
- walletClient;
776
- chain;
777
- account;
778
- constructor(vaultAddress, publicClient, walletClient, chain, account) {
779
- this.address = vaultAddress;
780
- this.publicClient = publicClient;
781
- this.walletClient = walletClient;
782
- if (!chain) {
783
- throw new Error("Chain parameter is required");
784
- }
785
- this.chain = chain;
786
- this.account = account;
787
- }
788
- // ============ Read Functions ============
789
- /**
790
- * Get comprehensive vault info
791
- */
792
- async getVaultInfo() {
793
- const [
794
- name,
795
- symbol,
796
- asset,
797
- agentId,
798
- totalAssets,
799
- totalSupply,
800
- sharePrice,
801
- highWaterMark,
802
- performanceFeeBps,
803
- managementFeeBps,
804
- feeRecipient,
805
- depositsPaused,
806
- withdrawalsPaused,
807
- circuitBreakerActive
808
- ] = await Promise.all([
809
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "name" }),
810
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "symbol" }),
811
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "asset" }),
812
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "agentId" }),
813
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "totalAssets" }),
814
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "totalSupply" }),
815
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "sharePrice" }),
816
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "highWaterMark" }),
817
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "performanceFeeBps" }),
818
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "managementFeeBps" }),
819
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "feeRecipient" }),
820
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "depositsPaused" }),
821
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "withdrawalsPaused" }),
822
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "circuitBreakerActive" })
823
- ]);
824
- return {
825
- address: this.address,
826
- name,
827
- symbol,
828
- asset,
829
- agentId,
830
- totalAssets,
831
- totalSupply,
832
- sharePrice,
833
- highWaterMark,
834
- performanceFeeBps,
835
- managementFeeBps,
836
- feeRecipient,
837
- depositsPaused,
838
- withdrawalsPaused,
839
- circuitBreakerActive
840
- };
841
- }
842
- /**
843
- * Get user's position in the vault
844
- */
845
- async getPosition(user) {
846
- const [
847
- shares,
848
- effectiveShares,
849
- pendingWithdrawals,
850
- userHighWaterMark
851
- ] = await Promise.all([
852
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "balanceOf", args: [user] }),
853
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "effectiveShares", args: [user] }),
854
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "pendingWithdrawals", args: [user] }),
855
- this.publicClient.readContract({ address: this.address, abi: EXAGENT_VAULT_ABI, functionName: "userHighWaterMark", args: [user] })
856
- ]);
857
- const assetsValue = await this.publicClient.readContract({
858
- address: this.address,
859
- abi: EXAGENT_VAULT_ABI,
860
- functionName: "convertToAssets",
861
- args: [effectiveShares]
862
- });
863
- return {
864
- shares,
865
- effectiveShares,
866
- pendingWithdrawals,
867
- assetsValue,
868
- userHighWaterMark
869
- };
870
- }
871
- /**
872
- * Get current share price (assets per share, scaled to 1e18)
873
- */
874
- async getSharePrice() {
875
- return this.publicClient.readContract({
876
- address: this.address,
877
- abi: EXAGENT_VAULT_ABI,
878
- functionName: "sharePrice"
879
- });
880
- }
881
- /**
882
- * Preview deposit - get shares for given assets
883
- */
884
- async previewDeposit(assets) {
885
- return this.publicClient.readContract({
886
- address: this.address,
887
- abi: EXAGENT_VAULT_ABI,
888
- functionName: "previewDeposit",
889
- args: [assets]
890
- });
891
- }
892
- /**
893
- * Preview withdrawal - get assets for given shares
894
- */
895
- async previewRedeem(shares) {
896
- return this.publicClient.readContract({
897
- address: this.address,
898
- abi: EXAGENT_VAULT_ABI,
899
- functionName: "previewRedeem",
900
- args: [shares]
901
- });
902
- }
903
- /**
904
- * Get max deposit amount
905
- */
906
- async maxDeposit(receiver) {
907
- return this.publicClient.readContract({
908
- address: this.address,
909
- abi: EXAGENT_VAULT_ABI,
910
- functionName: "maxDeposit",
911
- args: [receiver]
912
- });
913
- }
914
- /**
915
- * Get max withdrawal amount
916
- */
917
- async maxWithdraw(owner) {
918
- return this.publicClient.readContract({
919
- address: this.address,
920
- abi: EXAGENT_VAULT_ABI,
921
- functionName: "maxWithdraw",
922
- args: [owner]
923
- });
924
- }
925
- /**
926
- * Get rate limit status
927
- */
928
- async getRateLimitStatus() {
929
- const result = await this.publicClient.readContract({
930
- address: this.address,
931
- abi: EXAGENT_VAULT_ABI,
932
- functionName: "getRateLimitStatus"
933
- });
934
- return { remaining: result[0], periodEnds: result[1] };
935
- }
936
- /**
937
- * Get pending withdrawal request
938
- */
939
- async getPendingWithdrawal(requestId) {
940
- const result = await this.publicClient.readContract({
941
- address: this.address,
942
- abi: EXAGENT_VAULT_ABI,
943
- functionName: "getPendingWithdrawal",
944
- args: [requestId]
945
- });
946
- return {
947
- requestId,
948
- owner: result[0],
949
- receiver: result[1],
950
- shares: result[2],
951
- requestTime: result[3],
952
- processed: result[4],
953
- claimableAt: result[5]
954
- };
955
- }
956
- // ============ Write Functions ============
957
- /**
958
- * Deposit assets into the vault
959
- * @param assets Amount of underlying asset to deposit
960
- * @param receiver Address to receive vault shares
961
- * @returns Transaction hash
962
- */
963
- async deposit(assets, receiver) {
964
- if (!this.walletClient || !this.account) {
965
- throw new Error("Wallet client required for write operations");
966
- }
967
- const to = receiver ?? this.account.address;
968
- const hash = await this.walletClient.writeContract({
969
- address: this.address,
970
- abi: EXAGENT_VAULT_ABI,
971
- functionName: "deposit",
972
- args: [assets, to],
973
- account: this.account,
974
- chain: this.chain
975
- });
976
- return hash;
977
- }
978
- /**
979
- * Withdraw assets from the vault
980
- * @param assets Amount of underlying asset to withdraw
981
- * @param receiver Address to receive assets
982
- * @param owner Address whose shares to burn (defaults to caller)
983
- * @returns Transaction hash
984
- */
985
- async withdraw(assets, receiver, owner) {
986
- if (!this.walletClient || !this.account) {
987
- throw new Error("Wallet client required for write operations");
988
- }
989
- const to = receiver ?? this.account.address;
990
- const from = owner ?? this.account.address;
991
- const hash = await this.walletClient.writeContract({
992
- address: this.address,
993
- abi: EXAGENT_VAULT_ABI,
994
- functionName: "withdraw",
995
- args: [assets, to, from],
996
- account: this.account,
997
- chain: this.chain
998
- });
999
- return hash;
1000
- }
1001
- /**
1002
- * Redeem shares for assets
1003
- * @param shares Amount of shares to redeem
1004
- * @param receiver Address to receive assets
1005
- * @param owner Address whose shares to burn (defaults to caller)
1006
- * @returns Transaction hash
1007
- */
1008
- async redeem(shares, receiver, owner) {
1009
- if (!this.walletClient || !this.account) {
1010
- throw new Error("Wallet client required for write operations");
1011
- }
1012
- const to = receiver ?? this.account.address;
1013
- const from = owner ?? this.account.address;
1014
- const hash = await this.walletClient.writeContract({
1015
- address: this.address,
1016
- abi: EXAGENT_VAULT_ABI,
1017
- functionName: "redeem",
1018
- args: [shares, to, from],
1019
- account: this.account,
1020
- chain: this.chain
1021
- });
1022
- return hash;
1023
- }
1024
- /**
1025
- * Redeem all shares safely — charges performance fee first, then redeems remaining balance.
1026
- * Use this instead of redeem(fullBalance) to avoid ERC4626ExceededMaxRedeem revert.
1027
- * @param receiver Address to receive assets
1028
- * @param owner Address whose shares to burn (defaults to caller)
1029
- * @returns Transaction hash
1030
- */
1031
- async redeemMax(receiver, owner) {
1032
- if (!this.walletClient || !this.account) {
1033
- throw new Error("Wallet client required for write operations");
1034
- }
1035
- const to = receiver ?? this.account.address;
1036
- const from = owner ?? this.account.address;
1037
- const hash = await this.walletClient.writeContract({
1038
- address: this.address,
1039
- abi: EXAGENT_VAULT_ABI,
1040
- functionName: "redeemMax",
1041
- args: [to, from],
1042
- account: this.account,
1043
- chain: this.chain
1044
- });
1045
- return hash;
1046
- }
1047
- /**
1048
- * Withdraw all assets safely — charges performance fee first, then withdraws remaining balance.
1049
- * @param receiver Address to receive assets
1050
- * @param owner Address whose shares to burn (defaults to caller)
1051
- * @returns Transaction hash
1052
- */
1053
- async withdrawMax(receiver, owner) {
1054
- if (!this.walletClient || !this.account) {
1055
- throw new Error("Wallet client required for write operations");
1056
- }
1057
- const to = receiver ?? this.account.address;
1058
- const from = owner ?? this.account.address;
1059
- const hash = await this.walletClient.writeContract({
1060
- address: this.address,
1061
- abi: EXAGENT_VAULT_ABI,
1062
- functionName: "withdrawMax",
1063
- args: [to, from],
1064
- account: this.account,
1065
- chain: this.chain
1066
- });
1067
- return hash;
1068
- }
1069
- /**
1070
- * Request a queued withdrawal (for large amounts)
1071
- * @param shares Amount of shares to withdraw
1072
- * @param receiver Address to receive assets
1073
- * @returns Transaction hash
1074
- */
1075
- async requestWithdrawal(shares, receiver) {
1076
- if (!this.walletClient || !this.account) {
1077
- throw new Error("Wallet client required for write operations");
1078
- }
1079
- const to = receiver ?? this.account.address;
1080
- const hash = await this.walletClient.writeContract({
1081
- address: this.address,
1082
- abi: EXAGENT_VAULT_ABI,
1083
- functionName: "requestWithdrawal",
1084
- args: [shares, to],
1085
- account: this.account,
1086
- chain: this.chain
1087
- });
1088
- return hash;
1089
- }
1090
- /**
1091
- * Claim a pending withdrawal request
1092
- * @param requestId The withdrawal request ID
1093
- * @returns Transaction hash
1094
- */
1095
- async claimWithdrawal(requestId) {
1096
- if (!this.walletClient || !this.account) {
1097
- throw new Error("Wallet client required for write operations");
1098
- }
1099
- const hash = await this.walletClient.writeContract({
1100
- address: this.address,
1101
- abi: EXAGENT_VAULT_ABI,
1102
- functionName: "claimWithdrawal",
1103
- args: [requestId],
1104
- account: this.account,
1105
- chain: this.chain
1106
- });
1107
- return hash;
1108
- }
1109
- /**
1110
- * Cancel a pending withdrawal request
1111
- * @param requestId The withdrawal request ID
1112
- * @returns Transaction hash
1113
- */
1114
- async cancelWithdrawal(requestId) {
1115
- if (!this.walletClient || !this.account) {
1116
- throw new Error("Wallet client required for write operations");
1117
- }
1118
- const hash = await this.walletClient.writeContract({
1119
- address: this.address,
1120
- abi: EXAGENT_VAULT_ABI,
1121
- functionName: "cancelWithdrawal",
1122
- args: [requestId],
1123
- account: this.account,
1124
- chain: this.chain
1125
- });
1126
- return hash;
1127
- }
1128
- /**
1129
- * Emergency withdrawal with penalty (50% if vault < 7 days old, 20% otherwise).
1130
- * Penalty stays in the vault for remaining backers.
1131
- * @returns Transaction hash
1132
- */
1133
- async emergencyWithdraw() {
1134
- if (!this.walletClient || !this.account) {
1135
- throw new Error("Wallet client required for write operations");
1136
- }
1137
- const hash = await this.walletClient.writeContract({
1138
- address: this.address,
1139
- abi: EXAGENT_VAULT_ABI,
1140
- functionName: "emergencyWithdraw",
1141
- args: [],
1142
- account: this.account,
1143
- chain: this.chain
1144
- });
1145
- return hash;
1146
- }
1147
- /**
1148
- * Approve vault to spend underlying asset
1149
- * @param assetAddress The asset token address
1150
- * @param amount Amount to approve
1151
- * @returns Transaction hash
1152
- */
1153
- async approveAsset(assetAddress, amount) {
1154
- if (!this.walletClient || !this.account) {
1155
- throw new Error("Wallet client required for write operations");
1156
- }
1157
- const hash = await this.walletClient.writeContract({
1158
- address: assetAddress,
1159
- abi: ERC20_APPROVE_ABI,
1160
- functionName: "approve",
1161
- args: [this.address, amount],
1162
- account: this.account,
1163
- chain: this.chain
1164
- });
1165
- return hash;
1166
- }
1167
- };
1168
-
1169
- // src/constants.ts
1170
- import { base } from "viem/chains";
1171
- var SDK_VERSION = "0.1.21";
1172
- var DEFAULT_RPC_URL = "https://mainnet.base.org";
1173
- function getRpcUrl() {
1174
- if (typeof process !== "undefined" && process.env) {
1175
- return process.env.BASE_RPC_URL || process.env.EXAGENT_RPC_URL || DEFAULT_RPC_URL;
1176
- }
1177
- return DEFAULT_RPC_URL;
1178
- }
1179
- var CHAIN_CONFIG = {
1180
- mainnet: base
1181
- };
1182
- var CONTRACT_ADDRESSES = {
1183
- mainnet: {
1184
- agentRegistry: "0x2261706C751F8ac5cdDb481B7b56EA2137d4A723",
1185
- router: "0xBfE186E2Bba9B730c8732917AE36E7AC143420d3",
1186
- vaultFactory: "0x3312f7C0059C03b47Df63DA4f73B79A63a874ef5",
1187
- feeCollector: "0x00Ab9847049b5496619dFDd1A7bd36FA49eB7195",
1188
- serviceEscrow: "0x63A4d1dA774422EFC2cc57d71F948231BD812516"
1189
- }
1190
- };
1191
- var DEX_ADDRESSES = {
1192
- // Aerodrome
1193
- aerodromeRouter: "0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43",
1194
- aerodromeSlipstream: "0xBE6D8f0d05cC4be24d5167a3eF062215bE6D18a5",
1195
- // Uniswap
1196
- uniswapUniversalRouter: "0x198EF79F1F515F02dFE9e3115eD9fC07183f02fC",
1197
- uniswapV4Router: "0x6ff5693b99212da76ad316178a184ab56d299b43",
1198
- // Common tokens
1199
- WETH: "0x4200000000000000000000000000000000000006",
1200
- USDC: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
1201
- };
1202
- var ZERO_X_CONFIG = {
1203
- baseUrl: "https://api.0x.org",
1204
- chainId: 8453
1205
- // Base
1206
- };
1207
- var EXAGENT_API_CONFIG = {
1208
- mainnet: "https://exagent-api.onrender.com"
1209
- };
1210
- var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
1211
- function validateContractAddresses(network) {
1212
- const addresses = CONTRACT_ADDRESSES[network];
1213
- const missing = Object.entries(addresses).filter(([, addr]) => addr === ZERO_ADDRESS).map(([name]) => name);
1214
- if (missing.length > 0) {
1215
- throw new Error(
1216
- `Contract addresses not deployed on ${network}. Missing: ${missing.join(", ")}. Update @exagent/sdk to the latest version.`
1217
- );
1218
- }
1219
- }
1220
-
1221
- // src/types.ts
1222
- function buildGlobalAgentId(chainId, registryAddress, agentId) {
1223
- return `eip155:${chainId}:${registryAddress}:${Number(agentId)}`;
1224
- }
1225
- function parseGlobalAgentId(globalId) {
1226
- const parts = globalId.split(":");
1227
- if (parts.length !== 4 || parts[0] !== "eip155") {
1228
- throw new Error(`Invalid ERC-8004 global agent ID: ${globalId}`);
1229
- }
1230
- const chainId = parseInt(parts[1], 10);
1231
- const registryAddress = parts[2];
1232
- const agentId = parseInt(parts[3], 10);
1233
- if (isNaN(chainId) || isNaN(agentId)) {
1234
- throw new Error(`Invalid ERC-8004 global agent ID: ${globalId}`);
1235
- }
1236
- return { chainId, registryAddress, agentId };
1237
- }
1238
-
1239
- // src/client.ts
1240
- var ExagentClient = class {
1241
- publicClient;
1242
- walletClient;
1243
- account;
1244
- network;
1245
- apiKey;
1246
- // Contract interfaces
1247
- registry;
1248
- // Cached agent ID
1249
- _agentId;
1250
- constructor(config) {
1251
- this.network = config.network ?? "mainnet";
1252
- this.apiKey = config.apiKey;
1253
- validateContractAddresses(this.network);
1254
- const chain = CHAIN_CONFIG[this.network];
1255
- const rpcUrl = config.rpcUrl || getRpcUrl();
1256
- const transport = http(rpcUrl, { timeout: 6e4 });
1257
- this.publicClient = createPublicClient({
1258
- chain,
1259
- transport
1260
- });
1261
- if (config.walletClient) {
1262
- this.walletClient = config.walletClient;
1263
- this.account = config.walletClient.account;
1264
- } else if (config.privateKey) {
1265
- this.account = privateKeyToAccount(config.privateKey);
1266
- this.walletClient = createWalletClient({
1267
- account: this.account,
1268
- chain,
1269
- transport
1270
- });
1271
- } else {
1272
- throw new Error("Either privateKey or walletClient must be provided");
1273
- }
1274
- const addresses = CONTRACT_ADDRESSES[this.network];
1275
- this.registry = new ExagentRegistry(
1276
- addresses.agentRegistry,
1277
- this.publicClient,
1278
- this.walletClient,
1279
- chain,
1280
- this.account
1281
- );
1282
- }
1283
- /** Standard headers for all API requests (includes SDK version for gating) */
1284
- apiHeaders(contentType) {
1285
- return {
1286
- ...contentType && { "Content-Type": contentType },
1287
- "X-Exagent-SDK-Version": SDK_VERSION,
1288
- ...this.apiKey && { "X-API-Key": this.apiKey }
1289
- };
1290
- }
1291
- // ============ Agent Registration ============
1292
- /**
1293
- * Register as a new agent on Exagent.
1294
- *
1295
- * RECOMMENDED: Register your agent at https://exagent.io instead of using this method.
1296
- * The website handles the full registration flow including risk universe selection,
1297
- * position size limits, daily loss limits, and metadata upload — all in one step.
1298
- * After registering on the website, use your agent ID in agent-config.json.
1299
- *
1300
- * This SDK method is available for programmatic registration but uses default
1301
- * risk parameters (universe=1, maxPositionSize=10%, maxDailyLoss=5%).
1302
- *
1303
- * @param metadata Agent metadata (will be uploaded to IPFS)
1304
- * @returns The assigned agent ID
1305
- * @throws Error if wallet already owns an agent (one agent per wallet rule)
1306
- */
1307
- async register(metadata) {
1308
- const { canRegister, existingAgentId } = await this.registry.canWalletRegister(this.account.address);
1309
- if (!canRegister) {
1310
- throw new Error(
1311
- `This wallet already owns agent #${existingAgentId}. Each wallet can only own one agent. Use a different wallet to create a new agent.`
1312
- );
1313
- }
1314
- const nameAvailable = await this.registry.isNameAvailable(metadata.name);
1315
- if (!nameAvailable) {
1316
- throw new Error(`Agent name "${metadata.name}" is already taken`);
1317
- }
1318
- const metadataURI = await this.uploadMetadata(metadata);
1319
- const hash = await this.registry.register(metadata.name, metadataURI);
1320
- const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
1321
- const agentId = this.parseAgentIdFromReceipt(receipt);
1322
- this._agentId = agentId;
1323
- const linkHash = await this.registry.linkOwnWallet(agentId);
1324
- await this.publicClient.waitForTransactionReceipt({ hash: linkHash });
1325
- return agentId;
1326
- }
1327
- /**
1328
- * Check if an agent name is available for registration
1329
- * @param name The name to check
1330
- * @returns True if the name is available
1331
- */
1332
- async isNameAvailable(name) {
1333
- return this.registry.isNameAvailable(name);
1334
- }
1335
- /**
1336
- * Get agent by name (returns 0 if not found)
1337
- * @param name The agent name to look up
1338
- * @returns Agent ID or 0 if not found
1339
- */
1340
- async getAgentByName(name) {
1341
- return this.registry.getAgentByName(name);
1342
- }
1343
- /**
1344
- * Get the current agent's ID (if registered).
1345
- *
1346
- * Resolution order:
1347
- * 1. Cached value (from prior register() or getAgentId() call)
1348
- * 2. walletToAgent mapping (linked wallets)
1349
- * 3. Owner lookup (unlinked owner wallet)
1350
- *
1351
- * If the agent was just registered, public RPCs may return stale data (0).
1352
- * Use `retries` to wait for the on-chain state to propagate.
1353
- *
1354
- * @param retries Number of retries with 2s delay for stale RPC reads (default: 0)
1355
- */
1356
- async getAgentId(retries = 0) {
1357
- if (this._agentId) return this._agentId;
1358
- for (let attempt = 0; attempt <= retries; attempt++) {
1359
- if (attempt > 0) {
1360
- await new Promise((r) => setTimeout(r, 2e3));
1361
- }
1362
- const agentId = await this.registry.getAgentForWallet(this.account.address);
1363
- if (agentId > 0n) {
1364
- this._agentId = agentId;
1365
- return agentId;
1366
- }
1367
- const ownedAgentId = await this.registry.getAgentByOwner(this.account.address);
1368
- if (ownedAgentId > 0n) {
1369
- this._agentId = ownedAgentId;
1370
- return ownedAgentId;
1371
- }
1372
- }
1373
- return void 0;
1374
- }
1375
- /**
1376
- * Get agent profile
1377
- * @param agentId Optional agent ID (defaults to current agent)
1378
- */
1379
- async getAgent(agentId) {
1380
- const id = agentId ?? await this.getAgentId();
1381
- if (!id) throw new Error("Agent not registered");
1382
- return this.registry.getAgent(id);
1383
- }
1384
- // ============ Wallet Management ============
1385
- /**
1386
- * Link an additional wallet to this agent
1387
- * @param wallet Wallet address to link
1388
- * @param signMessage Function to sign the message hash with the wallet (use signMessage({ raw: hash }))
1389
- */
1390
- async linkWallet(wallet, signMessage) {
1391
- const agentId = await this.getAgentId();
1392
- if (!agentId) throw new Error("Agent not registered");
1393
- const nonce = await this.registry.getNonce(wallet);
1394
- const messageHash = ExagentRegistry.generateLinkMessage(wallet, agentId, nonce);
1395
- const signature = await signMessage({ raw: messageHash });
1396
- return this.registry.linkWallet(agentId, wallet, signature);
1397
- }
1398
- /**
1399
- * Get all wallets linked to this agent
1400
- */
1401
- async getLinkedWallets() {
1402
- const agentId = await this.getAgentId();
1403
- if (!agentId) throw new Error("Agent not registered");
1404
- return this.registry.getLinkedWallets(agentId);
1405
- }
1406
- // ============ Trading ============
1407
- /**
1408
- * Execute a trade through ExagentRouter
1409
- *
1410
- * All trades MUST go through the ExagentRouter. This ensures:
1411
- * - Trade attribution for leaderboard ranking
1412
- * - Protocol fee collection (0.2%)
1413
- * - On-chain performance tracking
1414
- * - Token whitelist enforcement per risk universe
1415
- *
1416
- * There is no way to trade outside the router. This is by design.
1417
- *
1418
- * @param intent Trade parameters
1419
- * @returns Trade result with transaction hash
1420
- *
1421
- * @example
1422
- * ```typescript
1423
- * const result = await exagent.trade({
1424
- * tokenIn: WETH,
1425
- * tokenOut: USDC,
1426
- * amountIn: parseEther('1'),
1427
- * maxSlippageBps: 50,
1428
- * });
1429
- * console.log('Trade submitted:', result.hash);
1430
- * ```
1431
- */
1432
- async trade(intent) {
1433
- const agentId = await this.getAgentId();
1434
- if (!agentId) throw new Error("Agent not registered");
1435
- const routerTrade = await this.buildRouterTrade(intent, agentId);
1436
- const seen = /* @__PURE__ */ new Set();
1437
- for (const approval of routerTrade.approvals) {
1438
- const key = `${approval.token.toLowerCase()}:${approval.spender.toLowerCase()}`;
1439
- if (seen.has(key)) continue;
1440
- seen.add(key);
1441
- if (!approval.amount) {
1442
- console.warn(`Skipping approval with missing amount for token ${approval.token} \u2014 0x did not return expected allowance`);
1443
- continue;
1444
- }
1445
- const amount = BigInt(approval.amount);
1446
- const existing = await this.getAllowance(
1447
- approval.token,
1448
- approval.spender
1449
- );
1450
- if (existing >= amount) continue;
1451
- await this.approveToken(
1452
- approval.token,
1453
- approval.spender,
1454
- maxUint256
1455
- );
1456
- }
1457
- const txParams = {
1458
- account: this.account,
1459
- chain: CHAIN_CONFIG[this.network],
1460
- to: routerTrade.transaction.to,
1461
- data: routerTrade.transaction.data,
1462
- value: BigInt(routerTrade.transaction.value)
1463
- };
1464
- const isETHInput = this.isETHAddress(intent.tokenIn);
1465
- try {
1466
- const estimated = await this.publicClient.estimateGas({
1467
- account: this.account,
1468
- to: routerTrade.transaction.to,
1469
- data: routerTrade.transaction.data,
1470
- value: BigInt(routerTrade.transaction.value)
1471
- });
1472
- txParams.gas = estimated * 150n / 100n;
1473
- } catch (err) {
1474
- const errorData = err?.data;
1475
- if (isETHInput) {
1476
- if (errorData && errorData.length > 2) {
1477
- console.error(`Gas estimation revert data: ${errorData}`);
1478
- try {
1479
- const decoded = decodeErrorResult({
1480
- abi: [...EXAGENT_ROUTER_ABI, ...EXAGENT_REGISTRY_ABI],
1481
- data: errorData
1482
- });
1483
- throw new Error(`Trade will revert: ${decoded.errorName}${decoded.args ? ` (${decoded.args.join(", ")})` : ""}`);
1484
- } catch (decodeErr) {
1485
- if (decodeErr instanceof Error && decodeErr.message.startsWith("Trade will revert:")) throw decodeErr;
1486
- throw new Error(`Trade will revert: unknown error selector ${errorData.slice(0, 10)} (raw: ${errorData.slice(0, 66)})`);
1487
- }
1488
- }
1489
- throw new Error(`Trade will revert: gas estimation failed. ${err?.message || "Unknown reason"}`);
1490
- }
1491
- if (routerTrade.gasEstimate) {
1492
- const apiGas = BigInt(routerTrade.gasEstimate);
1493
- txParams.gas = apiGas * 200n / 100n;
1494
- console.warn(
1495
- `[SDK] Local gas estimation failed for sell, using API estimate with 100% buffer: ${txParams.gas}. This is expected for some DEX routes. Warning: ${routerTrade.simulationWarning || "none"}`
1496
- );
1497
- } else {
1498
- txParams.gas = 800000n;
1499
- console.warn(
1500
- `[SDK] Local gas estimation failed for sell and no API estimate available. Using safe default: 800000. This may result in wasted gas if the transaction reverts.`
1501
- );
1502
- }
1503
- }
1504
- const hash = await this.walletClient.sendTransaction(txParams);
1505
- if (routerTrade.route && routerTrade.route.length > 0) {
1506
- const dexSource = [...new Set(routerTrade.route.map((r) => r.dex))].join(", ");
1507
- const routeApiUrl = EXAGENT_API_CONFIG[this.network];
1508
- fetch(`${routeApiUrl}/v1/router/trade-route`, {
1509
- method: "POST",
1510
- headers: this.apiHeaders("application/json"),
1511
- body: JSON.stringify({
1512
- txHash: hash,
1513
- agentId: Number(agentId),
1514
- dexSource,
1515
- route: routerTrade.route
1516
- })
1517
- }).catch(() => {
1518
- });
1519
- }
1520
- return {
1521
- hash,
1522
- agentId,
1523
- tokenIn: routerTrade.tokenIn,
1524
- tokenOut: routerTrade.tokenOut,
1525
- amountIn: routerTrade.amountIn,
1526
- expectedAmountOut: routerTrade.amountOut
1527
- };
1528
- }
1529
- /**
1530
- * Build a router trade transaction without executing
1531
- * Useful for simulation or multi-step workflows
1532
- */
1533
- async buildRouterTrade(intent, agentId) {
1534
- const id = agentId ?? await this.getAgentId();
1535
- if (!id) throw new Error("Agent not registered");
1536
- const apiUrl = EXAGENT_API_CONFIG[this.network];
1537
- let configHash = intent.configHash;
1538
- if (!configHash) {
1539
- const onChainHash = await this.registry.getConfigHash(id);
1540
- if (onChainHash && onChainHash !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1541
- configHash = onChainHash;
1542
- }
1543
- }
1544
- const response = await fetch(`${apiUrl}/v1/router/trade`, {
1545
- method: "POST",
1546
- headers: this.apiHeaders("application/json"),
1547
- body: JSON.stringify({
1548
- agentId: id.toString(),
1549
- tokenIn: intent.tokenIn,
1550
- tokenOut: intent.tokenOut,
1551
- amountIn: intent.amountIn.toString(),
1552
- slippageBps: intent.maxSlippageBps ?? 50,
1553
- taker: this.account.address,
1554
- ...configHash && { configHash }
1555
- })
1556
- });
1557
- if (!response.ok) {
1558
- const error = await response.json().catch(() => ({ error: response.statusText }));
1559
- throw new Error(`Failed to build trade: ${error.error || error.message}`);
1560
- }
1561
- return response.json();
1562
- }
1563
- /**
1564
- * Check if an address is the ETH sentinel or WETH
1565
- */
1566
- isETHAddress(addr) {
1567
- const lower = addr.toLowerCase();
1568
- return lower === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" || lower === "0x0000000000000000000000000000000000000000" || lower === "0x4200000000000000000000000000000000000006";
1569
- }
1570
- /**
1571
- * Read current ERC-20 allowance for a (token, spender) pair
1572
- */
1573
- async getAllowance(token, spender) {
1574
- try {
1575
- const data = await this.publicClient.call({
1576
- to: token,
1577
- data: `0xdd62ed3e${this.account.address.slice(2).padStart(64, "0")}${spender.slice(2).padStart(64, "0")}`
1578
- });
1579
- if (!data.data || data.data === "0x") return 0n;
1580
- return BigInt(data.data);
1581
- } catch {
1582
- return 0n;
1583
- }
1584
- }
1585
- /**
1586
- * Approve token spending for router.
1587
- * Verifies the approval receipt succeeded — previous version silently continued
1588
- * on reverted approvals, causing downstream "transfer amount exceeds allowance" errors.
1589
- */
1590
- async approveToken(token, spender, amount) {
1591
- const approveData = this.encodeApprove(spender, amount);
1592
- const hash = await this.walletClient.sendTransaction({
1593
- account: this.account,
1594
- chain: CHAIN_CONFIG[this.network],
1595
- to: token,
1596
- data: approveData
1597
- });
1598
- const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
1599
- if (receipt.status === "reverted") {
1600
- throw new Error(
1601
- `Token approval reverted: approve(${spender}, ${amount}) on ${token}. The token may have non-standard approval behavior.`
1602
- );
1603
- }
1604
- return hash;
1605
- }
1606
- /**
1607
- * Encode ERC20 approve calldata
1608
- */
1609
- encodeApprove(spender, amount) {
1610
- const selector = "0x095ea7b3";
1611
- const paddedSpender = spender.slice(2).padStart(64, "0");
1612
- const paddedAmount = amount.toString(16).padStart(64, "0");
1613
- return `${selector}${paddedSpender}${paddedAmount}`;
1614
- }
1615
- // ============ M2M Services ============
1616
- /**
1617
- * Request a service from another agent
1618
- */
1619
- async requestService(request) {
1620
- const agentId = await this.getAgentId();
1621
- if (!agentId) throw new Error("Agent not registered");
1622
- const apiUrl = EXAGENT_API_CONFIG[this.network];
1623
- const response = await fetch(`${apiUrl}/v1/services/request`, {
1624
- method: "POST",
1625
- headers: this.apiHeaders("application/json"),
1626
- body: JSON.stringify({
1627
- requesterId: agentId.toString(),
1628
- ...request
1629
- })
1630
- });
1631
- if (!response.ok) {
1632
- throw new Error(`Failed to request service: ${response.statusText}`);
1633
- }
1634
- return response.json();
1635
- }
1636
- /**
1637
- * List available services
1638
- */
1639
- async listServices(serviceType) {
1640
- const apiUrl = EXAGENT_API_CONFIG[this.network];
1641
- const params = new URLSearchParams();
1642
- if (serviceType) params.set("type", serviceType);
1643
- const response = await fetch(`${apiUrl}/v1/services?${params}`, {
1644
- headers: this.apiHeaders()
1645
- });
1646
- return response.json();
1647
- }
1648
- // ============ Leaderboards ============
1649
- /**
1650
- * Get agent leaderboard
1651
- */
1652
- async getLeaderboard(options) {
1653
- const apiUrl = EXAGENT_API_CONFIG[this.network];
1654
- const params = new URLSearchParams();
1655
- if (options?.category) params.set("category", options.category);
1656
- if (options?.limit) params.set("limit", options.limit.toString());
1657
- if (options?.offset) params.set("offset", options.offset.toString());
1658
- const response = await fetch(`${apiUrl}/v1/rankings/leaderboard?${params}`, {
1659
- headers: this.apiHeaders()
1660
- });
1661
- const data = await response.json();
1662
- return data.agents ?? [];
1663
- }
1664
- // ============ Vault Functions (Phase 4: Copy Trading) ============
1665
- /**
1666
- * Get a vault interface for interacting with an ExagentVault
1667
- * @param vaultAddress The vault contract address
1668
- * @returns ExagentVault instance
1669
- */
1670
- getVault(vaultAddress) {
1671
- return new ExagentVault(
1672
- vaultAddress,
1673
- this.publicClient,
1674
- this.walletClient,
1675
- CHAIN_CONFIG[this.network],
1676
- this.account
1677
- );
1678
- }
1679
- /**
1680
- * List all vaults for a given agent
1681
- * @param agentId Agent ID to get vaults for
1682
- * @returns Array of vault summaries
1683
- */
1684
- async getAgentVaults(agentId) {
1685
- const id = agentId ?? await this.getAgentId();
1686
- if (!id) throw new Error("Agent not registered");
1687
- const apiUrl = EXAGENT_API_CONFIG[this.network];
1688
- const response = await fetch(`${apiUrl}/v1/vaults?agentId=${id}`, {
1689
- headers: this.apiHeaders()
1690
- });
1691
- if (!response.ok) {
1692
- throw new Error(`Failed to get vaults: ${response.statusText}`);
1693
- }
1694
- return response.json();
1695
- }
1696
- /**
1697
- * List all vaults the user has positions in
1698
- * @param userAddress User address (defaults to connected wallet)
1699
- * @returns Array of vault summaries with user position info
1700
- */
1701
- async getUserVaults(userAddress) {
1702
- const user = userAddress ?? this.account.address;
1703
- const apiUrl = EXAGENT_API_CONFIG[this.network];
1704
- const response = await fetch(`${apiUrl}/v1/vaults?depositor=${user}`, {
1705
- headers: this.apiHeaders()
1706
- });
1707
- if (!response.ok) {
1708
- throw new Error(`Failed to get user vaults: ${response.statusText}`);
1709
- }
1710
- return response.json();
1711
- }
1712
- /**
1713
- * List top performing vaults
1714
- * @param options Filter options
1715
- * @returns Array of vault summaries sorted by performance
1716
- */
1717
- async getTopVaults(options) {
1718
- const apiUrl = EXAGENT_API_CONFIG[this.network];
1719
- const params = new URLSearchParams();
1720
- if (options?.asset) params.set("asset", options.asset);
1721
- if (options?.minTvl) params.set("minTvl", options.minTvl.toString());
1722
- if (options?.period) params.set("period", options.period);
1723
- if (options?.limit) params.set("limit", options.limit.toString());
1724
- const response = await fetch(`${apiUrl}/v1/vaults/top?${params}`, {
1725
- headers: this.apiHeaders()
1726
- });
1727
- if (!response.ok) {
1728
- throw new Error(`Failed to get top vaults: ${response.statusText}`);
1729
- }
1730
- return response.json();
1731
- }
1732
- /**
1733
- * Deposit into a vault
1734
- * @param vaultAddress Vault contract address
1735
- * @param amount Amount of underlying asset to deposit
1736
- * @returns Transaction hash
1737
- */
1738
- async depositToVault(vaultAddress, amount) {
1739
- const vault = this.getVault(vaultAddress);
1740
- const info = await vault.getVaultInfo();
1741
- await vault.approveAsset(info.asset, amount);
1742
- return vault.deposit(amount);
1743
- }
1744
- /**
1745
- * Withdraw from a vault
1746
- * @param vaultAddress Vault contract address
1747
- * @param assets Amount of underlying asset to withdraw
1748
- * @returns Transaction hash
1749
- */
1750
- async withdrawFromVault(vaultAddress, assets) {
1751
- const vault = this.getVault(vaultAddress);
1752
- return vault.withdraw(assets);
1753
- }
1754
- /**
1755
- * Redeem shares from a vault
1756
- * @param vaultAddress Vault contract address
1757
- * @param shares Amount of shares to redeem
1758
- * @returns Transaction hash
1759
- */
1760
- async redeemFromVault(vaultAddress, shares) {
1761
- const vault = this.getVault(vaultAddress);
1762
- return vault.redeem(shares);
1763
- }
1764
- // ============ ERC-8004 Global Agent Identity ============
1765
- /**
1766
- * Get the ERC-8004 global agent identifier for the current agent
1767
- *
1768
- * Format: eip155:{chainId}:{registryAddress}:{agentId}
1769
- *
1770
- * This creates a universally unique identifier that can be used for
1771
- * cross-protocol agent discovery and interoperability.
1772
- *
1773
- * @returns The global agent ID string
1774
- * @throws Error if agent is not registered
1775
- *
1776
- * @example
1777
- * ```typescript
1778
- * const globalId = await exagent.getGlobalAgentId();
1779
- * // => "eip155:8453:0xABC...DEF:42"
1780
- * ```
1781
- */
1782
- async getGlobalAgentId() {
1783
- const agentId = await this.getAgentId();
1784
- if (!agentId) throw new Error("Agent not registered");
1785
- const chain = CHAIN_CONFIG[this.network];
1786
- const addresses = CONTRACT_ADDRESSES[this.network];
1787
- return buildGlobalAgentId(
1788
- chain.id,
1789
- addresses.agentRegistry,
1790
- agentId
1791
- );
1792
- }
1793
- /**
1794
- * Build a global agent ID for any agent (static utility)
1795
- *
1796
- * @param chainId - EVM chain ID
1797
- * @param registryAddress - ExagentRegistry contract address
1798
- * @param agentId - On-chain agent ID
1799
- * @returns ERC-8004 global agent identifier
1800
- */
1801
- static buildGlobalAgentId(chainId, registryAddress, agentId) {
1802
- return buildGlobalAgentId(chainId, registryAddress, agentId);
1803
- }
1804
- // ============ Helpers ============
1805
- /**
1806
- * Get the agent's wallet address
1807
- */
1808
- get address() {
1809
- return this.account.address;
1810
- }
1811
- /**
1812
- * Sign a message with the wallet's private key (EIP-191 personal sign)
1813
- * @param message The message to sign — string for UTF-8, or { raw: Hex } for raw bytes
1814
- * @returns The signature
1815
- */
1816
- async signMessage(message) {
1817
- return this.walletClient.signMessage({ account: this.account, message });
1818
- }
1819
- async uploadMetadata(metadata) {
1820
- const apiUrl = EXAGENT_API_CONFIG[this.network];
1821
- const response = await fetch(`${apiUrl}/v1/metadata/upload`, {
1822
- method: "POST",
1823
- headers: this.apiHeaders("application/json"),
1824
- body: JSON.stringify(metadata)
1825
- });
1826
- if (!response.ok) {
1827
- throw new Error(`Failed to upload metadata: ${response.statusText}`);
1828
- }
1829
- const result = await response.json();
1830
- return result.uri;
1831
- }
1832
- parseAgentIdFromReceipt(receipt) {
1833
- const registryAddress = this.registry.address.toLowerCase();
1834
- const AGENT_REGISTERED_TOPIC = keccak2562(
1835
- toHex("AgentRegistered(uint256,address,string,string)")
1836
- );
1837
- const TRANSFER_TOPIC = keccak2562(
1838
- toHex("Transfer(address,address,uint256)")
1839
- );
1840
- const ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000000000000000000000000000";
1841
- for (const log of receipt.logs) {
1842
- if (log.address.toLowerCase() !== registryAddress) continue;
1843
- if (log.topics[0] === AGENT_REGISTERED_TOPIC && log.topics[1]) {
1844
- return BigInt(log.topics[1]);
1845
- }
1846
- }
1847
- for (const log of receipt.logs) {
1848
- if (log.address.toLowerCase() !== registryAddress) continue;
1849
- if (log.topics[0] === TRANSFER_TOPIC && log.topics[1] === ZERO_ADDRESS2 && log.topics[3]) {
1850
- return BigInt(log.topics[3]);
1851
- }
1852
- }
1853
- throw new Error(
1854
- "Could not extract agent ID from transaction receipt.\nNeither AgentRegistered nor ERC721 Transfer event found in logs.\nCheck the transaction on BaseScan to verify registration succeeded."
1855
- );
1856
- }
1857
- };
1858
-
1859
- // src/contracts/vault-factory.ts
1860
- var EXAGENT_VAULT_FACTORY_ABI = [
1861
- // Read functions
1862
- { type: "function", name: "owner", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
1863
- { type: "function", name: "registry", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
1864
- { type: "function", name: "router", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
1865
- { type: "function", name: "feeCollector", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
1866
- { type: "function", name: "stakingContract", inputs: [], outputs: [{ type: "address" }], stateMutability: "view" },
1867
- { type: "function", name: "VERIFIED_VEXA_REQUIREMENT", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
1868
- { type: "function", name: "MIN_SEED_AMOUNT", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
1869
- { type: "function", name: "UNVERIFIED_CAP_MULTIPLIER", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
1870
- { type: "function", name: "VERIFIED_CAP_MULTIPLIER", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view" },
1871
- { type: "function", name: "allowedAssets", inputs: [{ name: "asset", type: "address" }], outputs: [{ type: "bool" }], stateMutability: "view" },
1872
- { type: "function", name: "vaults", inputs: [{ name: "agentId", type: "uint256" }, { name: "asset", type: "address" }], outputs: [{ type: "address" }], stateMutability: "view" },
1873
- { type: "function", name: "agentVaultCount", inputs: [{ name: "agentId", type: "uint256" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
1874
- // NOTE: canCreateVault() and canCreateVerifiedVault() do not exist on the
1875
- // deployed ExagentVaultFactoryV2 contract. SDK methods that called these
1876
- // now use inline validation (allowedAssets, vaults, MIN_SEED_AMOUNT) instead.
1877
- // Write functions
1878
- {
1879
- type: "function",
1880
- name: "createVault",
1881
- inputs: [
1882
- { name: "agentId", type: "uint256" },
1883
- { name: "asset", type: "address" },
1884
- { name: "seedAmount", type: "uint256" },
1885
- { name: "name", type: "string" },
1886
- { name: "symbol", type: "string" },
1887
- { name: "feeRecipient", type: "address" }
1888
- ],
1889
- outputs: [{ type: "address" }],
1890
- stateMutability: "nonpayable"
1891
- },
1892
- // Events
1893
- {
1894
- type: "event",
1895
- name: "VaultCreated",
1896
- inputs: [
1897
- { name: "vault", type: "address", indexed: true },
1898
- { name: "agentId", type: "uint256", indexed: true },
1899
- { name: "asset", type: "address", indexed: true },
1900
- { name: "creator", type: "address", indexed: false },
1901
- { name: "name", type: "string", indexed: false },
1902
- { name: "symbol", type: "string", indexed: false }
1903
- ]
1904
- },
1905
- {
1906
- type: "event",
1907
- name: "AllowedAssetUpdated",
1908
- inputs: [
1909
- { name: "asset", type: "address", indexed: true },
1910
- { name: "allowed", type: "bool", indexed: false }
1911
- ]
1912
- }
1913
- ];
1914
- var ExagentVaultFactory = class {
1915
- address;
1916
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1917
- publicClient;
1918
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1919
- walletClient;
1920
- chain;
1921
- account;
1922
- constructor(factoryAddress, publicClient, walletClient, chain, account) {
1923
- this.address = factoryAddress;
1924
- this.publicClient = publicClient;
1925
- this.walletClient = walletClient;
1926
- if (!chain) {
1927
- throw new Error("Chain parameter is required");
1928
- }
1929
- this.chain = chain;
1930
- this.account = account;
1931
- }
1932
- // ============ Read Functions ============
1933
- /**
1934
- * Get vault creation requirements (mainnet: seed-based, no burn fee)
1935
- */
1936
- async getRequirements() {
1937
- const [minSeedAmount, unverifiedCapMultiplier, verifiedCapMultiplier] = await Promise.all([
1938
- this.publicClient.readContract({
1939
- address: this.address,
1940
- abi: EXAGENT_VAULT_FACTORY_ABI,
1941
- functionName: "MIN_SEED_AMOUNT"
1942
- }),
1943
- this.publicClient.readContract({
1944
- address: this.address,
1945
- abi: EXAGENT_VAULT_FACTORY_ABI,
1946
- functionName: "UNVERIFIED_CAP_MULTIPLIER"
1947
- }),
1948
- this.publicClient.readContract({
1949
- address: this.address,
1950
- abi: EXAGENT_VAULT_FACTORY_ABI,
1951
- functionName: "VERIFIED_CAP_MULTIPLIER"
1952
- })
1953
- ]);
1954
- return {
1955
- minSeedAmount,
1956
- unverifiedCapMultiplier,
1957
- verifiedCapMultiplier
1958
- };
1959
- }
1960
- /**
1961
- * Check if an address can create a vault.
1962
- * Performs inline validation since the on-chain contract does not expose
1963
- * a canCreateVault() view function.
1964
- * @param creator Address to check
1965
- */
1966
- async canCreateVault(creator) {
1967
- try {
1968
- const isUsdcAllowed = await this.isAssetAllowed("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913");
1969
- if (!isUsdcAllowed) {
1970
- return { canCreate: false, reason: "USDC is not an allowed asset" };
1971
- }
1972
- } catch {
1973
- }
1974
- return { canCreate: true, reason: "" };
1975
- }
1976
- /**
1977
- * Check if an asset is whitelisted for vault creation
1978
- * @param asset Asset address to check
1979
- */
1980
- async isAssetAllowed(asset) {
1981
- return this.publicClient.readContract({
1982
- address: this.address,
1983
- abi: EXAGENT_VAULT_FACTORY_ABI,
1984
- functionName: "allowedAssets",
1985
- args: [asset]
1986
- });
1987
- }
1988
- /**
1989
- * Get existing vault for an agent and asset
1990
- * @param agentId Agent ID
1991
- * @param asset Asset address
1992
- * @returns Vault address or zero address if none exists
1993
- */
1994
- async getVault(agentId, asset) {
1995
- return this.publicClient.readContract({
1996
- address: this.address,
1997
- abi: EXAGENT_VAULT_FACTORY_ABI,
1998
- functionName: "vaults",
1999
- args: [agentId, asset]
2000
- });
2001
- }
2002
- /**
2003
- * Get count of vaults for an agent
2004
- * @param agentId Agent ID
2005
- */
2006
- async getAgentVaultCount(agentId) {
2007
- return this.publicClient.readContract({
2008
- address: this.address,
2009
- abi: EXAGENT_VAULT_FACTORY_ABI,
2010
- functionName: "agentVaultCount",
2011
- args: [agentId]
2012
- });
2013
- }
2014
- /**
2015
- * Check if agent already has a vault for an asset
2016
- * @param agentId Agent ID
2017
- * @param asset Asset address
2018
- */
2019
- async hasVault(agentId, asset) {
2020
- const vault = await this.getVault(agentId, asset);
2021
- return vault !== "0x0000000000000000000000000000000000000000";
2022
- }
2023
- // ============ Write Functions ============
2024
- /**
2025
- * Create a new vault for an agent (mainnet: requires seed amount)
2026
- * @param options Vault creation options including seedAmount
2027
- * @returns Transaction hash
2028
- */
2029
- async createVault(options) {
2030
- if (!this.walletClient || !this.account) {
2031
- throw new Error("Wallet client required for vault creation");
2032
- }
2033
- const feeRecipient = options.feeRecipient ?? this.account.address;
2034
- const hash = await this.walletClient.writeContract({
2035
- address: this.address,
2036
- abi: EXAGENT_VAULT_FACTORY_ABI,
2037
- functionName: "createVault",
2038
- args: [options.agentId, options.asset, options.seedAmount, options.name, options.symbol, feeRecipient],
2039
- account: this.account,
2040
- chain: this.chain
2041
- });
2042
- return hash;
2043
- }
2044
- // ============ Convenience Methods ============
2045
- /**
2046
- * Check all requirements and create a vault if possible
2047
- * Returns detailed status about each requirement
2048
- */
2049
- async checkAndCreateVault(agentId, asset, seedAmount, name, symbol, feeRecipient) {
2050
- const [canCreateResult, assetAllowed, hasExisting] = await Promise.all([
2051
- this.canCreateVault(this.account?.address ?? "0x0000000000000000000000000000000000000000"),
2052
- this.isAssetAllowed(asset),
2053
- this.hasVault(agentId, asset)
2054
- ]);
2055
- const checks = {
2056
- canCreate: canCreateResult.canCreate,
2057
- canCreateReason: canCreateResult.reason,
2058
- assetAllowed,
2059
- hasExistingVault: hasExisting
2060
- };
2061
- if (!canCreateResult.canCreate) {
2062
- return {
2063
- success: false,
2064
- error: `Cannot create vault: ${canCreateResult.reason}`,
2065
- checks
2066
- };
2067
- }
2068
- if (!assetAllowed) {
2069
- return {
2070
- success: false,
2071
- error: `Asset ${asset} is not whitelisted for vault creation`,
2072
- checks
2073
- };
2074
- }
2075
- if (hasExisting) {
2076
- const existingVault = await this.getVault(agentId, asset);
2077
- return {
2078
- success: false,
2079
- error: `Vault already exists for this agent and asset`,
2080
- vaultAddress: existingVault,
2081
- checks
2082
- };
2083
- }
2084
- try {
2085
- const txHash = await this.createVault({
2086
- agentId,
2087
- asset,
2088
- seedAmount,
2089
- name,
2090
- symbol,
2091
- feeRecipient
2092
- });
2093
- const receipt = await this.publicClient.waitForTransactionReceipt({ hash: txHash });
2094
- if (receipt.status !== "success") {
2095
- return {
2096
- success: false,
2097
- error: "Transaction failed",
2098
- txHash,
2099
- checks
2100
- };
2101
- }
2102
- const vaultAddress = await this.getVault(agentId, asset);
2103
- return {
2104
- success: true,
2105
- vaultAddress,
2106
- txHash,
2107
- checks
2108
- };
2109
- } catch (error) {
2110
- return {
2111
- success: false,
2112
- error: error instanceof Error ? error.message : "Unknown error creating vault",
2113
- checks
2114
- };
2115
- }
2116
- }
2117
- };
2118
- export {
2119
- CHAIN_CONFIG,
2120
- CONTRACT_ADDRESSES,
2121
- DEFAULT_RPC_URL,
2122
- DEX_ADDRESSES,
2123
- EXAGENT_API_CONFIG,
2124
- EXAGENT_REGISTRY_ABI,
2125
- EXAGENT_ROUTER_ABI,
2126
- EXAGENT_VAULT_FACTORY_ABI,
2127
- ExagentClient,
2128
- ExagentRegistry,
2129
- ExagentVault,
2130
- ExagentVaultFactory,
2131
- SDK_VERSION,
2132
- ZERO_X_CONFIG,
2133
- buildGlobalAgentId,
2134
- getRpcUrl,
2135
- parseGlobalAgentId
2136
- };