@agether/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +480 -0
  2. package/dist/cli.d.mts +2 -0
  3. package/dist/cli.d.ts +19 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +2149 -0
  6. package/dist/cli.mjs +0 -0
  7. package/dist/clients/AgentIdentityClient.d.ts +163 -0
  8. package/dist/clients/AgentIdentityClient.d.ts.map +1 -0
  9. package/dist/clients/AgentIdentityClient.js +293 -0
  10. package/dist/clients/AgetherClient.d.ts +101 -0
  11. package/dist/clients/AgetherClient.d.ts.map +1 -0
  12. package/dist/clients/AgetherClient.js +272 -0
  13. package/dist/clients/ScoringClient.d.ts +138 -0
  14. package/dist/clients/ScoringClient.d.ts.map +1 -0
  15. package/dist/clients/ScoringClient.js +135 -0
  16. package/dist/clients/VaultClient.d.ts +62 -0
  17. package/dist/clients/VaultClient.d.ts.map +1 -0
  18. package/dist/clients/VaultClient.js +157 -0
  19. package/dist/clients/WalletClient.d.ts +73 -0
  20. package/dist/clients/WalletClient.d.ts.map +1 -0
  21. package/dist/clients/WalletClient.js +174 -0
  22. package/dist/clients/X402Client.d.ts +61 -0
  23. package/dist/clients/X402Client.d.ts.map +1 -0
  24. package/dist/clients/X402Client.js +303 -0
  25. package/dist/index.d.mts +932 -0
  26. package/dist/index.d.ts +932 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +1680 -0
  29. package/dist/index.mjs +1610 -0
  30. package/dist/types/index.d.ts +220 -0
  31. package/dist/types/index.d.ts.map +1 -0
  32. package/dist/types/index.js +52 -0
  33. package/dist/utils/abis.d.ts +21 -0
  34. package/dist/utils/abis.d.ts.map +1 -0
  35. package/dist/utils/abis.js +134 -0
  36. package/dist/utils/config.d.ts +31 -0
  37. package/dist/utils/config.d.ts.map +1 -0
  38. package/dist/utils/config.js +117 -0
  39. package/dist/utils/format.d.ts +44 -0
  40. package/dist/utils/format.d.ts.map +1 -0
  41. package/dist/utils/format.js +75 -0
  42. package/package.json +57 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,1610 @@
