@exagent/sdk 0.1.21 → 0.2.0

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