1
+ // src/clients/AgetherClient.ts
2
+ import { ethers, Contract } from "ethers";
3
+
4
+ // src/types/index.ts
5
+ var CreditStatus = /* @__PURE__ */ ((CreditStatus3) => {
6
+ CreditStatus3[CreditStatus3["None"] = 0] = "None";
7
+ CreditStatus3[CreditStatus3["Pending"] = 1] = "Pending";
8
+ CreditStatus3[CreditStatus3["Active"] = 2] = "Active";
9
+ CreditStatus3[CreditStatus3["Frozen"] = 3] = "Frozen";
10
+ CreditStatus3[CreditStatus3["Closed"] = 4] = "Closed";
11
+ CreditStatus3[CreditStatus3["Defaulted"] = 5] = "Defaulted";
12
+ return CreditStatus3;
13
+ })(CreditStatus || {});
14
+ var ChainId = /* @__PURE__ */ ((ChainId3) => {
15
+ ChainId3[ChainId3["Ethereum"] = 1] = "Ethereum";
16
+ ChainId3[ChainId3["Base"] = 8453] = "Base";
17
+ ChainId3[ChainId3["BaseSepolia"] = 84532] = "BaseSepolia";
18
+ ChainId3[ChainId3["Sepolia"] = 11155111] = "Sepolia";
19
+ ChainId3[ChainId3["Hardhat"] = 31337] = "Hardhat";
20
+ return ChainId3;
21
+ })(ChainId || {});
22
+ var AgetherError = class extends Error {
23
+ constructor(message, code, details) {
24
+ super(message);
25
+ this.code = code;
26
+ this.details = details;
27
+ this.name = "AgetherError";
28
+ }
29
+ };
30
+ var InsufficientCreditError = class extends AgetherError {
31
+ constructor(available, requested) {
32
+ super(
33
+ `Insufficient credit: available ${available}, requested ${requested}`,
34
+ "INSUFFICIENT_CREDIT",
35
+ { available: available.toString(), requested: requested.toString() }
36
+ );
37
+ }
38
+ };
39
+ var ScoringRejectedError = class extends AgetherError {
40
+ constructor(riskScore, reason) {
41
+ super(
42
+ `Scoring rejected: risk score ${riskScore}${reason ? `, ${reason}` : ""}`,
43
+ "SCORING_REJECTED",
44
+ { riskScore, reason }
45
+ );
46
+ }
47
+ };
48
+ var CreditNotActiveError = class extends AgetherError {
49
+ constructor(account, status) {
50
+ super(
51
+ `Credit line for ${account} is not active (status: ${CreditStatus[status]})`,
52
+ "CREDIT_NOT_ACTIVE",
53
+ { account, status: CreditStatus[status] }
54
+ );
55
+ }
56
+ };
57
+
58
+ // src/utils/abis.ts
59
+ var IDENTITY_REGISTRY_ABI = [
60
+ "function ownerOf(uint256 agentId) view returns (address)",
61
+ "function balanceOf(address owner) view returns (uint256)",
62
+ "function totalSupply() view returns (uint256)",
63
+ "function exists(uint256 agentId) view returns (bool)",
64
+ "function register() returns (uint256 agentId)",
65
+ "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
66
+ ];
67
+ var ACCOUNT_FACTORY_ABI = [
68
+ "function getAccount(uint256 agentId) view returns (address)",
69
+ "function accountExists(uint256 agentId) view returns (bool)",
70
+ "function predictAddress(uint256 agentId) view returns (address)",
71
+ "function totalAccounts() view returns (uint256)",
72
+ "function getAgentId(address account) view returns (uint256)",
73
+ "function createAccount(uint256 agentId) returns (address account)",
74
+ "event AccountCreated(uint256 indexed agentId, address indexed account, address indexed owner)"
75
+ ];
76
+ var AGENT_ACCOUNT_ABI = [
77
+ "function agentId() view returns (uint256)",
78
+ "function owner() view returns (address)",
79
+ "function balanceOf(address token) view returns (uint256)",
80
+ "function ethBalance() view returns (uint256)",
81
+ "function execute(address target, uint256 value, bytes data) payable returns (bytes)",
82
+ "function drawCredit(address creditProvider, uint256 amount)",
83
+ "function repayCredit(address creditProvider, uint256 amount)",
84
+ "function fund(address token, uint256 amount)",
85
+ "function withdraw(address token, uint256 amount, address to)"
86
+ ];
87
+ var CREDIT_PROVIDER_ABI = [
88
+ "function asset() view returns (address)",
89
+ "function isEligible(address account) view returns (bool)",
90
+ "function getCreditInfo(address account) view returns (tuple(uint256 limit, uint256 used, uint256 available, uint256 accruedInterest, uint256 aprBps, bool isActive, bool requiresCollateral))",
91
+ "function getTotalDebt(address account) view returns (uint256)",
92
+ "function maxDrawable(address account) view returns (uint256)",
93
+ "function draw(address account, uint256 amount)",
94
+ "function repay(address account, uint256 amount)",
95
+ "event CreditDrawn(address indexed account, uint256 amount, uint256 totalUsed)",
96
+ "event CreditRepaid(address indexed account, uint256 amount, uint256 totalUsed)",
97
+ "event CreditLineOpened(address indexed account, uint256 limit, uint256 aprBps)",
98
+ "event CreditLineClosed(address indexed account)"
99
+ ];
100
+ var REPUTATION_CREDIT_ABI = [
101
+ ...CREDIT_PROVIDER_ABI,
102
+ // Application / Approval flow
103
+ "function applyForCredit(address account, uint256 requestedLimit)",
104
+ "function approveCreditLine(address account, uint256 limit, uint256 aprBps)",
105
+ "function rejectCreditLine(address account, string reason)",
106
+ "function requestLimitIncrease(address account, uint256 newRequestedLimit)",
107
+ "function approveLimitIncrease(address account, uint256 newLimit)",
108
+ "function freezeCreditLine(address account)",
109
+ "function unfreezeCreditLine(address account)",
110
+ // Admin
111
+ "function setCreditLine(address account, uint256 limit, uint256 aprBps)",
112
+ "function openScoredCreditLine(address account)",
113
+ "function closeCreditLine(address account)",
114
+ "function declareDefault(address account)",
115
+ // View
116
+ "function getCreditLineStatus(address account) view returns (uint8)",
117
+ "function getCreditLineDetails(address account) view returns (uint256 agentId, uint256 limit, uint256 used, uint256 aprBps, uint256 accruedInterest, uint256 requestedLimit, uint256 createdAt, uint256 lastActivityAt, uint8 status)",
118
+ "function getAgentAccount(uint256 agentId) view returns (address)",
119
+ "function previewScoredLimit(uint256 agentId) view returns (uint256 limit, uint256 score, bool eligible)",
120
+ "function dailyDrawLimit() view returns (uint256)",
121
+ "function totalBorrowed() view returns (uint256)",
122
+ // Events
123
+ "event CreditApplied(uint256 indexed creditLineId, uint256 indexed agentId, uint256 requestedLimit)",
124
+ "event CreditApproved(uint256 indexed creditLineId, uint256 limit, uint256 aprBps)",
125
+ "event CreditRejected(uint256 indexed creditLineId, string reason)",
126
+ "event CreditFrozen(uint256 indexed creditLineId)",
127
+ "event CreditUnfrozen(uint256 indexed creditLineId)",
128
+ "event DefaultDeclared(address indexed account, uint256 amount)"
129
+ ];
130
+ var LP_VAULT_ABI = [
131
+ "function asset() view returns (address)",
132
+ "function totalAssets() view returns (uint256)",
133
+ "function totalSupply() view returns (uint256)",
134
+ "function balanceOf(address account) view returns (uint256)",
135
+ "function convertToShares(uint256 assets) view returns (uint256)",
136
+ "function convertToAssets(uint256 shares) view returns (uint256)",
137
+ "function previewDeposit(uint256 assets) view returns (uint256)",
138
+ "function previewRedeem(uint256 shares) view returns (uint256)",
139
+ "function deposit(uint256 assets, address receiver) returns (uint256)",
140
+ "function withdraw(uint256 assets, address receiver, address owner) returns (uint256)",
141
+ "function redeem(uint256 shares, address receiver, address owner) returns (uint256)",
142
+ "function totalBorrowed() view returns (uint256)",
143
+ "function availableLiquidity() view returns (uint256)",
144
+ "event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares)",
145
+ "event Withdraw(address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)"
146
+ ];
147
+ var AGENT_REPUTATION_ABI = [
148
+ "function getReputation(uint256 agentId) view returns (tuple(uint256 totalBorrowed, uint256 totalRepaid, uint256 totalInterestPaid, uint256 onTimePayments, uint256 latePayments, uint256 missedPayments, uint256 peakUtilization, uint256 avgUtilization, uint256 utilizationSamples, uint256 firstActivityAt, uint256 lastActivityAt, uint256 lastScoreUpdate, uint256 creditScore, uint256 creditLinesOpened, uint256 lastCreditOpenedAt, uint256 currentOutstanding, uint256 currentLimit, uint256 totalDeposits, uint256 totalWithdrawals))",
149
+ "function getCreditScore(uint256 agentId) view returns (uint256)",
150
+ "function getBayesianScore(uint256 agentId) view returns (uint256 combinedScore, uint256 confidence)",
151
+ "function getRecommendedLimit(uint256 agentId, uint256 baseLimit) view returns (uint256)",
152
+ "function isEligible(uint256 agentId, uint256 minScore) view returns (bool eligible, uint256 currentScore)",
153
+ "function getDecayedScore(uint256 agentId) view returns (uint256)",
154
+ "function getScoreExplanation(uint256 agentId) view returns (uint256 historical, uint256 currentRisk, uint256 utilization, uint256 onChainTx, uint256 newCredit, uint256 rawScore, uint256 decayedScore)",
155
+ "function getLoanPositions(uint256 agentId) view returns (tuple(uint256 amount, uint256 collateral, uint256 ltvBps, uint256 timestamp, bool liquidated, bool repaid, bool active)[])",
156
+ "function stressMultiplier() view returns (uint256)",
157
+ "function decayPeriod() view returns (uint256)"
158
+ ];
159
+ var VALIDATION_REGISTRY_ABI = [
160
+ "function isAgentCodeApproved(uint256 agentId) view returns (bool)",
161
+ "function isAgentCodeApprovedForTag(uint256 agentId, string tag) view returns (bool)",
162
+ "function getAgentValidations(uint256 agentId) view returns (bytes32[])"
163
+ ];
164
+ var ERC20_ABI = [
165
+ "function balanceOf(address account) view returns (uint256)",
166
+ "function allowance(address owner, address spender) view returns (uint256)",
167
+ "function approve(address spender, uint256 amount) returns (bool)",
168
+ "function transfer(address to, uint256 amount) returns (bool)",
169
+ "function transferFrom(address from, address to, uint256 amount) returns (bool)",
170
+ "function decimals() view returns (uint8)",
171
+ "function symbol() view returns (string)"
172
+ ];
173
+
174
+ // src/utils/config.ts
175
+ var CONTRACT_ADDRESSES = {
176
+ [1 /* Ethereum */]: {
177
+ accountFactory: "0x0000000000000000000000000000000000000000",
178
+ reputationCredit: "0x0000000000000000000000000000000000000000",
179
+ lpVault: "0x0000000000000000000000000000000000000000",
180
+ validationRegistry: "0x0000000000000000000000000000000000000000",
181
+ usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
182
+ identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
183
+ agentReputation: "0x0000000000000000000000000000000000000000"
184
+ },
185
+ [8453 /* Base */]: {
186
+ accountFactory: "0xeB72f248Ad9F4bf4024e8D9da75cf7AAD37B58f5",
187
+ reputationCredit: "0x57B2B4ef3e7B8BE5FC86c6369602125d240F552A",
188
+ lpVault: "0x612A80D6c3175F8283e9C7EE71d5177fE9acc338",
189
+ validationRegistry: "0x8842f2383A86134Dd80c3Ecf6Bbae2e38396A5ec",
190
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
191
+ identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
192
+ agentReputation: "0xF1bed094D4E33E47CC8C72E086FFFde09e2211b4",
193
+ morphoCredit: "0x7dFfa40E17471F7f26F5662D0F07a31977F47BeB"
194
+ },
195
+ [84532 /* BaseSepolia */]: {
196
+ accountFactory: "0x0000000000000000000000000000000000000000",
197
+ reputationCredit: "0x0000000000000000000000000000000000000000",
198
+ lpVault: "0x0000000000000000000000000000000000000000",
199
+ validationRegistry: "0x0000000000000000000000000000000000000000",
200
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
201
+ identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
202
+ agentReputation: "0x0000000000000000000000000000000000000000"
203
+ },
204
+ [11155111 /* Sepolia */]: {
205
+ accountFactory: "0x0000000000000000000000000000000000000000",
206
+ reputationCredit: "0x0000000000000000000000000000000000000000",
207
+ lpVault: "0x0000000000000000000000000000000000000000",
208
+ validationRegistry: "0x0000000000000000000000000000000000000000",
209
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
210
+ identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
211
+ agentReputation: "0x0000000000000000000000000000000000000000"
212
+ },
213
+ [31337 /* Hardhat */]: {
214
+ accountFactory: "0x0000000000000000000000000000000000000000",
215
+ reputationCredit: "0x0000000000000000000000000000000000000000",
216
+ lpVault: "0x0000000000000000000000000000000000000000",
217
+ validationRegistry: "0x0000000000000000000000000000000000000000",
218
+ usdc: "0x56d4d6aEe0278c5Df2FA23Ecb32eC146C9446FDf",
219
+ identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
220
+ agentReputation: "0x0000000000000000000000000000000000000000",
221
+ morphoCredit: "0x0000000000000000000000000000000000000000"
222
+ }
223
+ };
224
+ var RPC_URLS = {
225
+ [1 /* Ethereum */]: "https://ethereum-rpc.publicnode.com",
226
+ [8453 /* Base */]: "https://base-rpc.publicnode.com",
227
+ [84532 /* BaseSepolia */]: "https://sepolia.base.org",
228
+ [11155111 /* Sepolia */]: "https://rpc.sepolia.org",
229
+ [31337 /* Hardhat */]: "http://127.0.0.1:8545"
230
+ };
231
+ var SCORING_ENDPOINTS = {
232
+ [1 /* Ethereum */]: "https://scoring.agether.ai/v1",
233
+ [8453 /* Base */]: "http://95.179.189.214:3001",
234
+ [84532 /* BaseSepolia */]: "http://95.179.189.214:3001",
235
+ [11155111 /* Sepolia */]: "https://scoring-testnet.agether.ai/v1",
236
+ [31337 /* Hardhat */]: "http://127.0.0.1:3001"
237
+ };
238
+ function getDefaultConfig(chainId) {
239
+ return {
240
+ chainId,
241
+ rpcUrl: RPC_URLS[chainId],
242
+ contracts: CONTRACT_ADDRESSES[chainId],
243
+ scoringEndpoint: SCORING_ENDPOINTS[chainId]
244
+ };
245
+ }
246
+ function createConfig(chainId, options) {
247
+ const defaultConfig = getDefaultConfig(chainId);
248
+ return {
249
+ ...defaultConfig,
250
+ ...options,
251
+ contracts: {
252
+ ...defaultConfig.contracts,
253
+ ...options?.contracts
254
+ }
255
+ };
256
+ }
257
+ function getUSDCAddress(chainId) {
258
+ return CONTRACT_ADDRESSES[chainId].usdc;
259
+ }
260
+
261
+ // src/clients/AgetherClient.ts
262
+ var AgetherClient = class _AgetherClient {
263
+ /**
264
+ * Undercollateralized credit is disabled for now.
265
+ * All credit operations go through MorphoCredit (overcollateralized).
266
+ */
267
+ _requireUndercollateralizedEnabled() {
268
+ throw new AgetherError(
269
+ "Undercollateralized credit is not available yet. Use MorphoCredit for overcollateralized lending via the CLI: agether morpho-deposit / morpho-draw / morpho-repay",
270
+ "UNDERCOLLATERALIZED_NOT_AVAILABLE"
271
+ );
272
+ }
273
+ constructor(options) {
274
+ this.config = options.config;
275
+ this.signer = options.signer;
276
+ this.agentId = options.agentId;
277
+ const provider = options.signer.provider;
278
+ if (!provider) {
279
+ throw new AgetherError("Signer must have a provider", "NO_PROVIDER");
280
+ }
281
+ this.accountFactory = new Contract(
282
+ options.config.contracts.accountFactory,
283
+ ACCOUNT_FACTORY_ABI,
284
+ options.signer
285
+ );
286
+ this.reputationCredit = new Contract(
287
+ options.config.contracts.reputationCredit,
288
+ REPUTATION_CREDIT_ABI,
289
+ options.signer
290
+ );
291
+ }
292
+ // ============ Static Factory ============
293
+ /**
294
+ * Create client from private key.
295
+ *
296
+ * Simplest usage — only needs key, agentId, and chainId:
297
+ * AgetherClient.fromPrivateKey(key, 42n, ChainId.Sepolia)
298
+ *
299
+ * All contract addresses and RPC URLs are resolved automatically.
300
+ */
301
+ static fromPrivateKey(privateKey, agentId, chainIdOrConfig) {
302
+ const config = typeof chainIdOrConfig === "number" ? getDefaultConfig(chainIdOrConfig) : chainIdOrConfig;
303
+ const provider = new ethers.JsonRpcProvider(config.rpcUrl);
304
+ const signer = new ethers.Wallet(privateKey, provider);
305
+ return new _AgetherClient({ config, signer, agentId });
306
+ }
307
+ // ============ Account Management ============
308
+ /**
309
+ * Create an AgentAccount (smart wallet) for this agent.
310
+ * Returns the account address.
311
+ */
312
+ async createAccount() {
313
+ const tx = await this.accountFactory.createAccount(this.agentId);
314
+ const receipt = await tx.wait();
315
+ const event = receipt.logs.map((log) => {
316
+ try {
317
+ return this.accountFactory.interface.parseLog(log);
318
+ } catch {
319
+ return null;
320
+ }
321
+ }).find((e) => e?.name === "AccountCreated");
322
+ if (event) {
323
+ this.accountAddress = event.args.account;
324
+ } else {
325
+ this.accountAddress = await this.accountFactory.getAccount(this.agentId);
326
+ }
327
+ return this.accountAddress;
328
+ }
329
+ /**
330
+ * Get the AgentAccount address for this agent.
331
+ */
332
+ async getAccountAddress() {
333
+ if (this.accountAddress) return this.accountAddress;
334
+ const addr = await this.accountFactory.getAccount(this.agentId);
335
+ if (addr === ethers.ZeroAddress) {
336
+ throw new AgetherError(
337
+ "No account found. Create one first with createAccount().",
338
+ "NO_ACCOUNT"
339
+ );
340
+ }
341
+ this.accountAddress = addr;
342
+ return addr;
343
+ }
344
+ /**
345
+ * Check if an account already exists.
346
+ */
347
+ async accountExists() {
348
+ return this.accountFactory.accountExists(this.agentId);
349
+ }
350
+ // ============ Credit Application ============
351
+ /**
352
+ * Apply for a credit line.
353
+ * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
354
+ */
355
+ async apply(_limitOrApplication) {
356
+ this._requireUndercollateralizedEnabled();
357
+ }
358
+ // ============ Credit Line Info ============
359
+ /**
360
+ * Get full credit line details from ReputationCredit.
361
+ */
362
+ async getCreditLine() {
363
+ const account = await this.getAccountAddress();
364
+ const data = await this.reputationCredit.getCreditLineDetails(account);
365
+ return {
366
+ agentId: data[0],
367
+ limit: data[1],
368
+ used: data[2],
369
+ aprBps: data[3],
370
+ accruedInterest: data[4],
371
+ requestedLimit: data[5],
372
+ createdAt: data[6],
373
+ lastActivityAt: data[7],
374
+ status: Number(data[8])
375
+ };
376
+ }
377
+ /**
378
+ * Get ICreditProvider.CreditInfo view.
379
+ */
380
+ async getCreditInfo() {
381
+ const account = await this.getAccountAddress();
382
+ const info = await this.reputationCredit.getCreditInfo(account);
383
+ return {
384
+ limit: info.limit,
385
+ used: info.used,
386
+ available: info.available,
387
+ accruedInterest: info.accruedInterest,
388
+ aprBps: info.aprBps,
389
+ isActive: info.isActive,
390
+ requiresCollateral: info.requiresCollateral
391
+ };
392
+ }
393
+ /**
394
+ * Get credit line status.
395
+ */
396
+ async getStatus() {
397
+ const account = await this.getAccountAddress();
398
+ const status = await this.reputationCredit.getCreditLineStatus(account);
399
+ return Number(status);
400
+ }
401
+ /**
402
+ * Get available credit.
403
+ */
404
+ async getAvailableCredit() {
405
+ const account = await this.getAccountAddress();
406
+ return this.reputationCredit.maxDrawable(account);
407
+ }
408
+ /**
409
+ * Get total debt (principal + interest).
410
+ */
411
+ async getTotalDebt() {
412
+ const account = await this.getAccountAddress();
413
+ return this.reputationCredit.getTotalDebt(account);
414
+ }
415
+ /**
416
+ * Check if agent is eligible for credit.
417
+ */
418
+ async isEligible() {
419
+ const account = await this.getAccountAddress();
420
+ return this.reputationCredit.isEligible(account);
421
+ }
422
+ // ============ Draw & Repay ============
423
+ /**
424
+ * Draw funds from credit line via AgentAccount.
425
+ * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
426
+ */
427
+ async draw(_amountOrRequest) {
428
+ this._requireUndercollateralizedEnabled();
429
+ }
430
+ /**
431
+ * Repay credit line debt via AgentAccount.
432
+ * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
433
+ */
434
+ async repay(_amountOrRequest) {
435
+ this._requireUndercollateralizedEnabled();
436
+ }
437
+ /**
438
+ * Repay full debt.
439
+ * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
440
+ */
441
+ async repayFull() {
442
+ this._requireUndercollateralizedEnabled();
443
+ }
444
+ // ============ Limit Increase ============
445
+ /**
446
+ * Request a limit increase on-chain.
447
+ * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
448
+ */
449
+ async requestLimitIncrease(_newLimit) {
450
+ this._requireUndercollateralizedEnabled();
451
+ }
452
+ // ============ x402 Payments ============
453
+ /**
454
+ * Pay for a service using credit line.
455
+ * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
456
+ */
457
+ async pay(_service, _amount, _asset = "USDC") {
458
+ this._requireUndercollateralizedEnabled();
459
+ }
460
+ // ============ Getters ============
461
+ get chainId() {
462
+ return this.config.chainId;
463
+ }
464
+ get contracts() {
465
+ return this.config.contracts;
466
+ }
467
+ get currentAccountAddress() {
468
+ return this.accountAddress;
469
+ }
470
+ /** Get the underlying signer */
471
+ getSigner() {
472
+ return this.signer;
473
+ }
474
+ };
475
+
476
+ // src/clients/ScoringClient.ts
477
+ import axios from "axios";
478
+ var ScoringClient = class {
479
+ constructor(endpoint, apiKey) {
480
+ this.client = axios.create({
481
+ baseURL: endpoint,
482
+ headers: {
483
+ "Content-Type": "application/json",
484
+ ...apiKey && { Authorization: `Bearer ${apiKey}` }
485
+ },
486
+ timeout: 3e4
487
+ });
488
+ }
489
+ /**
490
+ * Evaluate a credit application (does not submit on-chain)
491
+ * Backend endpoint: POST /credit/evaluate
492
+ */
493
+ async evaluateCredit(request) {
494
+ try {
495
+ const response = await this.client.post("/credit/evaluate", {
496
+ agentId: request.agentId.toString(),
497
+ requestedLimit: request.requestedLimit.toString(),
498
+ codeHash: request.codeHash
499
+ });
500
+ const data = response.data;
501
+ if (!data.approved) {
502
+ throw new ScoringRejectedError(data.riskScore, data.reason);
503
+ }
504
+ return {
505
+ approved: data.approved,
506
+ limit: BigInt(data.limit),
507
+ aprBps: data.aprBps,
508
+ riskScore: data.riskScore,
509
+ bayesianScore: data.bayesianScore ?? 0,
510
+ confidence: data.confidence ?? 0,
511
+ reason: data.reason
512
+ };
513
+ } catch (error) {
514
+ if (error instanceof ScoringRejectedError) {
515
+ throw error;
516
+ }
517
+ if (axios.isAxiosError(error)) {
518
+ throw new AgetherError(
519
+ `Scoring request failed: ${error.message}`,
520
+ "SCORING_REQUEST_FAILED",
521
+ { status: error.response?.status }
522
+ );
523
+ }
524
+ throw error;
525
+ }
526
+ }
527
+ /**
528
+ * Process a credit application (evaluate + on-chain approve/reject)
529
+ * Backend endpoint: POST /admin/credit/process
530
+ *
531
+ * Requires the credit application to be in Pending status on-chain
532
+ * (agent must have called applyForCredit first).
533
+ */
534
+ async processApplication(agentId, requestedLimit, codeHash) {
535
+ try {
536
+ const response = await this.client.post("/admin/credit/process", {
537
+ agentId: agentId.toString(),
538
+ requestedLimit: requestedLimit.toString(),
539
+ codeHash
540
+ });
541
+ return response.data;
542
+ } catch (error) {
543
+ if (axios.isAxiosError(error)) {
544
+ if (error.response?.status === 400) {
545
+ return error.response.data;
546
+ }
547
+ throw new AgetherError(
548
+ `Process application failed: ${error.message}`,
549
+ "PROCESS_APPLICATION_FAILED",
550
+ { status: error.response?.status }
551
+ );
552
+ }
553
+ throw error;
554
+ }
555
+ }
556
+ /**
557
+ * Get credit score for an agent
558
+ * Backend endpoint: GET /credit/score/:agentId
559
+ */
560
+ async getCreditScore(agentId) {
561
+ const response = await this.client.get(
562
+ `/credit/score/${agentId.toString()}`
563
+ );
564
+ return response.data;
565
+ }
566
+ /**
567
+ * Preview scored limit for an agent
568
+ * Backend endpoint: GET /credit/preview/:agentId
569
+ */
570
+ async previewScoredLimit(agentId) {
571
+ const response = await this.client.get(
572
+ `/credit/preview/${agentId.toString()}`
573
+ );
574
+ return response.data;
575
+ }
576
+ /**
577
+ * Get credit line info for an agent
578
+ * Backend endpoint: GET /credit/agent/:agentId
579
+ */
580
+ async getAgentCredit(agentId) {
581
+ const response = await this.client.get(
582
+ `/credit/agent/${agentId.toString()}`
583
+ );
584
+ return response.data;
585
+ }
586
+ /**
587
+ * Get protocol info
588
+ * Backend endpoint: GET /credit/protocol-info
589
+ */
590
+ async getProtocolInfo() {
591
+ const response = await this.client.get("/credit/protocol-info");
592
+ return response.data;
593
+ }
594
+ /**
595
+ * Get backend service status
596
+ * Backend endpoint: GET /status
597
+ */
598
+ async getStatus() {
599
+ const response = await this.client.get("/status");
600
+ return response.data;
601
+ }
602
+ /**
603
+ * Get full OCCR score explanation with subscores, decay, and loan positions
604
+ * Backend endpoint: GET /agents/:agentId/score-explanation
605
+ */
606
+ async getScoreExplanation(agentId) {
607
+ const response = await this.client.get(
608
+ `/agents/${agentId.toString()}/score-explanation`
609
+ );
610
+ return response.data;
611
+ }
612
+ };
613
+
614
+ // src/clients/VaultClient.ts
615
+ import { Contract as Contract2 } from "ethers";
616
+ var VaultClient = class {
617
+ constructor(options) {
618
+ this.config = options.config;
619
+ this.signer = options.signer;
620
+ this.vault = new Contract2(
621
+ options.config.contracts.lpVault,
622
+ LP_VAULT_ABI,
623
+ options.signer
624
+ );
625
+ }
626
+ // ============ LP Operations ============
627
+ /**
628
+ * Deposit assets to vault
629
+ */
630
+ async deposit(amount) {
631
+ const asset = await this.getAsset();
632
+ const allowance = await asset.allowance(
633
+ await this.signer.getAddress(),
634
+ this.config.contracts.lpVault
635
+ );
636
+ if (allowance < amount) {
637
+ const approveTx = await asset.approve(
638
+ this.config.contracts.lpVault,
639
+ amount
640
+ );
641
+ await approveTx.wait();
642
+ }
643
+ const receiver = await this.signer.getAddress();
644
+ const tx = await this.vault.deposit(amount, receiver);
645
+ const receipt = await tx.wait();
646
+ return {
647
+ txHash: receipt.hash,
648
+ blockNumber: receipt.blockNumber,
649
+ status: receipt.status === 1 ? "success" : "failed",
650
+ gasUsed: receipt.gasUsed
651
+ };
652
+ }
653
+ /**
654
+ * Withdraw assets from vault
655
+ */
656
+ async withdraw(amount) {
657
+ const receiver = await this.signer.getAddress();
658
+ const owner = receiver;
659
+ const tx = await this.vault.withdraw(amount, receiver, owner);
660
+ const receipt = await tx.wait();
661
+ return {
662
+ txHash: receipt.hash,
663
+ blockNumber: receipt.blockNumber,
664
+ status: receipt.status === 1 ? "success" : "failed",
665
+ gasUsed: receipt.gasUsed
666
+ };
667
+ }
668
+ /**
669
+ * Redeem shares for assets
670
+ */
671
+ async redeem(shares) {
672
+ const receiver = await this.signer.getAddress();
673
+ const owner = receiver;
674
+ const tx = await this.vault.redeem(shares, receiver, owner);
675
+ const receipt = await tx.wait();
676
+ return {
677
+ txHash: receipt.hash,
678
+ blockNumber: receipt.blockNumber,
679
+ status: receipt.status === 1 ? "success" : "failed",
680
+ gasUsed: receipt.gasUsed
681
+ };
682
+ }
683
+ // ============ View Functions ============
684
+ /**
685
+ * Get vault statistics
686
+ */
687
+ async getStats() {
688
+ const [totalAssets, totalBorrowed, availableLiquidity] = await Promise.all([
689
+ this.vault.totalAssets(),
690
+ this.vault.totalBorrowed(),
691
+ this.vault.availableLiquidity()
692
+ ]);
693
+ const utilizationRate = totalAssets > 0n ? Number(totalBorrowed * 10000n / totalAssets) / 100 : 0;
694
+ const totalSupply = await this.vault.totalSupply();
695
+ const sharePrice = totalSupply > 0n ? totalAssets * BigInt(1e18) / totalSupply : BigInt(1e18);
696
+ return {
697
+ totalAssets,
698
+ totalBorrowed,
699
+ availableLiquidity,
700
+ utilizationRate,
701
+ sharePrice
702
+ };
703
+ }
704
+ /**
705
+ * Get LP position
706
+ */
707
+ async getPosition(address) {
708
+ const owner = address || await this.signer.getAddress();
709
+ const shares = await this.vault.balanceOf(owner);
710
+ const assets = await this.vault.convertToAssets(shares);
711
+ const pendingYield = 0n;
712
+ return {
713
+ shares,
714
+ assets,
715
+ pendingYield
716
+ };
717
+ }
718
+ /**
719
+ * Preview deposit (how many shares for assets)
720
+ */
721
+ async previewDeposit(assets) {
722
+ return await this.vault.previewDeposit(assets);
723
+ }
724
+ /**
725
+ * Preview withdraw (how many assets for shares)
726
+ */
727
+ async previewRedeem(shares) {
728
+ return await this.vault.previewRedeem(shares);
729
+ }
730
+ /**
731
+ * Check if withdrawal is allowed (sufficient liquidity)
732
+ */
733
+ async canWithdraw(assets) {
734
+ const liquidity = await this.vault.availableLiquidity();
735
+ return liquidity >= assets;
736
+ }
737
+ /**
738
+ * Get current APY estimate
739
+ */
740
+ async estimateAPY() {
741
+ const stats = await this.getStats();
742
+ const avgAPR = 1200;
743
+ const protocolFee = 1e3;
744
+ const lpYield = avgAPR * (1e4 - protocolFee) / 1e4;
745
+ const effectiveYield = lpYield * stats.utilizationRate / 100;
746
+ return effectiveYield / 100;
747
+ }
748
+ // ============ Utility ============
749
+ async getAsset() {
750
+ if (!this.asset) {
751
+ const assetAddress = await this.vault.asset();
752
+ this.asset = new Contract2(assetAddress, ERC20_ABI, this.signer);
753
+ }
754
+ return this.asset;
755
+ }
756
+ /**
757
+ * Get underlying asset address
758
+ */
759
+ async getAssetAddress() {
760
+ return await this.vault.asset();
761
+ }
762
+ /**
763
+ * Get vault share token address
764
+ */
765
+ getVaultAddress() {
766
+ return this.config.contracts.lpVault;
767
+ }
768
+ };
769
+
770
+ // src/clients/AgentIdentityClient.ts
771
+ import { ethers as ethers2 } from "ethers";
772
+ var ERC8004_IDENTITY_ABI = [
773
+ // Registration
774
+ "function register() returns (uint256)",
775
+ "function register(string agentURI) returns (uint256)",
776
+ "function register(string agentURI, tuple(string key, bytes value)[] metadata) returns (uint256)",
777
+ // Management
778
+ "function setAgentURI(uint256 agentId, string newURI)",
779
+ "function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)",
780
+ "function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)",
781
+ // ERC-721 standard
782
+ "function ownerOf(uint256 tokenId) view returns (address)",
783
+ "function balanceOf(address owner) view returns (uint256)",
784
+ "function tokenURI(uint256 tokenId) view returns (string)",
785
+ "function transferFrom(address from, address to, uint256 tokenId)",
786
+ "function approve(address to, uint256 tokenId)",
787
+ "function getApproved(uint256 tokenId) view returns (address)",
788
+ // Events
789
+ "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
790
+ ];
791
+ var ERC8004_REPUTATION_ABI = [
792
+ // Feedback
793
+ "function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash)",
794
+ "function revokeFeedback(uint256 agentId, uint64 feedbackIndex)",
795
+ // Queries
796
+ "function readFeedback(uint256 agentId, address clientAddress, uint64 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
797
+ "function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint64 count, int128 summaryValue, uint8 summaryValueDecimals)",
798
+ "function getClients(uint256 agentId) view returns (address[])",
799
+ "function getLastIndex(uint256 agentId, address clientAddress) view returns (uint64)",
800
+ // Registry
801
+ "function getIdentityRegistry() view returns (address)"
802
+ ];
803
+ var ERC8004_SEPOLIA = {
804
+ identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
805
+ reputationRegistry: "0x8004B663056A597Dffe9eCcC1965A193B7388713"
806
+ };
807
+ var AgentIdentityClient = class {
808
+ constructor(options) {
809
+ this.config = options.config;
810
+ this.signer = options.signer;
811
+ const identityAddr = options.config.contracts?.identityRegistry || ERC8004_SEPOLIA.identityRegistry;
812
+ const reputationAddr = ERC8004_SEPOLIA.reputationRegistry;
813
+ this.identityRegistry = new ethers2.Contract(identityAddr, ERC8004_IDENTITY_ABI, this.signer);
814
+ this.reputationRegistry = new ethers2.Contract(reputationAddr, ERC8004_REPUTATION_ABI, this.signer);
815
+ }
816
+ // ============ Identity Functions ============
817
+ /**
818
+ * Register a new agent (minimal - no metadata)
819
+ */
820
+ async register() {
821
+ const tx = await this.identityRegistry["register()"]();
822
+ const receipt = await tx.wait();
823
+ const agentId = this.parseAgentIdFromReceipt(receipt);
824
+ return { agentId, txHash: receipt.hash };
825
+ }
826
+ /**
827
+ * Register agent with IPFS/HTTP URI to metadata JSON
828
+ * @param agentURI URI pointing to agent metadata (ipfs:// or https://)
829
+ */
830
+ async registerWithURI(agentURI) {
831
+ const tx = await this.identityRegistry["register(string)"](agentURI);
832
+ const receipt = await tx.wait();
833
+ const agentId = this.parseAgentIdFromReceipt(receipt);
834
+ return { agentId, txHash: receipt.hash };
835
+ }
836
+ /**
837
+ * Check if the signer already owns an ERC-8004 identity token.
838
+ * Returns true if balanceOf > 0, false otherwise.
839
+ * Note: Cannot determine the specific agentId without enumeration —
840
+ * use agether init <pk> --agent-id <id> if you know your agentId.
841
+ */
842
+ async hasExistingIdentity() {
843
+ try {
844
+ const address = await this.signer.getAddress();
845
+ const balance = await this.identityRegistry.balanceOf(address);
846
+ return balance > 0n;
847
+ } catch {
848
+ return false;
849
+ }
850
+ }
851
+ /**
852
+ * Register only if no identity exists; otherwise throw.
853
+ * Prevents accidental double-registration.
854
+ */
855
+ async registerOrGet() {
856
+ const hasIdentity = await this.hasExistingIdentity();
857
+ if (hasIdentity) {
858
+ throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
859
+ }
860
+ const result = await this.register();
861
+ return { ...result, existing: false };
862
+ }
863
+ /**
864
+ * Register with URI only if no identity exists; otherwise throw.
865
+ * Prevents accidental double-registration.
866
+ */
867
+ async registerOrGetWithURI(agentURI) {
868
+ const hasIdentity = await this.hasExistingIdentity();
869
+ if (hasIdentity) {
870
+ throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
871
+ }
872
+ const result = await this.registerWithURI(agentURI);
873
+ return { ...result, existing: false };
874
+ }
875
+ /**
876
+ * Register agent with URI and on-chain metadata
877
+ */
878
+ async registerWithMetadata(agentURI, metadata) {
879
+ const metadataEntries = metadata.map((m) => ({
880
+ key: m.key,
881
+ value: ethers2.toUtf8Bytes(m.value)
882
+ }));
883
+ const tx = await this.identityRegistry["register(string,tuple(string,bytes)[])"](
884
+ agentURI,
885
+ metadataEntries
886
+ );
887
+ const receipt = await tx.wait();
888
+ const agentId = this.parseAgentIdFromReceipt(receipt);
889
+ return { agentId, txHash: receipt.hash };
890
+ }
891
+ /**
892
+ * Get agent owner address
893
+ */
894
+ async getOwner(agentId) {
895
+ return await this.identityRegistry.ownerOf(agentId);
896
+ }
897
+ /**
898
+ * Get agent URI (metadata JSON location)
899
+ */
900
+ async getAgentURI(agentId) {
901
+ return await this.identityRegistry.tokenURI(agentId);
902
+ }
903
+ /**
904
+ * Update agent URI
905
+ */
906
+ async setAgentURI(agentId, newURI) {
907
+ const tx = await this.identityRegistry.setAgentURI(agentId, newURI);
908
+ const receipt = await tx.wait();
909
+ return receipt.hash;
910
+ }
911
+ /**
912
+ * Set on-chain metadata (key-value)
913
+ */
914
+ async setMetadata(agentId, key, value) {
915
+ const tx = await this.identityRegistry.setMetadata(
916
+ agentId,
917
+ key,
918
+ ethers2.toUtf8Bytes(value)
919
+ );
920
+ const receipt = await tx.wait();
921
+ return receipt.hash;
922
+ }
923
+ /**
924
+ * Get on-chain metadata
925
+ */
926
+ async getMetadata(agentId, key) {
927
+ const value = await this.identityRegistry.getMetadata(agentId, key);
928
+ return ethers2.toUtf8String(value);
929
+ }
930
+ /**
931
+ * Transfer agent to new owner
932
+ */
933
+ async transfer(agentId, to) {
934
+ const from = await this.signer.getAddress();
935
+ const tx = await this.identityRegistry.transferFrom(from, to, agentId);
936
+ const receipt = await tx.wait();
937
+ return receipt.hash;
938
+ }
939
+ /**
940
+ * Fetch and parse agent metadata from URI
941
+ */
942
+ async fetchAgentMetadata(agentId) {
943
+ try {
944
+ const uri = await this.getAgentURI(agentId);
945
+ let fetchUrl = uri;
946
+ if (uri.startsWith("ipfs://")) {
947
+ const cid = uri.replace("ipfs://", "");
948
+ fetchUrl = `https://ipfs.io/ipfs/${cid}`;
949
+ }
950
+ const response = await fetch(fetchUrl);
951
+ if (!response.ok) return null;
952
+ return await response.json();
953
+ } catch {
954
+ return null;
955
+ }
956
+ }
957
+ // ============ Reputation Functions ============
958
+ /**
959
+ * Give feedback to an agent
960
+ */
961
+ async giveFeedback(input) {
962
+ const feedbackHash = ethers2.keccak256(
963
+ ethers2.AbiCoder.defaultAbiCoder().encode(
964
+ ["uint256", "int128", "uint256"],
965
+ [input.agentId, input.value, Date.now()]
966
+ )
967
+ );
968
+ const tx = await this.reputationRegistry.giveFeedback(
969
+ input.agentId,
970
+ input.value,
971
+ input.decimals || 0,
972
+ input.tag1 || "",
973
+ input.tag2 || "",
974
+ input.endpoint || "",
975
+ input.feedbackURI || "",
976
+ feedbackHash
977
+ );
978
+ const receipt = await tx.wait();
979
+ return receipt.hash;
980
+ }
981
+ /**
982
+ * Give positive feedback (shorthand)
983
+ */
984
+ async givePosisitiveFeedback(agentId, value = 100, tags = {}) {
985
+ return this.giveFeedback({
986
+ agentId,
987
+ value: Math.abs(value),
988
+ ...tags
989
+ });
990
+ }
991
+ /**
992
+ * Give negative feedback (e.g., for defaults)
993
+ */
994
+ async giveNegativeFeedback(agentId, value = -100, tags = {}) {
995
+ return this.giveFeedback({
996
+ agentId,
997
+ value: -Math.abs(value),
998
+ ...tags
999
+ });
1000
+ }
1001
+ /**
1002
+ * Record credit default in reputation system
1003
+ */
1004
+ async recordCreditDefault(agentId, creditLineId) {
1005
+ return this.giveFeedback({
1006
+ agentId,
1007
+ value: -100,
1008
+ tag1: "credit",
1009
+ tag2: "default",
1010
+ feedbackURI: `agether://default/${creditLineId}`
1011
+ });
1012
+ }
1013
+ /**
1014
+ * Get reputation summary for an agent
1015
+ */
1016
+ async getReputation(agentId, tag1 = "", tag2 = "") {
1017
+ const clients = await this.reputationRegistry.getClients(agentId);
1018
+ if (clients.length === 0) {
1019
+ return {
1020
+ count: 0,
1021
+ totalValue: 0,
1022
+ averageValue: 0,
1023
+ clients: []
1024
+ };
1025
+ }
1026
+ const [count, summaryValue, decimals] = await this.reputationRegistry.getSummary(
1027
+ agentId,
1028
+ clients,
1029
+ tag1,
1030
+ tag2
1031
+ );
1032
+ const divisor = 10 ** Number(decimals);
1033
+ const total = Number(summaryValue) / divisor;
1034
+ return {
1035
+ count: Number(count),
1036
+ totalValue: total,
1037
+ averageValue: Number(count) > 0 ? total / Number(count) : 0,
1038
+ clients: clients.map((c) => c)
1039
+ };
1040
+ }
1041
+ /**
1042
+ * Check if agent has negative credit reputation
1043
+ */
1044
+ async hasNegativeCreditReputation(agentId) {
1045
+ const rep = await this.getReputation(agentId, "credit", "");
1046
+ return rep.count > 0 && rep.averageValue < 0;
1047
+ }
1048
+ // ============ Agether Integration ============
1049
+ /**
1050
+ * Verify agent is eligible for Agether credit
1051
+ * Checks:
1052
+ * 1. Agent exists in ERC-8004 registry
1053
+ * 2. Agent has no negative credit reputation
1054
+ */
1055
+ async verifyForCredit(agentId) {
1056
+ let owner;
1057
+ try {
1058
+ owner = await this.getOwner(agentId);
1059
+ } catch {
1060
+ return { eligible: false, reason: "Agent not found in ERC-8004 registry" };
1061
+ }
1062
+ const reputation = await this.getReputation(agentId, "credit", "");
1063
+ if (reputation.count > 0 && reputation.averageValue < 0) {
1064
+ return {
1065
+ eligible: false,
1066
+ reason: "Agent has negative credit reputation",
1067
+ owner,
1068
+ reputation
1069
+ };
1070
+ }
1071
+ return {
1072
+ eligible: true,
1073
+ owner,
1074
+ reputation
1075
+ };
1076
+ }
1077
+ // ============ Helpers ============
1078
+ parseAgentIdFromReceipt(receipt) {
1079
+ for (const log of receipt.logs) {
1080
+ try {
1081
+ const parsed = this.identityRegistry.interface.parseLog({
1082
+ topics: log.topics,
1083
+ data: log.data
1084
+ });
1085
+ if (parsed?.name === "Transfer") {
1086
+ return parsed.args[2];
1087
+ }
1088
+ } catch {
1089
+ continue;
1090
+ }
1091
+ }
1092
+ return 0n;
1093
+ }
1094
+ /**
1095
+ * Get contract addresses
1096
+ */
1097
+ getContractAddresses() {
1098
+ return {
1099
+ identity: this.identityRegistry.target,
1100
+ reputation: this.reputationRegistry.target
1101
+ };
1102
+ }
1103
+ };
1104
+
1105
+ // src/clients/X402Client.ts
1106
+ import { ethers as ethers3 } from "ethers";
1107
+ var USDC_DOMAINS = {
1108
+ "eip155:1": { name: "USD Coin", version: "2", address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
1109
+ "eip155:8453": { name: "USD Coin", version: "2", address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
1110
+ "eip155:84532": { name: "USD Coin", version: "2", address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e" },
1111
+ "eip155:42161": { name: "USD Coin", version: "2", address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" },
1112
+ "eip155:10": { name: "USD Coin", version: "2", address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85" }
1113
+ };
1114
+ function chainIdFromNetwork(network) {
1115
+ const m = network.match(/^eip155:(\d+)$/);
1116
+ return m ? Number(m[1]) : 1;
1117
+ }
1118
+ var X402Client = class {
1119
+ constructor(config) {
1120
+ this.config = config;
1121
+ const provider = new ethers3.JsonRpcProvider(config.rpcUrl);
1122
+ this.wallet = new ethers3.Wallet(config.privateKey, provider);
1123
+ }
1124
+ async get(url, opts) {
1125
+ return this.request(url, { ...opts, method: "GET" });
1126
+ }
1127
+ async post(url, body, opts) {
1128
+ return this.request(url, {
1129
+ ...opts,
1130
+ method: "POST",
1131
+ body: body ? JSON.stringify(body) : void 0,
1132
+ headers: { "Content-Type": "application/json", ...opts?.headers }
1133
+ });
1134
+ }
1135
+ getAddress() {
1136
+ return this.wallet.address;
1137
+ }
1138
+ // ──────────── Core request / 402-retry loop ────────────
1139
+ async request(url, options) {
1140
+ try {
1141
+ console.log(" [1/4] Calling resource server\u2026");
1142
+ const response = await fetch(url, {
1143
+ ...options,
1144
+ headers: {
1145
+ ...options?.headers,
1146
+ "X-Agent-Id": this.config.agentId || ""
1147
+ }
1148
+ });
1149
+ if (response.ok) {
1150
+ const data = await response.json();
1151
+ return { success: true, data };
1152
+ }
1153
+ if (response.status !== 402) {
1154
+ return { success: false, error: `HTTP ${response.status}: ${await response.text()}` };
1155
+ }
1156
+ console.log(" [2/4] 402 received \u2014 parsing payment requirements\u2026");
1157
+ const parsed = await this.parsePaymentRequired(response);
1158
+ if (!parsed) {
1159
+ return { success: false, error: "Could not parse payment requirements from 402 response" };
1160
+ }
1161
+ const { requirements, resource } = parsed;
1162
+ console.log(` scheme : ${requirements.scheme}`);
1163
+ console.log(` network : ${requirements.network}`);
1164
+ console.log(` amount : ${requirements.amount} (atomic)`);
1165
+ console.log(` asset : ${requirements.asset}`);
1166
+ console.log(` payTo : ${requirements.payTo}`);
1167
+ console.log(" [3/4] Signing EIP-3009 transferWithAuthorization\u2026");
1168
+ const paymentPayload = await this.buildPaymentPayload(requirements, resource, url);
1169
+ const paymentB64 = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
1170
+ await this.riskCheck(paymentPayload, requirements);
1171
+ console.log(" [4/4] Retrying with PAYMENT-SIGNATURE header\u2026");
1172
+ const paidResponse = await fetch(url, {
1173
+ ...options,
1174
+ headers: {
1175
+ ...options?.headers,
1176
+ "X-Agent-Id": this.config.agentId || "",
1177
+ // v2 header
1178
+ "PAYMENT-SIGNATURE": paymentB64,
1179
+ // v1 compat header (some servers still use this)
1180
+ "X-PAYMENT": paymentB64
1181
+ }
1182
+ });
1183
+ if (paidResponse.ok) {
1184
+ const data = await paidResponse.json();
1185
+ const settlementHeader = paidResponse.headers.get("PAYMENT-RESPONSE") || paidResponse.headers.get("X-PAYMENT-RESPONSE");
1186
+ let txHash;
1187
+ if (settlementHeader) {
1188
+ try {
1189
+ const settlement = JSON.parse(Buffer.from(settlementHeader, "base64").toString("utf-8"));
1190
+ txHash = settlement.transaction;
1191
+ } catch {
1192
+ }
1193
+ }
1194
+ return {
1195
+ success: true,
1196
+ data,
1197
+ paymentInfo: {
1198
+ amount: requirements.amount,
1199
+ asset: requirements.extra?.name || "USDC",
1200
+ network: requirements.network,
1201
+ txHash
1202
+ }
1203
+ };
1204
+ }
1205
+ const errBody = await paidResponse.text();
1206
+ return { success: false, error: `Payment rejected (HTTP ${paidResponse.status}): ${errBody}` };
1207
+ } catch (error) {
1208
+ return { success: false, error: `Request failed: ${error instanceof Error ? error.message : String(error)}` };
1209
+ }
1210
+ }
1211
+ // ──────────── Parse 402 response ────────────
1212
+ async parsePaymentRequired(response) {
1213
+ const prHeader = response.headers.get("PAYMENT-REQUIRED") || response.headers.get("x-payment-required");
1214
+ if (prHeader) {
1215
+ try {
1216
+ const decoded = JSON.parse(Buffer.from(prHeader, "base64").toString("utf-8"));
1217
+ if (decoded.accepts?.length) {
1218
+ return { requirements: decoded.accepts[0], resource: decoded.resource };
1219
+ }
1220
+ } catch {
1221
+ }
1222
+ }
1223
+ try {
1224
+ const body = await response.json();
1225
+ if (body.accepts && Array.isArray(body.accepts) && body.accepts.length > 0) {
1226
+ return { requirements: body.accepts[0], resource: body.resource };
1227
+ }
1228
+ if (body.paymentRequirements) {
1229
+ const pr = Array.isArray(body.paymentRequirements) ? body.paymentRequirements[0] : body.paymentRequirements;
1230
+ return { requirements: pr, resource: body.resource };
1231
+ }
1232
+ if (body.scheme && body.network) {
1233
+ return { requirements: body, resource: body.resource };
1234
+ }
1235
+ } catch {
1236
+ }
1237
+ const wwwAuth = response.headers.get("WWW-Authenticate");
1238
+ if (wwwAuth) {
1239
+ const m = wwwAuth.match(/x402[^"]*"([^"]+)"/);
1240
+ if (m) {
1241
+ try {
1242
+ const decoded = JSON.parse(Buffer.from(m[1], "base64").toString("utf-8"));
1243
+ const reqs = Array.isArray(decoded) ? decoded[0] : decoded;
1244
+ return { requirements: reqs };
1245
+ } catch {
1246
+ }
1247
+ }
1248
+ }
1249
+ return null;
1250
+ }
1251
+ // ──────────── Build x402 v2 PaymentPayload with EIP-3009 ────────────
1252
+ //
1253
+ // If an AgentAccount is configured, we use it as the `from` address
1254
+ // (smart wallet pays directly). The AgentAccount implements EIP-1271
1255
+ // so USDC's transferWithAuthorization will call isValidSignature()
1256
+ // to verify the owner's ECDSA signature. The facilitator detects
1257
+ // the >65-byte or smart-wallet case and uses the bytes overload.
1258
+ async buildPaymentPayload(reqs, resource, url) {
1259
+ const now = Math.floor(Date.now() / 1e3);
1260
+ const validAfter = String(now - 60);
1261
+ const validBefore = String(now + (reqs.maxTimeoutSeconds || 300));
1262
+ const nonce = ethers3.hexlify(ethers3.randomBytes(32));
1263
+ const chainId = chainIdFromNetwork(reqs.network);
1264
+ const usdcDomain = USDC_DOMAINS[reqs.network] || USDC_DOMAINS["eip155:1"];
1265
+ const payerAddress = this.config.accountAddress || this.wallet.address;
1266
+ const isSmartWallet = !!this.config.accountAddress;
1267
+ const domain = {
1268
+ name: usdcDomain.name,
1269
+ version: usdcDomain.version,
1270
+ chainId,
1271
+ verifyingContract: reqs.asset || usdcDomain.address
1272
+ };
1273
+ const types = {
1274
+ TransferWithAuthorization: [
1275
+ { name: "from", type: "address" },
1276
+ { name: "to", type: "address" },
1277
+ { name: "value", type: "uint256" },
1278
+ { name: "validAfter", type: "uint256" },
1279
+ { name: "validBefore", type: "uint256" },
1280
+ { name: "nonce", type: "bytes32" }
1281
+ ]
1282
+ };
1283
+ const value = {
1284
+ from: payerAddress,
1285
+ // AgentAccount or EOA
1286
+ to: reqs.payTo,
1287
+ value: reqs.amount,
1288
+ validAfter,
1289
+ validBefore,
1290
+ nonce
1291
+ };
1292
+ let signature = await this.wallet.signTypedData(domain, types, value);
1293
+ if (isSmartWallet) {
1294
+ signature = signature + "00";
1295
+ }
1296
+ if (isSmartWallet) {
1297
+ console.log(` \u2713 Signed for AgentAccount ${payerAddress.slice(0, 10)}\u2026 (EIP-1271, chain=${chainId})`);
1298
+ } else {
1299
+ console.log(` \u2713 Signed (from=${payerAddress.slice(0, 10)}\u2026, chain=${chainId})`);
1300
+ }
1301
+ return {
1302
+ x402Version: 2,
1303
+ resource: resource || { url, description: "", mimeType: "application/json" },
1304
+ accepted: {
1305
+ scheme: reqs.scheme,
1306
+ network: reqs.network,
1307
+ amount: reqs.amount,
1308
+ asset: reqs.asset,
1309
+ payTo: reqs.payTo,
1310
+ maxTimeoutSeconds: reqs.maxTimeoutSeconds,
1311
+ extra: reqs.extra || {}
1312
+ },
1313
+ payload: {
1314
+ signature,
1315
+ authorization: {
1316
+ from: payerAddress,
1317
+ // AgentAccount address — facilitator checks balance here
1318
+ to: reqs.payTo,
1319
+ value: reqs.amount,
1320
+ validAfter,
1321
+ validBefore,
1322
+ nonce
1323
+ }
1324
+ }
1325
+ };
1326
+ }
1327
+ // ──────────── Risk check via our backend ────────────
1328
+ async riskCheck(paymentPayload, reqs) {
1329
+ try {
1330
+ const verifyUrl = `${this.config.backendUrl}/x402/verify`;
1331
+ const resp = await fetch(verifyUrl, {
1332
+ method: "POST",
1333
+ headers: {
1334
+ "Content-Type": "application/json",
1335
+ "X-Agent-Id": this.config.agentId || "",
1336
+ ...this.config.accountAddress ? { "X-Agent-Account": this.config.accountAddress } : {}
1337
+ },
1338
+ body: JSON.stringify({
1339
+ x402Version: 2,
1340
+ paymentPayload,
1341
+ paymentRequirements: reqs
1342
+ }),
1343
+ signal: AbortSignal.timeout(5e3)
1344
+ });
1345
+ if (resp.ok) {
1346
+ const result = await resp.json();
1347
+ const decision = resp.headers.get("X-Risk-Decision") || (result.isValid ? "allow" : "unknown");
1348
+ const score = resp.headers.get("X-Risk-Score") || "?";
1349
+ console.log(` \u2713 Risk check: ${decision} (score=${score})`);
1350
+ } else {
1351
+ console.log(` \u26A0 Risk check failed (HTTP ${resp.status}) \u2014 continuing anyway`);
1352
+ }
1353
+ } catch {
1354
+ console.log(" \u26A0 Risk check unavailable \u2014 continuing");
1355
+ }
1356
+ }
1357
+ };
1358
+
1359
+ // src/clients/WalletClient.ts
1360
+ import { ethers as ethers4, Contract as Contract3, Wallet } from "ethers";
1361
+ var WalletClient = class {
1362
+ constructor(config) {
1363
+ this.privateKey = null;
1364
+ this.factoryAddress = config.factoryAddress;
1365
+ this.usdcAddress = config.usdcAddress || ethers4.ZeroAddress;
1366
+ this.chainId = config.chainId;
1367
+ this.rpcUrl = config.rpcUrl;
1368
+ this.provider = new ethers4.JsonRpcProvider(config.rpcUrl);
1369
+ this.privateKey = config.privateKey || null;
1370
+ }
1371
+ /**
1372
+ * Create a fresh signer to avoid nonce caching issues
1373
+ */
1374
+ getFreshSigner() {
1375
+ if (!this.privateKey) {
1376
+ throw new Error("Signer not configured - provide privateKey");
1377
+ }
1378
+ const freshProvider = new ethers4.JsonRpcProvider(this.rpcUrl);
1379
+ return new Wallet(this.privateKey, freshProvider);
1380
+ }
1381
+ getFactoryContract() {
1382
+ if (this.privateKey) {
1383
+ return new Contract3(this.factoryAddress, ACCOUNT_FACTORY_ABI, this.getFreshSigner());
1384
+ }
1385
+ return new Contract3(this.factoryAddress, ACCOUNT_FACTORY_ABI, this.provider);
1386
+ }
1387
+ getAccountContract(accountAddress) {
1388
+ if (this.privateKey) {
1389
+ return new Contract3(accountAddress, AGENT_ACCOUNT_ABI, this.getFreshSigner());
1390
+ }
1391
+ return new Contract3(accountAddress, AGENT_ACCOUNT_ABI, this.provider);
1392
+ }
1393
+ getChainId() {
1394
+ return this.chainId;
1395
+ }
1396
+ // ============ Account Factory ============
1397
+ async accountExists(agentId) {
1398
+ const factory = this.getFactoryContract();
1399
+ return factory.accountExists(agentId);
1400
+ }
1401
+ async getAccount(agentId) {
1402
+ const factory = this.getFactoryContract();
1403
+ const address = await factory.getAccount(agentId);
1404
+ if (address === ethers4.ZeroAddress) {
1405
+ return null;
1406
+ }
1407
+ return address;
1408
+ }
1409
+ async predictAddress(agentId) {
1410
+ const factory = this.getFactoryContract();
1411
+ return factory.predictAddress(agentId);
1412
+ }
1413
+ async createAccount(agentId) {
1414
+ if (!this.privateKey) {
1415
+ throw new Error("Signer not configured - provide privateKey");
1416
+ }
1417
+ const factory = this.getFactoryContract();
1418
+ const tx = await factory.createAccount(agentId);
1419
+ await tx.wait();
1420
+ const account = await this.getAccount(agentId);
1421
+ if (!account) {
1422
+ throw new Error("Account creation failed");
1423
+ }
1424
+ return account;
1425
+ }
1426
+ async totalAccounts() {
1427
+ const factory = this.getFactoryContract();
1428
+ return factory.totalAccounts();
1429
+ }
1430
+ // ============ Account Info ============
1431
+ async getWalletInfo(accountAddress) {
1432
+ const account = this.getAccountContract(accountAddress);
1433
+ const [agentId, owner, ethBalance] = await Promise.all([
1434
+ account.agentId(),
1435
+ account.owner(),
1436
+ account.ethBalance()
1437
+ ]);
1438
+ let usdcBalance = 0n;
1439
+ if (this.usdcAddress !== ethers4.ZeroAddress) {
1440
+ usdcBalance = await account.balanceOf(this.usdcAddress);
1441
+ }
1442
+ return {
1443
+ address: accountAddress,
1444
+ agentId: BigInt(agentId),
1445
+ owner,
1446
+ ethBalance: BigInt(ethBalance),
1447
+ usdcBalance: BigInt(usdcBalance)
1448
+ };
1449
+ }
1450
+ async getProviderStatus(accountAddress, creditProvider) {
1451
+ const provider = new Contract3(
1452
+ creditProvider,
1453
+ CREDIT_PROVIDER_ABI,
1454
+ this.provider
1455
+ );
1456
+ const [isEligible, totalDebt, maxDrawable] = await Promise.all([
1457
+ provider.isEligible(accountAddress),
1458
+ provider.getTotalDebt(accountAddress),
1459
+ provider.maxDrawable(accountAddress)
1460
+ ]);
1461
+ return {
1462
+ provider: creditProvider,
1463
+ isEligible,
1464
+ totalDebt: BigInt(totalDebt),
1465
+ maxDrawable: BigInt(maxDrawable)
1466
+ };
1467
+ }
1468
+ // ============ Fund / Withdraw ============
1469
+ async fundAccount(accountAddress, tokenAddress, amount) {
1470
+ if (!this.privateKey) {
1471
+ throw new Error("Signer not configured");
1472
+ }
1473
+ const signer = this.getFreshSigner();
1474
+ const token = new Contract3(tokenAddress, ERC20_ABI, signer);
1475
+ const approveTx = await token.approve(accountAddress, amount);
1476
+ await approveTx.wait();
1477
+ const signer2 = this.getFreshSigner();
1478
+ const account = new Contract3(accountAddress, AGENT_ACCOUNT_ABI, signer2);
1479
+ const tx = await account.fund(tokenAddress, amount);
1480
+ const receipt = await tx.wait();
1481
+ return receipt.hash;
1482
+ }
1483
+ async withdraw(accountAddress, tokenAddress, amount, to) {
1484
+ if (!this.privateKey) {
1485
+ throw new Error("Signer not configured");
1486
+ }
1487
+ const account = this.getAccountContract(accountAddress);
1488
+ const recipient = to || await this.getFreshSigner().getAddress();
1489
+ const tx = await account.withdraw(tokenAddress, amount, recipient);
1490
+ const receipt = await tx.wait();
1491
+ return receipt.hash;
1492
+ }
1493
+ // ============ Credit Operations ============
1494
+ async drawCredit(accountAddress, creditProvider, amount) {
1495
+ if (!this.privateKey) {
1496
+ throw new Error("Signer not configured");
1497
+ }
1498
+ const account = this.getAccountContract(accountAddress);
1499
+ const tx = await account.drawCredit(creditProvider, amount);
1500
+ const receipt = await tx.wait();
1501
+ return receipt.hash;
1502
+ }
1503
+ async repayCredit(accountAddress, creditProvider, amount) {
1504
+ if (!this.privateKey) {
1505
+ throw new Error("Signer not configured");
1506
+ }
1507
+ const account = this.getAccountContract(accountAddress);
1508
+ const tx = await account.repayCredit(creditProvider, amount);
1509
+ const receipt = await tx.wait();
1510
+ return receipt.hash;
1511
+ }
1512
+ // ============ Generic Execute ============
1513
+ async execute(accountAddress, target, value, data) {
1514
+ if (!this.privateKey) {
1515
+ throw new Error("Signer not configured");
1516
+ }
1517
+ const account = this.getAccountContract(accountAddress);
1518
+ const tx = await account.execute(target, value, data);
1519
+ const receipt = await tx.wait();
1520
+ return receipt.hash;
1521
+ }
1522
+ // ============ Utility ============
1523
+ generatePaymentMessageHash(accountAddress, recipient, amount, nonce, deadline, chainId) {
1524
+ return ethers4.keccak256(
1525
+ ethers4.AbiCoder.defaultAbiCoder().encode(
1526
+ ["address", "uint256", "uint256", "uint256", "uint256", "address"],
1527
+ [recipient, amount, nonce, deadline, chainId, accountAddress]
1528
+ )
1529
+ );
1530
+ }
1531
+ };
1532
+
1533
+ // src/utils/format.ts
1534
+ function parseUnits(value, decimals = 6) {
1535
+ const [integer, fraction = ""] = value.split(".");
1536
+ const paddedFraction = fraction.padEnd(decimals, "0").slice(0, decimals);
1537
+ return BigInt(integer + paddedFraction);
1538
+ }
1539
+ function formatUnits(value, decimals = 6) {
1540
+ const str = value.toString().padStart(decimals + 1, "0");
1541
+ const integer = str.slice(0, -decimals) || "0";
1542
+ const fraction = str.slice(-decimals);
1543
+ return `${integer}.${fraction}`;
1544
+ }
1545
+ function formatUSD(value, decimals = 6) {
1546
+ const num = Number(formatUnits(value, decimals));
1547
+ return new Intl.NumberFormat("en-US", {
1548
+ style: "currency",
1549
+ currency: "USD"
1550
+ }).format(num);
1551
+ }
1552
+ function formatPercent(bps) {
1553
+ return `${(bps / 100).toFixed(2)}%`;
1554
+ }
1555
+ function formatAPR(bps) {
1556
+ return formatPercent(Number(bps));
1557
+ }
1558
+ function formatHealthFactor(factor) {
1559
+ const num = Number(factor) / 1e18;
1560
+ if (num >= 100) return "\u221E";
1561
+ return num.toFixed(2);
1562
+ }
1563
+ function formatAddress(address) {
1564
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
1565
+ }
1566
+ function formatTimestamp(timestamp) {
1567
+ return new Date(Number(timestamp) * 1e3).toISOString();
1568
+ }
1569
+ function bpsToRate(bps) {
1570
+ return Number(bps) / 1e4;
1571
+ }
1572
+ function rateToBps(rate) {
1573
+ return BigInt(Math.round(rate * 1e4));
1574
+ }
1575
+ export {
1576
+ ACCOUNT_FACTORY_ABI,
1577
+ AGENT_ACCOUNT_ABI,
1578
+ AGENT_REPUTATION_ABI,
1579
+ AgentIdentityClient,
1580
+ AgetherClient,
1581
+ AgetherError,
1582
+ CREDIT_PROVIDER_ABI,
1583
+ ChainId,
1584
+ CreditNotActiveError,
1585
+ CreditStatus,
1586
+ ERC20_ABI,
1587
+ IDENTITY_REGISTRY_ABI,
1588
+ InsufficientCreditError,
1589
+ LP_VAULT_ABI,
1590
+ REPUTATION_CREDIT_ABI,
1591
+ ScoringClient,
1592
+ ScoringRejectedError,
1593
+ VALIDATION_REGISTRY_ABI,
1594
+ VaultClient,
1595
+ WalletClient,
1596
+ X402Client,
1597
+ bpsToRate,
1598
+ createConfig,
1599
+ formatAPR,
1600
+ formatAddress,
1601
+ formatHealthFactor,
1602
+ formatPercent,
1603
+ formatTimestamp,
1604
+ formatUSD,
1605
+ formatUnits,
1606
+ getDefaultConfig,
1607
+ getUSDCAddress,
1608
+ parseUnits,
1609
+ rateToBps
1610
+ };