@agether/sdk 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -34,24 +34,18 @@ __export(index_exports, {
34
34
  AGENT_ACCOUNT_ABI: () => AGENT_ACCOUNT_ABI,
35
35
  AGENT_REPUTATION_ABI: () => AGENT_REPUTATION_ABI,
36
36
  AgentIdentityClient: () => AgentIdentityClient,
37
+ AgentNotApprovedError: () => AgentNotApprovedError,
37
38
  AgetherClient: () => AgetherClient,
38
39
  AgetherError: () => AgetherError,
39
- CREDIT_PROVIDER_ABI: () => CREDIT_PROVIDER_ABI,
40
40
  ChainId: () => ChainId,
41
- CreditNotActiveError: () => CreditNotActiveError,
42
- CreditStatus: () => CreditStatus,
43
41
  ERC20_ABI: () => ERC20_ABI,
44
42
  IDENTITY_REGISTRY_ABI: () => IDENTITY_REGISTRY_ABI,
45
- InsufficientCreditError: () => InsufficientCreditError,
46
- LP_VAULT_ABI: () => LP_VAULT_ABI,
47
- MORPHO_CREDIT_ABI: () => MORPHO_CREDIT_ABI,
48
- MorphoCreditClient: () => MorphoCreditClient,
49
- REPUTATION_CREDIT_ABI: () => REPUTATION_CREDIT_ABI,
43
+ InsufficientBalanceError: () => InsufficientBalanceError,
44
+ MORPHO_BLUE_ABI: () => MORPHO_BLUE_ABI,
45
+ MorphoClient: () => MorphoClient,
50
46
  ScoringClient: () => ScoringClient,
51
47
  ScoringRejectedError: () => ScoringRejectedError,
52
48
  VALIDATION_REGISTRY_ABI: () => VALIDATION_REGISTRY_ABI,
53
- VaultClient: () => VaultClient,
54
- WalletClient: () => WalletClient,
55
49
  X402Client: () => X402Client,
56
50
  bpsToRate: () => bpsToRate,
57
51
  createConfig: () => createConfig,
@@ -63,7 +57,6 @@ __export(index_exports, {
63
57
  formatUSD: () => formatUSD,
64
58
  formatUnits: () => formatUnits,
65
59
  getDefaultConfig: () => getDefaultConfig,
66
- getUSDCAddress: () => getUSDCAddress,
67
60
  parseUnits: () => parseUnits,
68
61
  rateToBps: () => rateToBps
69
62
  });
@@ -73,15 +66,6 @@ module.exports = __toCommonJS(index_exports);
73
66
  var import_ethers = require("ethers");
74
67
 
75
68
  // src/types/index.ts
76
- var CreditStatus = /* @__PURE__ */ ((CreditStatus3) => {
77
- CreditStatus3[CreditStatus3["None"] = 0] = "None";
78
- CreditStatus3[CreditStatus3["Pending"] = 1] = "Pending";
79
- CreditStatus3[CreditStatus3["Active"] = 2] = "Active";
80
- CreditStatus3[CreditStatus3["Frozen"] = 3] = "Frozen";
81
- CreditStatus3[CreditStatus3["Closed"] = 4] = "Closed";
82
- CreditStatus3[CreditStatus3["Defaulted"] = 5] = "Defaulted";
83
- return CreditStatus3;
84
- })(CreditStatus || {});
85
69
  var ChainId = /* @__PURE__ */ ((ChainId3) => {
86
70
  ChainId3[ChainId3["Ethereum"] = 1] = "Ethereum";
87
71
  ChainId3[ChainId3["Base"] = 8453] = "Base";
@@ -98,12 +82,12 @@ var AgetherError = class extends Error {
98
82
  this.name = "AgetherError";
99
83
  }
100
84
  };
101
- var InsufficientCreditError = class extends AgetherError {
102
- constructor(available, requested) {
85
+ var InsufficientBalanceError = class extends AgetherError {
86
+ constructor(available, required) {
103
87
  super(
104
- `Insufficient credit: available ${available}, requested ${requested}`,
105
- "INSUFFICIENT_CREDIT",
106
- { available: available.toString(), requested: requested.toString() }
88
+ `Insufficient balance: available ${available}, required ${required}`,
89
+ "INSUFFICIENT_BALANCE",
90
+ { available: available.toString(), required: required.toString() }
107
91
  );
108
92
  }
109
93
  };
@@ -116,12 +100,12 @@ var ScoringRejectedError = class extends AgetherError {
116
100
  );
117
101
  }
118
102
  };
119
- var CreditNotActiveError = class extends AgetherError {
120
- constructor(account, status) {
103
+ var AgentNotApprovedError = class extends AgetherError {
104
+ constructor(agentId) {
121
105
  super(
122
- `Credit line for ${account} is not active (status: ${CreditStatus[status]})`,
123
- "CREDIT_NOT_ACTIVE",
124
- { account, status: CreditStatus[status] }
106
+ `Agent ${agentId} is not KYA-approved. Submit code for validation first.`,
107
+ "AGENT_NOT_APPROVED",
108
+ { agentId: agentId.toString() }
125
109
  );
126
110
  }
127
111
  };
@@ -147,103 +131,51 @@ var ACCOUNT_FACTORY_ABI = [
147
131
  var AGENT_ACCOUNT_ABI = [
148
132
  "function agentId() view returns (uint256)",
149
133
  "function owner() view returns (address)",
134
+ "function factory() view returns (address)",
135
+ "function validationRegistry() view returns (address)",
136
+ "function identityRegistry() view returns (address)",
150
137
  "function balanceOf(address token) view returns (uint256)",
151
138
  "function ethBalance() view returns (uint256)",
152
139
  "function execute(address target, uint256 value, bytes data) payable returns (bytes)",
153
140
  "function executeBatch(address[] targets, uint256[] values, bytes[] datas) payable returns (bytes[])",
154
- "function drawCredit(address creditProvider, uint256 amount)",
155
- "function repayCredit(address creditProvider, uint256 amount)",
156
141
  "function fund(address token, uint256 amount)",
157
142
  "function withdraw(address token, uint256 amount, address to)",
158
- "function approveCreditProvider(address creditProvider)"
159
- ];
160
- var CREDIT_PROVIDER_ABI = [
161
- "function asset() view returns (address)",
162
- "function isEligible(address account) view returns (bool)",
163
- "function getCreditInfo(address account) view returns (tuple(uint256 limit, uint256 used, uint256 available, uint256 accruedInterest, uint256 aprBps, bool isActive, bool requiresCollateral))",
164
- "function getTotalDebt(address account) view returns (uint256)",
165
- "function maxDrawable(address account) view returns (uint256)",
166
- "function draw(address account, uint256 amount)",
167
- "function repay(address account, uint256 amount)",
168
- "event CreditDrawn(address indexed account, uint256 amount, uint256 totalUsed)",
169
- "event CreditRepaid(address indexed account, uint256 amount, uint256 totalUsed)",
170
- "event CreditLineOpened(address indexed account, uint256 limit, uint256 aprBps)",
171
- "event CreditLineClosed(address indexed account)"
172
- ];
173
- var REPUTATION_CREDIT_ABI = [
174
- ...CREDIT_PROVIDER_ABI,
175
- // Application / Approval flow
176
- "function applyForCredit(address account, uint256 requestedLimit)",
177
- "function approveCreditLine(address account, uint256 limit, uint256 aprBps)",
178
- "function rejectCreditLine(address account, string reason)",
179
- "function requestLimitIncrease(address account, uint256 newRequestedLimit)",
180
- "function approveLimitIncrease(address account, uint256 newLimit)",
181
- "function freezeCreditLine(address account)",
182
- "function unfreezeCreditLine(address account)",
183
- // Admin
184
- "function setCreditLine(address account, uint256 limit, uint256 aprBps)",
185
- "function openScoredCreditLine(address account)",
186
- "function closeCreditLine(address account)",
187
- "function declareDefault(address account)",
188
- // View
189
- "function getCreditLineStatus(address account) view returns (uint8)",
190
- "function getCreditLineDetails(address account) view returns (uint256 agentId, uint256 limit, uint256 used, uint256 aprBps, uint256 accruedInterest, uint256 requestedLimit, uint256 createdAt, uint256 lastActivityAt, uint8 status)",
191
- "function getAgentAccount(uint256 agentId) view returns (address)",
192
- "function previewScoredLimit(uint256 agentId) view returns (uint256 limit, uint256 score, bool eligible)",
193
- "function dailyDrawLimit() view returns (uint256)",
194
- "function totalBorrowed() view returns (uint256)",
195
- // Events
196
- "event CreditApplied(uint256 indexed creditLineId, uint256 indexed agentId, uint256 requestedLimit)",
197
- "event CreditApproved(uint256 indexed creditLineId, uint256 limit, uint256 aprBps)",
198
- "event CreditRejected(uint256 indexed creditLineId, string reason)",
199
- "event CreditFrozen(uint256 indexed creditLineId)",
200
- "event CreditUnfrozen(uint256 indexed creditLineId)",
201
- "event DefaultDeclared(address indexed account, uint256 amount)"
202
- ];
203
- var LP_VAULT_ABI = [
204
- "function asset() view returns (address)",
205
- "function totalAssets() view returns (uint256)",
206
- "function totalSupply() view returns (uint256)",
207
- "function balanceOf(address account) view returns (uint256)",
208
- "function convertToShares(uint256 assets) view returns (uint256)",
209
- "function convertToAssets(uint256 shares) view returns (uint256)",
210
- "function previewDeposit(uint256 assets) view returns (uint256)",
211
- "function previewRedeem(uint256 shares) view returns (uint256)",
212
- "function deposit(uint256 assets, address receiver) returns (uint256)",
213
- "function withdraw(uint256 assets, address receiver, address owner) returns (uint256)",
214
- "function redeem(uint256 shares, address receiver, address owner) returns (uint256)",
215
- "function totalBorrowed() view returns (uint256)",
216
- "function availableLiquidity() view returns (uint256)",
217
- "event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares)",
218
- "event Withdraw(address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)"
143
+ "function withdrawETH(uint256 amount, address to)",
144
+ "function isValidSignature(bytes32 hash, bytes signature) view returns (bytes4)"
219
145
  ];
220
146
  var AGENT_REPUTATION_ABI = [
221
- "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))",
222
147
  "function getCreditScore(uint256 agentId) view returns (uint256)",
223
- "function getBayesianScore(uint256 agentId) view returns (uint256 combinedScore, uint256 confidence)",
224
- "function getRecommendedLimit(uint256 agentId, uint256 baseLimit) view returns (uint256)",
148
+ "function getAttestation(uint256 agentId) view returns (tuple(uint256 score, uint256 timestamp, address signer))",
149
+ "function isScoreFresh(uint256 agentId) view returns (bool fresh, uint256 age)",
225
150
  "function isEligible(uint256 agentId, uint256 minScore) view returns (bool eligible, uint256 currentScore)",
226
- "function getDecayedScore(uint256 agentId) view returns (uint256)",
227
- "function getScoreExplanation(uint256 agentId) view returns (uint256 historical, uint256 currentRisk, uint256 utilization, uint256 onChainTx, uint256 newCredit, uint256 rawScore, uint256 decayedScore)",
228
- "function getLoanPositions(uint256 agentId) view returns (tuple(uint256 amount, uint256 collateral, uint256 ltvBps, uint256 timestamp, bool liquidated, bool repaid, bool active)[])",
229
- "function stressMultiplier() view returns (uint256)",
230
- "function decayPeriod() view returns (uint256)"
151
+ "function oracleSigner() view returns (address)",
152
+ "function submitScore(uint256 agentId, uint256 score_, uint256 timestamp_, bytes signature)",
153
+ "function setOracleSigner(address signer_)",
154
+ "event ScoreUpdated(uint256 indexed agentId, uint256 score, uint256 timestamp, address signer)"
231
155
  ];
232
156
  var VALIDATION_REGISTRY_ABI = [
233
157
  "function isAgentCodeApproved(uint256 agentId) view returns (bool)",
234
158
  "function isAgentCodeApprovedForTag(uint256 agentId, string tag) view returns (bool)",
235
- "function getAgentValidations(uint256 agentId) view returns (bytes32[])"
159
+ "function getAgentValidations(uint256 agentId) view returns (bytes32[])",
160
+ "function getValidation(bytes32 requestHash) view returns (tuple(address validatorAddress, uint256 agentId, string requestURI, uint8 response, string responseURI, bytes32 responseHash, string tag, uint256 requestedAt, uint256 respondedAt, bool hasResponse))"
236
161
  ];
237
- var MORPHO_CREDIT_ABI = [
238
- ...CREDIT_PROVIDER_ABI,
239
- "function depositCollateral(address collateralToken, uint256 amount)",
240
- "function depositCollateralFor(address onBehalf, address collateralToken, uint256 amount)",
241
- "function withdrawCollateral(address collateralToken, uint256 amount)",
242
- "function drawWithCollateral(address collateralToken, uint256 amount)",
243
- "function repayWithCollateral(address collateralToken, uint256 amount)",
244
- "function getPosition(address account, address collateralToken) view returns (tuple(uint256 collateralAmount, uint256 borrowedAmount, uint256 borrowShares, bool isActive))",
245
- "function getSupportedCollaterals() view returns (address[])",
246
- "function markets(address collateralToken) view returns (tuple(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) params, bytes32 marketId, uint256 maxLtvBps, bool active))"
162
+ var MORPHO_BLUE_ABI = [
163
+ // Supply & Withdraw (lending side)
164
+ "function supply(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) returns (uint256 assetsSupplied, uint256 sharesSupplied)",
165
+ "function withdraw(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn)",
166
+ // Collateral
167
+ "function supplyCollateral(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, bytes data)",
168
+ "function withdrawCollateral(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, address receiver)",
169
+ // Borrow & Repay
170
+ "function borrow(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) returns (uint256 assetsBorrowed, uint256 sharesBorrowed)",
171
+ "function repay(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) returns (uint256 assetsRepaid, uint256 sharesRepaid)",
172
+ // Authorization
173
+ "function setAuthorization(address authorized, bool newIsAuthorized)",
174
+ "function isAuthorized(address authorizer, address authorized) view returns (bool)",
175
+ // Views
176
+ "function position(bytes32 id, address user) view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral)",
177
+ "function market(bytes32 id) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)",
178
+ "function idToMarketParams(bytes32 id) view returns (tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv))"
247
179
  ];
248
180
  var ERC20_ABI = [
249
181
  "function balanceOf(address account) view returns (uint256)",
@@ -252,57 +184,51 @@ var ERC20_ABI = [
252
184
  "function transfer(address to, uint256 amount) returns (bool)",
253
185
  "function transferFrom(address from, address to, uint256 amount) returns (bool)",
254
186
  "function decimals() view returns (uint8)",
255
- "function symbol() view returns (string)"
187
+ "function symbol() view returns (string)",
188
+ "function name() view returns (string)"
256
189
  ];
257
190
 
258
191
  // src/utils/config.ts
259
192
  var CONTRACT_ADDRESSES = {
260
193
  [1 /* Ethereum */]: {
261
194
  accountFactory: "0x0000000000000000000000000000000000000000",
262
- reputationCredit: "0x0000000000000000000000000000000000000000",
263
- lpVault: "0x0000000000000000000000000000000000000000",
264
195
  validationRegistry: "0x0000000000000000000000000000000000000000",
196
+ agentReputation: "0x0000000000000000000000000000000000000000",
265
197
  usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
266
198
  identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
267
- agentReputation: "0x0000000000000000000000000000000000000000"
199
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
268
200
  },
269
201
  [8453 /* Base */]: {
270
- accountFactory: "0xeB72f248Ad9F4bf4024e8D9da75cf7AAD37B58f5",
271
- reputationCredit: "0x57B2B4ef3e7B8BE5FC86c6369602125d240F552A",
272
- lpVault: "0x612A80D6c3175F8283e9C7EE71d5177fE9acc338",
273
- validationRegistry: "0x8842f2383A86134Dd80c3Ecf6Bbae2e38396A5ec",
202
+ accountFactory: "0x7D5D56416bAEA06a9DCBe3092eF335724C6320a0",
203
+ validationRegistry: "0xd196C32D2149270F56E209ba7aEE67CE9ceD2001",
204
+ agentReputation: "0x65c9cA1211809D3CF3A2707558198eb2b2bE623c",
274
205
  usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
275
206
  identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
276
- agentReputation: "0xF1bed094D4E33E47CC8C72E086FFFde09e2211b4",
277
- morphoCredit: "0x7dFfa40E17471F7f26F5662D0F07a31977F47BeB"
207
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
278
208
  },
279
209
  [84532 /* BaseSepolia */]: {
280
210
  accountFactory: "0x0000000000000000000000000000000000000000",
281
- reputationCredit: "0x0000000000000000000000000000000000000000",
282
- lpVault: "0x0000000000000000000000000000000000000000",
283
211
  validationRegistry: "0x0000000000000000000000000000000000000000",
212
+ agentReputation: "0x0000000000000000000000000000000000000000",
284
213
  usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
285
214
  identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
286
- agentReputation: "0x0000000000000000000000000000000000000000"
215
+ morphoBlue: "0x0000000000000000000000000000000000000000"
287
216
  },
288
217
  [11155111 /* Sepolia */]: {
289
218
  accountFactory: "0x0000000000000000000000000000000000000000",
290
- reputationCredit: "0x0000000000000000000000000000000000000000",
291
- lpVault: "0x0000000000000000000000000000000000000000",
292
219
  validationRegistry: "0x0000000000000000000000000000000000000000",
220
+ agentReputation: "0x0000000000000000000000000000000000000000",
293
221
  usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
294
222
  identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
295
- agentReputation: "0x0000000000000000000000000000000000000000"
223
+ morphoBlue: "0x0000000000000000000000000000000000000000"
296
224
  },
297
225
  [31337 /* Hardhat */]: {
298
226
  accountFactory: "0x0000000000000000000000000000000000000000",
299
- reputationCredit: "0x0000000000000000000000000000000000000000",
300
- lpVault: "0x0000000000000000000000000000000000000000",
301
227
  validationRegistry: "0x0000000000000000000000000000000000000000",
228
+ agentReputation: "0x0000000000000000000000000000000000000000",
302
229
  usdc: "0x56d4d6aEe0278c5Df2FA23Ecb32eC146C9446FDf",
303
230
  identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
304
- agentReputation: "0x0000000000000000000000000000000000000000",
305
- morphoCredit: "0x0000000000000000000000000000000000000000"
231
+ morphoBlue: "0x0000000000000000000000000000000000000000"
306
232
  }
307
233
  };
308
234
  var RPC_URLS = {
@@ -338,61 +264,44 @@ function createConfig(chainId, options) {
338
264
  }
339
265
  };
340
266
  }
341
- function getUSDCAddress(chainId) {
342
- return CONTRACT_ADDRESSES[chainId].usdc;
343
- }
344
267
 
345
268
  // src/clients/AgetherClient.ts
346
269
  var AgetherClient = class _AgetherClient {
347
- /**
348
- * Undercollateralized credit is disabled for now.
349
- * All credit operations go through MorphoCredit (overcollateralized).
350
- */
351
- _requireUndercollateralizedEnabled() {
352
- throw new AgetherError(
353
- "Undercollateralized credit is not available yet. Use MorphoCredit for overcollateralized lending via the CLI: agether morpho-deposit / morpho-draw / morpho-repay",
354
- "UNDERCOLLATERALIZED_NOT_AVAILABLE"
355
- );
356
- }
357
270
  constructor(options) {
358
271
  this.config = options.config;
359
272
  this.signer = options.signer;
360
273
  this.agentId = options.agentId;
361
274
  const provider = options.signer.provider;
362
- if (!provider) {
363
- throw new AgetherError("Signer must have a provider", "NO_PROVIDER");
364
- }
275
+ if (!provider) throw new AgetherError("Signer must have a provider", "NO_PROVIDER");
365
276
  this.accountFactory = new import_ethers.Contract(
366
277
  options.config.contracts.accountFactory,
367
278
  ACCOUNT_FACTORY_ABI,
368
279
  options.signer
369
280
  );
370
- this.reputationCredit = new import_ethers.Contract(
371
- options.config.contracts.reputationCredit,
372
- REPUTATION_CREDIT_ABI,
373
- options.signer
281
+ this.identityRegistry = new import_ethers.Contract(
282
+ options.config.contracts.identityRegistry,
283
+ IDENTITY_REGISTRY_ABI,
284
+ provider
285
+ );
286
+ this.validationRegistry = new import_ethers.Contract(
287
+ options.config.contracts.validationRegistry,
288
+ VALIDATION_REGISTRY_ABI,
289
+ provider
290
+ );
291
+ this.agentReputation = new import_ethers.Contract(
292
+ options.config.contracts.agentReputation,
293
+ AGENT_REPUTATION_ABI,
294
+ provider
374
295
  );
375
296
  }
376
- // ============ Static Factory ============
377
- /**
378
- * Create client from private key.
379
- *
380
- * Simplest usage — only needs key, agentId, and chainId:
381
- * AgetherClient.fromPrivateKey(key, 42n, ChainId.Sepolia)
382
- *
383
- * All contract addresses and RPC URLs are resolved automatically.
384
- */
297
+ // Static Factory
385
298
  static fromPrivateKey(privateKey, agentId, chainIdOrConfig) {
386
299
  const config = typeof chainIdOrConfig === "number" ? getDefaultConfig(chainIdOrConfig) : chainIdOrConfig;
387
300
  const provider = new import_ethers.ethers.JsonRpcProvider(config.rpcUrl);
388
301
  const signer = new import_ethers.ethers.Wallet(privateKey, provider);
389
302
  return new _AgetherClient({ config, signer, agentId });
390
303
  }
391
- // ============ Account Management ============
392
- /**
393
- * Create an AgentAccount (smart wallet) for this agent.
394
- * Returns the account address.
395
- */
304
+ // Account Management
396
305
  async createAccount() {
397
306
  const tx = await this.accountFactory.createAccount(this.agentId);
398
307
  const receipt = await tx.wait();
@@ -410,138 +319,113 @@ var AgetherClient = class _AgetherClient {
410
319
  }
411
320
  return this.accountAddress;
412
321
  }
413
- /**
414
- * Get the AgentAccount address for this agent.
415
- */
416
322
  async getAccountAddress() {
417
323
  if (this.accountAddress) return this.accountAddress;
418
324
  const addr = await this.accountFactory.getAccount(this.agentId);
419
325
  if (addr === import_ethers.ethers.ZeroAddress) {
420
- throw new AgetherError(
421
- "No account found. Create one first with createAccount().",
422
- "NO_ACCOUNT"
423
- );
326
+ throw new AgetherError("No account found. Create one with createAccount().", "NO_ACCOUNT");
424
327
  }
425
328
  this.accountAddress = addr;
426
329
  return addr;
427
330
  }
428
- /**
429
- * Check if an account already exists.
430
- */
431
331
  async accountExists() {
432
332
  return this.accountFactory.accountExists(this.agentId);
433
333
  }
434
- // ============ Credit Application ============
435
- /**
436
- * Apply for a credit line.
437
- * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
438
- */
439
- async apply(_limitOrApplication) {
440
- this._requireUndercollateralizedEnabled();
334
+ async predictAddress() {
335
+ return this.accountFactory.predictAddress(this.agentId);
441
336
  }
442
- // ============ Credit Line Info ============
443
- /**
444
- * Get full credit line details from ReputationCredit.
445
- */
446
- async getCreditLine() {
447
- const account = await this.getAccountAddress();
448
- const data = await this.reputationCredit.getCreditLineDetails(account);
449
- return {
450
- agentId: data[0],
451
- limit: data[1],
452
- used: data[2],
453
- aprBps: data[3],
454
- accruedInterest: data[4],
455
- requestedLimit: data[5],
456
- createdAt: data[6],
457
- lastActivityAt: data[7],
458
- status: Number(data[8])
337
+ // Balances
338
+ async getBalances() {
339
+ const provider = this.signer.provider;
340
+ const eoaAddr = await this.signer.getAddress();
341
+ const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, provider);
342
+ const ethBal = await provider.getBalance(eoaAddr);
343
+ const usdcBal = await usdc.balanceOf(eoaAddr);
344
+ const result = {
345
+ eoa: { eth: import_ethers.ethers.formatEther(ethBal), usdc: import_ethers.ethers.formatUnits(usdcBal, 6) }
459
346
  };
347
+ try {
348
+ const acctAddr = await this.getAccountAddress();
349
+ const acctEth = await provider.getBalance(acctAddr);
350
+ const acctUsdc = await usdc.balanceOf(acctAddr);
351
+ result.account = {
352
+ address: acctAddr,
353
+ eth: import_ethers.ethers.formatEther(acctEth),
354
+ usdc: import_ethers.ethers.formatUnits(acctUsdc, 6)
355
+ };
356
+ } catch {
357
+ }
358
+ return result;
460
359
  }
461
- /**
462
- * Get ICreditProvider.CreditInfo view.
463
- */
464
- async getCreditInfo() {
465
- const account = await this.getAccountAddress();
466
- const info = await this.reputationCredit.getCreditInfo(account);
360
+ async fundAccount(usdcAmount) {
361
+ const acctAddr = await this.getAccountAddress();
362
+ const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, this.signer);
363
+ const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
364
+ const tx = await usdc.transfer(acctAddr, amount);
365
+ const receipt = await tx.wait();
467
366
  return {
468
- limit: info.limit,
469
- used: info.used,
470
- available: info.available,
471
- accruedInterest: info.accruedInterest,
472
- aprBps: info.aprBps,
473
- isActive: info.isActive,
474
- requiresCollateral: info.requiresCollateral
367
+ txHash: receipt.hash,
368
+ blockNumber: receipt.blockNumber,
369
+ status: receipt.status === 1 ? "success" : "failed",
370
+ gasUsed: receipt.gasUsed
475
371
  };
476
372
  }
477
- /**
478
- * Get credit line status.
479
- */
480
- async getStatus() {
481
- const account = await this.getAccountAddress();
482
- const status = await this.reputationCredit.getCreditLineStatus(account);
483
- return Number(status);
484
- }
485
- /**
486
- * Get available credit.
487
- */
488
- async getAvailableCredit() {
489
- const account = await this.getAccountAddress();
490
- return this.reputationCredit.maxDrawable(account);
373
+ async withdrawUsdc(usdcAmount) {
374
+ const acctAddr = await this.getAccountAddress();
375
+ const account = new import_ethers.Contract(acctAddr, AGENT_ACCOUNT_ABI, this.signer);
376
+ const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
377
+ const eoaAddr = await this.signer.getAddress();
378
+ const tx = await account.withdraw(this.config.contracts.usdc, amount, eoaAddr);
379
+ const receipt = await tx.wait();
380
+ return {
381
+ txHash: receipt.hash,
382
+ blockNumber: receipt.blockNumber,
383
+ status: receipt.status === 1 ? "success" : "failed",
384
+ gasUsed: receipt.gasUsed
385
+ };
491
386
  }
492
- /**
493
- * Get total debt (principal + interest).
494
- */
495
- async getTotalDebt() {
496
- const account = await this.getAccountAddress();
497
- return this.reputationCredit.getTotalDebt(account);
387
+ async withdrawEth(ethAmount) {
388
+ const acctAddr = await this.getAccountAddress();
389
+ const account = new import_ethers.Contract(acctAddr, AGENT_ACCOUNT_ABI, this.signer);
390
+ const amount = import_ethers.ethers.parseEther(ethAmount);
391
+ const eoaAddr = await this.signer.getAddress();
392
+ const tx = await account.withdrawETH(amount, eoaAddr);
393
+ const receipt = await tx.wait();
394
+ return {
395
+ txHash: receipt.hash,
396
+ blockNumber: receipt.blockNumber,
397
+ status: receipt.status === 1 ? "success" : "failed",
398
+ gasUsed: receipt.gasUsed
399
+ };
498
400
  }
499
- /**
500
- * Check if agent is eligible for credit.
501
- */
502
- async isEligible() {
503
- const account = await this.getAccountAddress();
504
- return this.reputationCredit.isEligible(account);
401
+ // Identity & Validation
402
+ async isKyaApproved() {
403
+ return this.validationRegistry.isAgentCodeApproved(this.agentId);
505
404
  }
506
- // ============ Draw & Repay ============
507
- /**
508
- * Draw funds from credit line via AgentAccount.
509
- * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
510
- */
511
- async draw(_amountOrRequest) {
512
- this._requireUndercollateralizedEnabled();
405
+ async identityExists() {
406
+ try {
407
+ await this.identityRegistry.ownerOf(this.agentId);
408
+ return true;
409
+ } catch {
410
+ return false;
411
+ }
513
412
  }
514
- /**
515
- * Repay credit line debt via AgentAccount.
516
- * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
517
- */
518
- async repay(_amountOrRequest) {
519
- this._requireUndercollateralizedEnabled();
413
+ async getIdentityOwner() {
414
+ return this.identityRegistry.ownerOf(this.agentId);
520
415
  }
521
- /**
522
- * Repay full debt.
523
- * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
524
- */
525
- async repayFull() {
526
- this._requireUndercollateralizedEnabled();
416
+ // Reputation
417
+ async getCreditScore() {
418
+ return this.agentReputation.getCreditScore(this.agentId);
527
419
  }
528
- // ============ Limit Increase ============
529
- /**
530
- * Request a limit increase on-chain.
531
- * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
532
- */
533
- async requestLimitIncrease(_newLimit) {
534
- this._requireUndercollateralizedEnabled();
420
+ async isScoreFresh() {
421
+ const [fresh, age] = await this.agentReputation.isScoreFresh(this.agentId);
422
+ return { fresh, age };
535
423
  }
536
- // ============ x402 Payments ============
537
- /**
538
- * Pay for a service using credit line.
539
- * @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
540
- */
541
- async pay(_service, _amount, _asset = "USDC") {
542
- this._requireUndercollateralizedEnabled();
424
+ async isEligible(minScore = 500n) {
425
+ const [eligible, currentScore] = await this.agentReputation.isEligible(this.agentId, minScore);
426
+ return { eligible, currentScore };
543
427
  }
544
- // ============ Getters ============
428
+ // Getters
545
429
  get chainId() {
546
430
  return this.config.chainId;
547
431
  }
@@ -551,643 +435,640 @@ var AgetherClient = class _AgetherClient {
551
435
  get currentAccountAddress() {
552
436
  return this.accountAddress;
553
437
  }
554
- /** Get the underlying signer */
555
438
  getSigner() {
556
439
  return this.signer;
557
440
  }
441
+ getAgentId() {
442
+ return this.agentId;
443
+ }
558
444
  };
559
445
 
560
- // src/clients/ScoringClient.ts
446
+ // src/clients/MorphoClient.ts
447
+ var import_ethers2 = require("ethers");
561
448
  var import_axios = __toESM(require("axios"));
562
- var ScoringClient = class {
563
- constructor(endpoint, apiKey) {
564
- this.client = import_axios.default.create({
565
- baseURL: endpoint,
566
- headers: {
567
- "Content-Type": "application/json",
568
- ...apiKey && { Authorization: `Bearer ${apiKey}` }
569
- },
570
- timeout: 3e4
571
- });
449
+ var BASE_COLLATERALS = {
450
+ WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
451
+ wstETH: { address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", symbol: "wstETH", decimals: 18 },
452
+ cbETH: { address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", symbol: "cbETH", decimals: 18 }
453
+ };
454
+ var MORPHO_API_URL = "https://api.morpho.org/graphql";
455
+ var morphoIface = new import_ethers2.ethers.Interface(MORPHO_BLUE_ABI);
456
+ var erc20Iface = new import_ethers2.ethers.Interface(ERC20_ABI);
457
+ var MorphoClient = class {
458
+ constructor(config) {
459
+ this._marketCache = /* @__PURE__ */ new Map();
460
+ this._discoveredAt = 0;
461
+ const chainId = config.chainId ?? 8453 /* Base */;
462
+ const defaultCfg = getDefaultConfig(chainId);
463
+ this.config = defaultCfg;
464
+ this.agentId = config.agentId;
465
+ this.provider = new import_ethers2.ethers.JsonRpcProvider(config.rpcUrl || defaultCfg.rpcUrl);
466
+ this.wallet = new import_ethers2.ethers.Wallet(config.privateKey, this.provider);
467
+ const addrs = { ...defaultCfg.contracts, ...config.contracts };
468
+ this.accountFactory = new import_ethers2.Contract(addrs.accountFactory, ACCOUNT_FACTORY_ABI, this.wallet);
469
+ this.morphoBlue = new import_ethers2.Contract(addrs.morphoBlue, MORPHO_BLUE_ABI, this.provider);
470
+ this.agentReputation = new import_ethers2.Contract(addrs.agentReputation, AGENT_REPUTATION_ABI, this.provider);
471
+ this.identityRegistry = new import_ethers2.Contract(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this.provider);
472
+ }
473
+ // ════════════════════════════════════════════════════════
474
+ // Account Management
475
+ // ════════════════════════════════════════════════════════
476
+ /** Resolve the AgentAccount address (cached). */
477
+ async getAccountAddress() {
478
+ if (this._accountAddress) return this._accountAddress;
479
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
480
+ const addr = await this.accountFactory.getAccount(BigInt(this.agentId));
481
+ if (addr === import_ethers2.ethers.ZeroAddress) {
482
+ throw new AgetherError("No AgentAccount found. Call register() first.", "NO_ACCOUNT");
483
+ }
484
+ this._accountAddress = addr;
485
+ return addr;
486
+ }
487
+ getAgentId() {
488
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
489
+ return this.agentId;
490
+ }
491
+ getWalletAddress() {
492
+ return this.wallet.address;
572
493
  }
573
494
  /**
574
- * Evaluate a credit application (does not submit on-chain)
575
- * Backend endpoint: POST /credit/evaluate
495
+ * Register: create ERC-8004 identity + AgentAccount in one flow.
496
+ * If already registered, returns existing state.
576
497
  */
577
- async evaluateCredit(request) {
578
- try {
579
- const response = await this.client.post("/credit/evaluate", {
580
- agentId: request.agentId.toString(),
581
- requestedLimit: request.requestedLimit.toString(),
582
- codeHash: request.codeHash
583
- });
584
- const data = response.data;
585
- if (!data.approved) {
586
- throw new ScoringRejectedError(data.riskScore, data.reason);
587
- }
588
- return {
589
- approved: data.approved,
590
- limit: BigInt(data.limit),
591
- aprBps: data.aprBps,
592
- riskScore: data.riskScore,
593
- bayesianScore: data.bayesianScore ?? 0,
594
- confidence: data.confidence ?? 0,
595
- reason: data.reason
596
- };
597
- } catch (error) {
598
- if (error instanceof ScoringRejectedError) {
599
- throw error;
600
- }
601
- if (import_axios.default.isAxiosError(error)) {
602
- throw new AgetherError(
603
- `Scoring request failed: ${error.message}`,
604
- "SCORING_REQUEST_FAILED",
605
- { status: error.response?.status }
606
- );
498
+ async register(_name) {
499
+ const eoaAddr = this.wallet.address;
500
+ if (this.agentId) {
501
+ const exists = await this.accountFactory.accountExists(BigInt(this.agentId));
502
+ if (exists) {
503
+ const acct = await this.accountFactory.getAccount(BigInt(this.agentId));
504
+ this._accountAddress = acct;
505
+ return { agentId: this.agentId, address: eoaAddr, agentAccount: acct, alreadyRegistered: true };
607
506
  }
608
- throw error;
609
507
  }
610
- }
611
- /**
612
- * Process a credit application (evaluate + on-chain approve/reject)
613
- * Backend endpoint: POST /admin/credit/process
614
- *
615
- * Requires the credit application to be in Pending status on-chain
616
- * (agent must have called applyForCredit first).
617
- */
618
- async processApplication(agentId, requestedLimit, codeHash) {
619
- try {
620
- const response = await this.client.post("/admin/credit/process", {
621
- agentId: agentId.toString(),
622
- requestedLimit: requestedLimit.toString(),
623
- codeHash
624
- });
625
- return response.data;
626
- } catch (error) {
627
- if (import_axios.default.isAxiosError(error)) {
628
- if (error.response?.status === 400) {
629
- return error.response.data;
508
+ const balance = await this.identityRegistry.balanceOf(eoaAddr);
509
+ let agentId;
510
+ if (balance > 0n && this.agentId) {
511
+ agentId = BigInt(this.agentId);
512
+ } else if (balance > 0n) {
513
+ throw new AgetherError(
514
+ "Wallet already has an ERC-8004 identity but agentId is unknown. Pass agentId in config.",
515
+ "AGENT_ID_UNKNOWN"
516
+ );
517
+ } else {
518
+ const regTx = await this.identityRegistry.register();
519
+ const regReceipt = await regTx.wait();
520
+ agentId = 0n;
521
+ for (const log of regReceipt.logs) {
522
+ try {
523
+ const parsed = this.identityRegistry.interface.parseLog({ topics: log.topics, data: log.data });
524
+ if (parsed?.name === "Transfer") {
525
+ agentId = parsed.args[2];
526
+ break;
527
+ }
528
+ } catch {
529
+ continue;
630
530
  }
631
- throw new AgetherError(
632
- `Process application failed: ${error.message}`,
633
- "PROCESS_APPLICATION_FAILED",
634
- { status: error.response?.status }
635
- );
636
531
  }
637
- throw error;
532
+ if (agentId === 0n) throw new AgetherError("Failed to parse agentId from registration", "PARSE_ERROR");
638
533
  }
534
+ this.agentId = agentId.toString();
535
+ const acctExists = await this.accountFactory.accountExists(agentId);
536
+ let txHash;
537
+ if (!acctExists) {
538
+ const tx = await this.accountFactory.createAccount(agentId);
539
+ const receipt = await tx.wait();
540
+ txHash = receipt.hash;
541
+ }
542
+ const acctAddr = await this.accountFactory.getAccount(agentId);
543
+ this._accountAddress = acctAddr;
544
+ return {
545
+ agentId: this.agentId,
546
+ address: eoaAddr,
547
+ agentAccount: acctAddr,
548
+ alreadyRegistered: acctExists,
549
+ tx: txHash
550
+ };
639
551
  }
640
- /**
641
- * Get credit score for an agent
642
- * Backend endpoint: GET /credit/score/:agentId
643
- */
644
- async getCreditScore(agentId) {
645
- const response = await this.client.get(
646
- `/credit/score/${agentId.toString()}`
647
- );
648
- return response.data;
649
- }
650
- /**
651
- * Preview scored limit for an agent
652
- * Backend endpoint: GET /credit/preview/:agentId
653
- */
654
- async previewScoredLimit(agentId) {
655
- const response = await this.client.get(
656
- `/credit/preview/${agentId.toString()}`
657
- );
658
- return response.data;
659
- }
660
- /**
661
- * Get credit line info for an agent
662
- * Backend endpoint: GET /credit/agent/:agentId
663
- */
664
- async getAgentCredit(agentId) {
665
- const response = await this.client.get(
666
- `/credit/agent/${agentId.toString()}`
667
- );
668
- return response.data;
552
+ /** Get ETH / USDC balances for EOA and AgentAccount. */
553
+ async getBalances() {
554
+ const eoaAddr = this.wallet.address;
555
+ const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, this.provider);
556
+ const ethBal = await this.provider.getBalance(eoaAddr);
557
+ const usdcBal = await usdc.balanceOf(eoaAddr);
558
+ const result = {
559
+ agentId: this.agentId || "?",
560
+ address: eoaAddr,
561
+ eth: import_ethers2.ethers.formatEther(ethBal),
562
+ usdc: import_ethers2.ethers.formatUnits(usdcBal, 6)
563
+ };
564
+ try {
565
+ const acctAddr = await this.getAccountAddress();
566
+ const acctEth = await this.provider.getBalance(acctAddr);
567
+ const acctUsdc = await usdc.balanceOf(acctAddr);
568
+ result.agentAccount = {
569
+ address: acctAddr,
570
+ eth: import_ethers2.ethers.formatEther(acctEth),
571
+ usdc: import_ethers2.ethers.formatUnits(acctUsdc, 6)
572
+ };
573
+ } catch {
574
+ }
575
+ return result;
669
576
  }
670
- /**
671
- * Get protocol info
672
- * Backend endpoint: GET /credit/protocol-info
673
- */
674
- async getProtocolInfo() {
675
- const response = await this.client.get("/credit/protocol-info");
676
- return response.data;
577
+ /** Transfer USDC from EOA to AgentAccount. */
578
+ async fundAccount(usdcAmount) {
579
+ const acctAddr = await this.getAccountAddress();
580
+ const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, this.wallet);
581
+ const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
582
+ const tx = await usdc.transfer(acctAddr, amount);
583
+ const receipt = await tx.wait();
584
+ return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
677
585
  }
586
+ // ════════════════════════════════════════════════════════
587
+ // Market Discovery (Morpho GraphQL API)
588
+ // ════════════════════════════════════════════════════════
678
589
  /**
679
- * Get backend service status
680
- * Backend endpoint: GET /status
590
+ * Fetch USDC borrow markets on Base from Morpho API.
591
+ * Caches results for 5 minutes.
681
592
  */
682
- async getStatus() {
683
- const response = await this.client.get("/status");
684
- return response.data;
593
+ async getMarkets(forceRefresh = false) {
594
+ if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
595
+ return this._discoveredMarkets;
596
+ }
597
+ const chainId = this.config.chainId;
598
+ const usdcAddr = this.config.contracts.usdc.toLowerCase();
599
+ const query = `{
600
+ markets(
601
+ first: 50
602
+ orderBy: SupplyAssetsUsd
603
+ orderDirection: Desc
604
+ where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"] }
605
+ ) {
606
+ items {
607
+ uniqueKey
608
+ lltv
609
+ oracleAddress
610
+ irmAddress
611
+ loanAsset { address symbol decimals }
612
+ collateralAsset { address symbol decimals }
613
+ state {
614
+ borrowAssets
615
+ supplyAssets
616
+ utilization
617
+ }
618
+ }
619
+ }
620
+ }`;
621
+ try {
622
+ const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
623
+ const items = resp.data?.data?.markets?.items ?? [];
624
+ this._discoveredMarkets = items.map((m) => ({
625
+ uniqueKey: m.uniqueKey,
626
+ loanAsset: m.loanAsset,
627
+ collateralAsset: m.collateralAsset ?? { address: import_ethers2.ethers.ZeroAddress, symbol: "N/A", decimals: 0 },
628
+ oracle: m.oracleAddress,
629
+ irm: m.irmAddress,
630
+ lltv: BigInt(m.lltv),
631
+ totalSupplyAssets: BigInt(m.state?.supplyAssets ?? "0"),
632
+ totalBorrowAssets: BigInt(m.state?.borrowAssets ?? "0"),
633
+ utilization: m.state?.utilization ? Number(m.state.utilization) : 0
634
+ }));
635
+ this._discoveredAt = Date.now();
636
+ for (const mi of this._discoveredMarkets) {
637
+ if (mi.collateralAsset.address !== import_ethers2.ethers.ZeroAddress) {
638
+ this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
639
+ loanToken: mi.loanAsset.address,
640
+ collateralToken: mi.collateralAsset.address,
641
+ oracle: mi.oracle,
642
+ irm: mi.irm,
643
+ lltv: mi.lltv
644
+ });
645
+ }
646
+ }
647
+ return this._discoveredMarkets;
648
+ } catch {
649
+ return this._discoveredMarkets ?? [];
650
+ }
685
651
  }
686
652
  /**
687
- * Get full OCCR score explanation with subscores, decay, and loan positions
688
- * Backend endpoint: GET /agents/:agentId/score-explanation
653
+ * Get MarketParams for a collateral token.
654
+ * Tries cache API → on-chain idToMarketParams.
689
655
  */
690
- async getScoreExplanation(agentId) {
691
- const response = await this.client.get(
692
- `/agents/${agentId.toString()}/score-explanation`
656
+ async findMarketForCollateral(collateralSymbolOrAddress) {
657
+ const colInfo = BASE_COLLATERALS[collateralSymbolOrAddress];
658
+ const colAddr = (colInfo?.address ?? collateralSymbolOrAddress).toLowerCase();
659
+ const cached = this._marketCache.get(colAddr);
660
+ if (cached) return cached;
661
+ await this.getMarkets();
662
+ const fromApi = this._marketCache.get(colAddr);
663
+ if (fromApi) return fromApi;
664
+ throw new AgetherError(
665
+ `No Morpho market found for collateral ${collateralSymbolOrAddress}`,
666
+ "MARKET_NOT_FOUND"
693
667
  );
694
- return response.data;
695
668
  }
696
- };
697
-
698
- // src/clients/VaultClient.ts
699
- var import_ethers2 = require("ethers");
700
- var VaultClient = class {
701
- constructor(options) {
702
- this.config = options.config;
703
- this.signer = options.signer;
704
- this.vault = new import_ethers2.Contract(
705
- options.config.contracts.lpVault,
706
- LP_VAULT_ABI,
707
- options.signer
708
- );
669
+ /** Read MarketParams on-chain by market ID (bytes32). */
670
+ async getMarketParams(marketId) {
671
+ const result = await this.morphoBlue.idToMarketParams(marketId);
672
+ return {
673
+ loanToken: result.loanToken,
674
+ collateralToken: result.collateralToken,
675
+ oracle: result.oracle,
676
+ irm: result.irm,
677
+ lltv: result.lltv
678
+ };
709
679
  }
710
- // ============ LP Operations ============
711
- /**
712
- * Deposit assets to vault
713
- */
714
- async deposit(amount) {
715
- const asset = await this.getAsset();
716
- const allowance = await asset.allowance(
717
- await this.signer.getAddress(),
718
- this.config.contracts.lpVault
719
- );
720
- if (allowance < amount) {
721
- const approveTx = await asset.approve(
722
- this.config.contracts.lpVault,
723
- amount
724
- );
725
- await approveTx.wait();
726
- }
727
- const receiver = await this.signer.getAddress();
728
- const tx = await this.vault.deposit(amount, receiver);
729
- const receipt = await tx.wait();
680
+ // ════════════════════════════════════════════════════════
681
+ // Position Reads
682
+ // ════════════════════════════════════════════════════════
683
+ /** Read on-chain position for a specific market. */
684
+ async getPosition(marketId) {
685
+ const acctAddr = await this.getAccountAddress();
686
+ const pos = await this.morphoBlue.position(marketId, acctAddr);
730
687
  return {
731
- txHash: receipt.hash,
732
- blockNumber: receipt.blockNumber,
733
- status: receipt.status === 1 ? "success" : "failed",
734
- gasUsed: receipt.gasUsed
688
+ supplyShares: pos.supplyShares,
689
+ borrowShares: pos.borrowShares,
690
+ collateral: pos.collateral
735
691
  };
736
692
  }
737
693
  /**
738
- * Withdraw assets from vault
694
+ * Full status: positions across all discovered markets.
739
695
  */
740
- async withdraw(amount) {
741
- const receiver = await this.signer.getAddress();
742
- const owner = receiver;
743
- const tx = await this.vault.withdraw(amount, receiver, owner);
744
- const receipt = await tx.wait();
696
+ async getStatus() {
697
+ const acctAddr = await this.getAccountAddress();
698
+ const markets = await this.getMarkets();
699
+ const positions = [];
700
+ let totalDebt = 0n;
701
+ for (const m of markets) {
702
+ if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
703
+ try {
704
+ const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
705
+ if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
706
+ let debt = 0n;
707
+ if (pos.borrowShares > 0n) {
708
+ try {
709
+ const mkt = await this.morphoBlue.market(m.uniqueKey);
710
+ const totalBorrowShares = BigInt(mkt.totalBorrowShares);
711
+ const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
712
+ debt = totalBorrowShares > 0n ? BigInt(pos.borrowShares) * totalBorrowAssets / totalBorrowShares : 0n;
713
+ totalDebt += debt;
714
+ } catch {
715
+ }
716
+ }
717
+ positions.push({
718
+ marketId: m.uniqueKey,
719
+ collateralToken: m.collateralAsset.symbol,
720
+ collateral: import_ethers2.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
721
+ borrowShares: pos.borrowShares.toString(),
722
+ supplyShares: pos.supplyShares.toString(),
723
+ debt: import_ethers2.ethers.formatUnits(debt, 6)
724
+ });
725
+ } catch {
726
+ continue;
727
+ }
728
+ }
745
729
  return {
746
- txHash: receipt.hash,
747
- blockNumber: receipt.blockNumber,
748
- status: receipt.status === 1 ? "success" : "failed",
749
- gasUsed: receipt.gasUsed
730
+ agentId: this.agentId || "?",
731
+ agentAccount: acctAddr,
732
+ totalDebt: import_ethers2.ethers.formatUnits(totalDebt, 6),
733
+ positions
750
734
  };
751
735
  }
736
+ // ════════════════════════════════════════════════════════
737
+ // Lending Operations (all via AgentAccount.executeBatch)
738
+ // ════════════════════════════════════════════════════════
752
739
  /**
753
- * Redeem shares for assets
754
- */
755
- async redeem(shares) {
756
- const receiver = await this.signer.getAddress();
757
- const owner = receiver;
758
- const tx = await this.vault.redeem(shares, receiver, owner);
759
- const receipt = await tx.wait();
740
+ * Deposit collateral into Morpho Blue.
741
+ *
742
+ * Flow:
743
+ * 1. EOA transfers collateral to AgentAccount
744
+ * 2. AgentAccount.executeBatch:
745
+ * [collateral.approve(MorphoBlue), Morpho.supplyCollateral(params)]
746
+ */
747
+ async supplyCollateral(tokenSymbol, amount, marketParams) {
748
+ const acctAddr = await this.getAccountAddress();
749
+ const colInfo = BASE_COLLATERALS[tokenSymbol];
750
+ if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
751
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
752
+ const weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
753
+ const morphoAddr = this.config.contracts.morphoBlue;
754
+ const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this.wallet);
755
+ const transferTx = await colToken.transfer(acctAddr, weiAmount);
756
+ await transferTx.wait();
757
+ const targets = [colInfo.address, morphoAddr];
758
+ const values = [0n, 0n];
759
+ const datas = [
760
+ erc20Iface.encodeFunctionData("approve", [morphoAddr, weiAmount]),
761
+ morphoIface.encodeFunctionData("supplyCollateral", [
762
+ this._toTuple(params),
763
+ weiAmount,
764
+ acctAddr,
765
+ "0x"
766
+ ])
767
+ ];
768
+ const receipt = await this.batch(targets, values, datas);
760
769
  return {
761
- txHash: receipt.hash,
762
- blockNumber: receipt.blockNumber,
763
- status: receipt.status === 1 ? "success" : "failed",
764
- gasUsed: receipt.gasUsed
770
+ tx: receipt.hash,
771
+ collateralToken: tokenSymbol,
772
+ amount,
773
+ agentAccount: acctAddr
765
774
  };
766
775
  }
767
- // ============ View Functions ============
768
776
  /**
769
- * Get vault statistics
770
- */
771
- async getStats() {
772
- const [totalAssets, totalBorrowed, availableLiquidity] = await Promise.all([
773
- this.vault.totalAssets(),
774
- this.vault.totalBorrowed(),
775
- this.vault.availableLiquidity()
777
+ * Borrow USDC against existing collateral.
778
+ *
779
+ * AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
780
+ *
781
+ * @param usdcAmount - USDC amount (e.g. '100')
782
+ * @param tokenSymbol - collateral symbol to identify which market (default: first with collateral)
783
+ */
784
+ async borrow(usdcAmount, tokenSymbol, marketParams) {
785
+ const acctAddr = await this.getAccountAddress();
786
+ const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
787
+ const morphoAddr = this.config.contracts.morphoBlue;
788
+ let params;
789
+ let usedToken = tokenSymbol || "WETH";
790
+ if (marketParams) {
791
+ params = marketParams;
792
+ } else if (tokenSymbol) {
793
+ params = await this.findMarketForCollateral(tokenSymbol);
794
+ } else {
795
+ const { params: p, symbol } = await this._findActiveMarket();
796
+ params = p;
797
+ usedToken = symbol;
798
+ }
799
+ const data = morphoIface.encodeFunctionData("borrow", [
800
+ this._toTuple(params),
801
+ amount,
802
+ 0n,
803
+ acctAddr,
804
+ acctAddr
776
805
  ]);
777
- const utilizationRate = totalAssets > 0n ? Number(totalBorrowed * 10000n / totalAssets) / 100 : 0;
778
- const totalSupply = await this.vault.totalSupply();
779
- const sharePrice = totalSupply > 0n ? totalAssets * BigInt(1e18) / totalSupply : BigInt(1e18);
806
+ const receipt = await this.exec(morphoAddr, data);
780
807
  return {
781
- totalAssets,
782
- totalBorrowed,
783
- availableLiquidity,
784
- utilizationRate,
785
- sharePrice
808
+ tx: receipt.hash,
809
+ amount: usdcAmount,
810
+ collateralToken: usedToken,
811
+ agentAccount: acctAddr
786
812
  };
787
813
  }
788
814
  /**
789
- * Get LP position
790
- */
791
- async getPosition(address) {
792
- const owner = address || await this.signer.getAddress();
793
- const shares = await this.vault.balanceOf(owner);
794
- const assets = await this.vault.convertToAssets(shares);
795
- const pendingYield = 0n;
815
+ * Deposit collateral AND borrow USDC in one batched transaction.
816
+ *
817
+ * AgentAccount.executeBatch:
818
+ * [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
819
+ *
820
+ * The collateral must be transferred to AgentAccount first.
821
+ */
822
+ async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
823
+ const acctAddr = await this.getAccountAddress();
824
+ const colInfo = BASE_COLLATERALS[tokenSymbol];
825
+ if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
826
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
827
+ const colWei = import_ethers2.ethers.parseUnits(collateralAmount, colInfo.decimals);
828
+ const borrowWei = import_ethers2.ethers.parseUnits(borrowUsdcAmount, 6);
829
+ const morphoAddr = this.config.contracts.morphoBlue;
830
+ const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this.wallet);
831
+ const transferTx = await colToken.transfer(acctAddr, colWei);
832
+ await transferTx.wait();
833
+ const targets = [colInfo.address, morphoAddr, morphoAddr];
834
+ const values = [0n, 0n, 0n];
835
+ const datas = [
836
+ erc20Iface.encodeFunctionData("approve", [morphoAddr, colWei]),
837
+ morphoIface.encodeFunctionData("supplyCollateral", [
838
+ this._toTuple(params),
839
+ colWei,
840
+ acctAddr,
841
+ "0x"
842
+ ]),
843
+ morphoIface.encodeFunctionData("borrow", [
844
+ this._toTuple(params),
845
+ borrowWei,
846
+ 0n,
847
+ acctAddr,
848
+ acctAddr
849
+ ])
850
+ ];
851
+ const receipt = await this.batch(targets, values, datas);
796
852
  return {
797
- shares,
798
- assets,
799
- pendingYield
853
+ tx: receipt.hash,
854
+ collateralToken: tokenSymbol,
855
+ collateralAmount,
856
+ borrowAmount: borrowUsdcAmount,
857
+ agentAccount: acctAddr
800
858
  };
801
859
  }
802
860
  /**
803
- * Preview deposit (how many shares for assets)
804
- */
805
- async previewDeposit(assets) {
806
- return await this.vault.previewDeposit(assets);
807
- }
808
- /**
809
- * Preview withdraw (how many assets for shares)
810
- */
811
- async previewRedeem(shares) {
812
- return await this.vault.previewRedeem(shares);
861
+ * Repay borrowed USDC from AgentAccount.
862
+ *
863
+ * AgentAccount.executeBatch:
864
+ * [USDC.approve(MorphoBlue), Morpho.repay(params)]
865
+ */
866
+ async repay(usdcAmount, tokenSymbol, marketParams) {
867
+ const acctAddr = await this.getAccountAddress();
868
+ const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
869
+ const morphoAddr = this.config.contracts.morphoBlue;
870
+ const usdcAddr = this.config.contracts.usdc;
871
+ let params;
872
+ if (marketParams) {
873
+ params = marketParams;
874
+ } else if (tokenSymbol) {
875
+ params = await this.findMarketForCollateral(tokenSymbol);
876
+ } else {
877
+ const { params: p } = await this._findActiveMarket();
878
+ params = p;
879
+ }
880
+ const targets = [usdcAddr, morphoAddr];
881
+ const values = [0n, 0n];
882
+ const datas = [
883
+ erc20Iface.encodeFunctionData("approve", [morphoAddr, amount]),
884
+ morphoIface.encodeFunctionData("repay", [
885
+ this._toTuple(params),
886
+ amount,
887
+ 0n,
888
+ acctAddr,
889
+ "0x"
890
+ ])
891
+ ];
892
+ const receipt = await this.batch(targets, values, datas);
893
+ let remainingDebt = "0";
894
+ try {
895
+ const status = await this.getStatus();
896
+ remainingDebt = status.totalDebt;
897
+ } catch {
898
+ }
899
+ return { tx: receipt.hash, amount: usdcAmount, remainingDebt };
813
900
  }
814
901
  /**
815
- * Check if withdrawal is allowed (sufficient liquidity)
816
- */
817
- async canWithdraw(assets) {
818
- const liquidity = await this.vault.availableLiquidity();
819
- return liquidity >= assets;
902
+ * Withdraw collateral from Morpho Blue.
903
+ *
904
+ * AgentAccount.execute: Morpho.withdrawCollateral(params, amount, account, receiver)
905
+ *
906
+ * @param receiver - defaults to EOA wallet
907
+ */
908
+ async withdrawCollateral(tokenSymbol, amount, marketParams, receiver) {
909
+ const acctAddr = await this.getAccountAddress();
910
+ const colInfo = BASE_COLLATERALS[tokenSymbol];
911
+ if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
912
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
913
+ const morphoAddr = this.config.contracts.morphoBlue;
914
+ const dest = receiver || this.wallet.address;
915
+ let weiAmount;
916
+ if (amount === "all") {
917
+ const markets = await this.getMarkets();
918
+ const market = markets.find(
919
+ (m) => m.collateralAsset.address.toLowerCase() === colInfo.address.toLowerCase()
920
+ );
921
+ if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
922
+ const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
923
+ weiAmount = pos.collateral;
924
+ if (weiAmount === 0n) throw new AgetherError("No collateral to withdraw", "NO_COLLATERAL");
925
+ } else {
926
+ weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
927
+ }
928
+ const data = morphoIface.encodeFunctionData("withdrawCollateral", [
929
+ this._toTuple(params),
930
+ weiAmount,
931
+ acctAddr,
932
+ dest
933
+ ]);
934
+ const receipt = await this.exec(morphoAddr, data);
935
+ let remainingCollateral = "0";
936
+ try {
937
+ const markets = await this.getMarkets();
938
+ const market = markets.find(
939
+ (m) => m.collateralAsset.address.toLowerCase() === colInfo.address.toLowerCase()
940
+ );
941
+ if (market) {
942
+ const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
943
+ remainingCollateral = import_ethers2.ethers.formatUnits(pos.collateral, colInfo.decimals);
944
+ }
945
+ } catch {
946
+ }
947
+ return {
948
+ tx: receipt.hash,
949
+ token: tokenSymbol,
950
+ amount: amount === "all" ? import_ethers2.ethers.formatUnits(weiAmount, colInfo.decimals) : amount,
951
+ remainingCollateral,
952
+ destination: dest
953
+ };
820
954
  }
821
955
  /**
822
- * Get current APY estimate
956
+ * Sponsor: transfer collateral to another agent's AgentAccount.
957
+ * (The agent must then supplyCollateral themselves via their own account.)
823
958
  */
824
- async estimateAPY() {
825
- const stats = await this.getStats();
826
- const avgAPR = 1200;
827
- const protocolFee = 1e3;
828
- const lpYield = avgAPR * (1e4 - protocolFee) / 1e4;
829
- const effectiveYield = lpYield * stats.utilizationRate / 100;
830
- return effectiveYield / 100;
831
- }
832
- // ============ Utility ============
833
- async getAsset() {
834
- if (!this.asset) {
835
- const assetAddress = await this.vault.asset();
836
- this.asset = new import_ethers2.Contract(assetAddress, ERC20_ABI, this.signer);
959
+ async sponsor(target, tokenSymbol, amount) {
960
+ const colInfo = BASE_COLLATERALS[tokenSymbol];
961
+ if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
962
+ let targetAddr;
963
+ if (target.address) {
964
+ targetAddr = target.address;
965
+ } else if (target.agentId) {
966
+ targetAddr = await this.accountFactory.getAccount(BigInt(target.agentId));
967
+ if (targetAddr === import_ethers2.ethers.ZeroAddress) throw new AgetherError("Target agent has no account", "NO_ACCOUNT");
968
+ } else {
969
+ throw new AgetherError("Provide agentId or address", "INVALID_TARGET");
970
+ }
971
+ const weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
972
+ const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this.wallet);
973
+ const tx = await colToken.transfer(targetAddr, weiAmount);
974
+ const receipt = await tx.wait();
975
+ return { tx: receipt.hash, targetAccount: targetAddr, targetAgentId: target.agentId };
976
+ }
977
+ // ════════════════════════════════════════════════════════
978
+ // Reputation (AgentReputation contract)
979
+ // ════════════════════════════════════════════════════════
980
+ async getCreditScore() {
981
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
982
+ return this.agentReputation.getCreditScore(BigInt(this.agentId));
983
+ }
984
+ async getAttestation() {
985
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
986
+ const att = await this.agentReputation.getAttestation(BigInt(this.agentId));
987
+ return { score: att.score, timestamp: att.timestamp, signer: att.signer };
988
+ }
989
+ async isEligible(minScore = 500n) {
990
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
991
+ const [eligible, currentScore] = await this.agentReputation.isEligible(BigInt(this.agentId), minScore);
992
+ return { eligible, currentScore };
993
+ }
994
+ async isScoreFresh() {
995
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
996
+ const [fresh, age] = await this.agentReputation.isScoreFresh(BigInt(this.agentId));
997
+ return { fresh, age };
998
+ }
999
+ // ════════════════════════════════════════════════════════
1000
+ // Internal Helpers
1001
+ // ════════════════════════════════════════════════════════
1002
+ /**
1003
+ * Execute a single call via AgentAccount.execute.
1004
+ */
1005
+ async exec(target, data, value = 0n) {
1006
+ const acctAddr = await this.getAccountAddress();
1007
+ const account = new import_ethers2.Contract(acctAddr, AGENT_ACCOUNT_ABI, this.wallet);
1008
+ let gasLimit;
1009
+ try {
1010
+ const estimate = await account.execute.estimateGas(target, value, data);
1011
+ gasLimit = estimate * 130n / 100n;
1012
+ } catch {
1013
+ gasLimit = 500000n;
837
1014
  }
838
- return this.asset;
1015
+ const tx = await account.execute(target, value, data, { gasLimit });
1016
+ return tx.wait();
839
1017
  }
840
1018
  /**
841
- * Get underlying asset address
1019
+ * Execute multiple calls via AgentAccount.executeBatch.
842
1020
  */
843
- async getAssetAddress() {
844
- return await this.vault.asset();
845
- }
846
- /**
847
- * Get vault share token address
848
- */
849
- getVaultAddress() {
850
- return this.config.contracts.lpVault;
851
- }
852
- };
853
-
854
- // src/clients/AgentIdentityClient.ts
855
- var import_ethers3 = require("ethers");
856
- var ERC8004_IDENTITY_ABI = [
857
- // Registration
858
- "function register() returns (uint256)",
859
- "function register(string agentURI) returns (uint256)",
860
- "function register(string agentURI, tuple(string key, bytes value)[] metadata) returns (uint256)",
861
- // Management
862
- "function setAgentURI(uint256 agentId, string newURI)",
863
- "function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)",
864
- "function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)",
865
- // ERC-721 standard
866
- "function ownerOf(uint256 tokenId) view returns (address)",
867
- "function balanceOf(address owner) view returns (uint256)",
868
- "function tokenURI(uint256 tokenId) view returns (string)",
869
- "function transferFrom(address from, address to, uint256 tokenId)",
870
- "function approve(address to, uint256 tokenId)",
871
- "function getApproved(uint256 tokenId) view returns (address)",
872
- // Events
873
- "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
874
- ];
875
- var ERC8004_REPUTATION_ABI = [
876
- // Feedback
877
- "function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash)",
878
- "function revokeFeedback(uint256 agentId, uint64 feedbackIndex)",
879
- // Queries
880
- "function readFeedback(uint256 agentId, address clientAddress, uint64 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
881
- "function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint64 count, int128 summaryValue, uint8 summaryValueDecimals)",
882
- "function getClients(uint256 agentId) view returns (address[])",
883
- "function getLastIndex(uint256 agentId, address clientAddress) view returns (uint64)",
884
- // Registry
885
- "function getIdentityRegistry() view returns (address)"
886
- ];
887
- var ERC8004_SEPOLIA = {
888
- identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
889
- reputationRegistry: "0x8004B663056A597Dffe9eCcC1965A193B7388713"
890
- };
891
- var AgentIdentityClient = class {
892
- constructor(options) {
893
- this.config = options.config;
894
- this.signer = options.signer;
895
- const identityAddr = options.config.contracts?.identityRegistry || ERC8004_SEPOLIA.identityRegistry;
896
- const reputationAddr = options.config.contracts?.reputationRegistry || ERC8004_SEPOLIA.reputationRegistry;
897
- this.identityRegistry = new import_ethers3.ethers.Contract(identityAddr, ERC8004_IDENTITY_ABI, this.signer);
898
- this.reputationRegistry = new import_ethers3.ethers.Contract(reputationAddr, ERC8004_REPUTATION_ABI, this.signer);
899
- }
900
- // ============ Identity Functions ============
901
- /**
902
- * Register a new agent (minimal - no metadata)
903
- */
904
- async register() {
905
- const tx = await this.identityRegistry["register()"]();
906
- const receipt = await tx.wait();
907
- const agentId = this.parseAgentIdFromReceipt(receipt);
908
- return { agentId, txHash: receipt.hash };
909
- }
910
- /**
911
- * Register agent with IPFS/HTTP URI to metadata JSON
912
- * @param agentURI URI pointing to agent metadata (ipfs:// or https://)
913
- */
914
- async registerWithURI(agentURI) {
915
- const tx = await this.identityRegistry["register(string)"](agentURI);
916
- const receipt = await tx.wait();
917
- const agentId = this.parseAgentIdFromReceipt(receipt);
918
- return { agentId, txHash: receipt.hash };
919
- }
920
- /**
921
- * Check if the signer already owns an ERC-8004 identity token.
922
- * Returns true if balanceOf > 0, false otherwise.
923
- * Note: Cannot determine the specific agentId without enumeration —
924
- * use agether init <pk> --agent-id <id> if you know your agentId.
925
- */
926
- async hasExistingIdentity() {
927
- try {
928
- const address = await this.signer.getAddress();
929
- const balance = await this.identityRegistry.balanceOf(address);
930
- return balance > 0n;
931
- } catch {
932
- return false;
933
- }
934
- }
935
- /**
936
- * Register only if no identity exists; otherwise throw.
937
- * Prevents accidental double-registration.
938
- */
939
- async registerOrGet() {
940
- const hasIdentity = await this.hasExistingIdentity();
941
- if (hasIdentity) {
942
- throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
943
- }
944
- const result = await this.register();
945
- return { ...result, existing: false };
946
- }
947
- /**
948
- * Register with URI only if no identity exists; otherwise throw.
949
- * Prevents accidental double-registration.
950
- */
951
- async registerOrGetWithURI(agentURI) {
952
- const hasIdentity = await this.hasExistingIdentity();
953
- if (hasIdentity) {
954
- throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
955
- }
956
- const result = await this.registerWithURI(agentURI);
957
- return { ...result, existing: false };
958
- }
959
- /**
960
- * Register agent with URI and on-chain metadata
961
- */
962
- async registerWithMetadata(agentURI, metadata) {
963
- const metadataEntries = metadata.map((m) => ({
964
- key: m.key,
965
- value: import_ethers3.ethers.toUtf8Bytes(m.value)
966
- }));
967
- const tx = await this.identityRegistry["register(string,tuple(string,bytes)[])"](
968
- agentURI,
969
- metadataEntries
970
- );
971
- const receipt = await tx.wait();
972
- const agentId = this.parseAgentIdFromReceipt(receipt);
973
- return { agentId, txHash: receipt.hash };
974
- }
975
- /**
976
- * Get agent owner address
977
- */
978
- async getOwner(agentId) {
979
- return await this.identityRegistry.ownerOf(agentId);
980
- }
981
- /**
982
- * Get agent URI (metadata JSON location)
983
- */
984
- async getAgentURI(agentId) {
985
- return await this.identityRegistry.tokenURI(agentId);
986
- }
987
- /**
988
- * Update agent URI
989
- */
990
- async setAgentURI(agentId, newURI) {
991
- const tx = await this.identityRegistry.setAgentURI(agentId, newURI);
992
- const receipt = await tx.wait();
993
- return receipt.hash;
994
- }
995
- /**
996
- * Set on-chain metadata (key-value)
997
- */
998
- async setMetadata(agentId, key, value) {
999
- const tx = await this.identityRegistry.setMetadata(
1000
- agentId,
1001
- key,
1002
- import_ethers3.ethers.toUtf8Bytes(value)
1003
- );
1004
- const receipt = await tx.wait();
1005
- return receipt.hash;
1006
- }
1007
- /**
1008
- * Get on-chain metadata
1009
- */
1010
- async getMetadata(agentId, key) {
1011
- const value = await this.identityRegistry.getMetadata(agentId, key);
1012
- return import_ethers3.ethers.toUtf8String(value);
1013
- }
1014
- /**
1015
- * Transfer agent to new owner
1016
- */
1017
- async transfer(agentId, to) {
1018
- const from = await this.signer.getAddress();
1019
- const tx = await this.identityRegistry.transferFrom(from, to, agentId);
1020
- const receipt = await tx.wait();
1021
- return receipt.hash;
1022
- }
1023
- /**
1024
- * Fetch and parse agent metadata from URI
1025
- */
1026
- async fetchAgentMetadata(agentId) {
1027
- try {
1028
- const uri = await this.getAgentURI(agentId);
1029
- let fetchUrl = uri;
1030
- if (uri.startsWith("ipfs://")) {
1031
- const cid = uri.replace("ipfs://", "");
1032
- fetchUrl = `https://ipfs.io/ipfs/${cid}`;
1033
- }
1034
- const response = await fetch(fetchUrl);
1035
- if (!response.ok) return null;
1036
- return await response.json();
1037
- } catch {
1038
- return null;
1039
- }
1040
- }
1041
- // ============ Reputation Functions ============
1042
- /**
1043
- * Give feedback to an agent
1044
- */
1045
- async giveFeedback(input) {
1046
- const feedbackHash = import_ethers3.ethers.keccak256(
1047
- import_ethers3.ethers.AbiCoder.defaultAbiCoder().encode(
1048
- ["uint256", "int128", "uint256"],
1049
- [input.agentId, input.value, Date.now()]
1050
- )
1051
- );
1052
- const tx = await this.reputationRegistry.giveFeedback(
1053
- input.agentId,
1054
- input.value,
1055
- input.decimals || 0,
1056
- input.tag1 || "",
1057
- input.tag2 || "",
1058
- input.endpoint || "",
1059
- input.feedbackURI || "",
1060
- feedbackHash
1061
- );
1062
- const receipt = await tx.wait();
1063
- return receipt.hash;
1064
- }
1065
- /**
1066
- * Give positive feedback (shorthand)
1067
- */
1068
- async givePosisitiveFeedback(agentId, value = 100, tags = {}) {
1069
- return this.giveFeedback({
1070
- agentId,
1071
- value: Math.abs(value),
1072
- ...tags
1073
- });
1074
- }
1075
- /**
1076
- * Give negative feedback (e.g., for defaults)
1077
- */
1078
- async giveNegativeFeedback(agentId, value = -100, tags = {}) {
1079
- return this.giveFeedback({
1080
- agentId,
1081
- value: -Math.abs(value),
1082
- ...tags
1083
- });
1084
- }
1085
- /**
1086
- * Record credit default in reputation system
1087
- */
1088
- async recordCreditDefault(agentId, creditLineId) {
1089
- return this.giveFeedback({
1090
- agentId,
1091
- value: -100,
1092
- tag1: "credit",
1093
- tag2: "default",
1094
- feedbackURI: `agether://default/${creditLineId}`
1095
- });
1096
- }
1097
- /**
1098
- * Get reputation summary for an agent
1099
- */
1100
- async getReputation(agentId, tag1 = "", tag2 = "") {
1101
- const clients = await this.reputationRegistry.getClients(agentId);
1102
- if (clients.length === 0) {
1103
- return {
1104
- count: 0,
1105
- totalValue: 0,
1106
- averageValue: 0,
1107
- clients: []
1108
- };
1109
- }
1110
- const [count, summaryValue, decimals] = await this.reputationRegistry.getSummary(
1111
- agentId,
1112
- clients,
1113
- tag1,
1114
- tag2
1115
- );
1116
- const divisor = 10 ** Number(decimals);
1117
- const total = Number(summaryValue) / divisor;
1118
- return {
1119
- count: Number(count),
1120
- totalValue: total,
1121
- averageValue: Number(count) > 0 ? total / Number(count) : 0,
1122
- clients: clients.map((c) => c)
1123
- };
1124
- }
1125
- /**
1126
- * Check if agent has negative credit reputation
1127
- */
1128
- async hasNegativeCreditReputation(agentId) {
1129
- const rep = await this.getReputation(agentId, "credit", "");
1130
- return rep.count > 0 && rep.averageValue < 0;
1131
- }
1132
- // ============ Agether Integration ============
1133
- /**
1134
- * Verify agent is eligible for Agether credit
1135
- * Checks:
1136
- * 1. Agent exists in ERC-8004 registry
1137
- * 2. Agent has no negative credit reputation
1138
- */
1139
- async verifyForCredit(agentId) {
1140
- let owner;
1021
+ async batch(targets, values, datas) {
1022
+ const acctAddr = await this.getAccountAddress();
1023
+ const account = new import_ethers2.Contract(acctAddr, AGENT_ACCOUNT_ABI, this.wallet);
1024
+ let gasLimit;
1141
1025
  try {
1142
- owner = await this.getOwner(agentId);
1026
+ const estimate = await account.executeBatch.estimateGas(targets, values, datas);
1027
+ gasLimit = estimate * 130n / 100n;
1143
1028
  } catch {
1144
- return { eligible: false, reason: "Agent not found in ERC-8004 registry" };
1145
- }
1146
- const reputation = await this.getReputation(agentId, "credit", "");
1147
- if (reputation.count > 0 && reputation.averageValue < 0) {
1148
- return {
1149
- eligible: false,
1150
- reason: "Agent has negative credit reputation",
1151
- owner,
1152
- reputation
1153
- };
1029
+ gasLimit = 800000n;
1154
1030
  }
1155
- return {
1156
- eligible: true,
1157
- owner,
1158
- reputation
1159
- };
1160
- }
1161
- // ============ Helpers ============
1162
- parseAgentIdFromReceipt(receipt) {
1163
- for (const log of receipt.logs) {
1031
+ const tx = await account.executeBatch(targets, values, datas, { gasLimit });
1032
+ return tx.wait();
1033
+ }
1034
+ /** Convert MorphoMarketParams to Solidity tuple. */
1035
+ _toTuple(p) {
1036
+ return [p.loanToken, p.collateralToken, p.oracle, p.irm, p.lltv];
1037
+ }
1038
+ /** Find the first market where the agent has collateral deposited. */
1039
+ async _findActiveMarket() {
1040
+ const acctAddr = await this.getAccountAddress();
1041
+ const markets = await this.getMarkets();
1042
+ for (const m of markets) {
1043
+ if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
1164
1044
  try {
1165
- const parsed = this.identityRegistry.interface.parseLog({
1166
- topics: log.topics,
1167
- data: log.data
1168
- });
1169
- if (parsed?.name === "Transfer") {
1170
- return parsed.args[2];
1045
+ const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
1046
+ if (pos.collateral > 0n) {
1047
+ return {
1048
+ params: {
1049
+ loanToken: m.loanAsset.address,
1050
+ collateralToken: m.collateralAsset.address,
1051
+ oracle: m.oracle,
1052
+ irm: m.irm,
1053
+ lltv: m.lltv
1054
+ },
1055
+ symbol: m.collateralAsset.symbol
1056
+ };
1171
1057
  }
1172
1058
  } catch {
1173
1059
  continue;
1174
1060
  }
1175
1061
  }
1176
- return 0n;
1177
- }
1178
- /**
1179
- * Get contract addresses
1180
- */
1181
- getContractAddresses() {
1182
- return {
1183
- identity: this.identityRegistry.target,
1184
- reputation: this.reputationRegistry.target
1185
- };
1062
+ const params = await this.findMarketForCollateral("WETH");
1063
+ return { params, symbol: "WETH" };
1186
1064
  }
1187
1065
  };
1188
1066
 
1067
+ // src/clients/ScoringClient.ts
1068
+ var import_axios2 = __toESM(require("axios"));
1069
+
1189
1070
  // src/clients/X402Client.ts
1190
- var import_ethers4 = require("ethers");
1071
+ var import_ethers3 = require("ethers");
1191
1072
  var USDC_DOMAINS = {
1192
1073
  "eip155:1": { name: "USD Coin", version: "2", address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
1193
1074
  "eip155:8453": { name: "USD Coin", version: "2", address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
@@ -1199,11 +1080,11 @@ function chainIdFromNetwork(network) {
1199
1080
  const m = network.match(/^eip155:(\d+)$/);
1200
1081
  return m ? Number(m[1]) : 1;
1201
1082
  }
1202
- var _X402Client = class _X402Client {
1083
+ var X402Client = class {
1203
1084
  constructor(config) {
1204
1085
  this.config = config;
1205
- const provider = new import_ethers4.ethers.JsonRpcProvider(config.rpcUrl);
1206
- this.wallet = new import_ethers4.ethers.Wallet(config.privateKey, provider);
1086
+ const provider = new import_ethers3.ethers.JsonRpcProvider(config.rpcUrl);
1087
+ this.wallet = new import_ethers3.ethers.Wallet(config.privateKey, provider);
1207
1088
  }
1208
1089
  async get(url, opts) {
1209
1090
  return this.request(url, { ...opts, method: "GET" });
@@ -1248,9 +1129,6 @@ var _X402Client = class _X402Client {
1248
1129
  console.log(` amount : ${requirements.amount} (atomic)`);
1249
1130
  console.log(` asset : ${requirements.asset}`);
1250
1131
  console.log(` payTo : ${requirements.payTo}`);
1251
- if (this.config.autoDraw && this.config.accountAddress && this.config.morphoCreditAddress) {
1252
- await this.ensureBalance(BigInt(requirements.amount));
1253
- }
1254
1132
  console.log(" [3/4] Signing EIP-3009 transferWithAuthorization\u2026");
1255
1133
  const paymentPayload = await this.buildPaymentPayload(requirements, resource, url);
1256
1134
  const paymentB64 = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
@@ -1346,7 +1224,7 @@ var _X402Client = class _X402Client {
1346
1224
  const now = Math.floor(Date.now() / 1e3);
1347
1225
  const validAfter = String(now - 60);
1348
1226
  const validBefore = String(now + (reqs.maxTimeoutSeconds || 300));
1349
- const nonce = import_ethers4.ethers.hexlify(import_ethers4.ethers.randomBytes(32));
1227
+ const nonce = import_ethers3.ethers.hexlify(import_ethers3.ethers.randomBytes(32));
1350
1228
  const chainId = chainIdFromNetwork(reqs.network);
1351
1229
  const usdcDomain = USDC_DOMAINS[reqs.network] || USDC_DOMAINS["eip155:1"];
1352
1230
  const payerAddress = this.config.accountAddress || this.wallet.address;
@@ -1411,43 +1289,6 @@ var _X402Client = class _X402Client {
1411
1289
  }
1412
1290
  };
1413
1291
  }
1414
- async ensureBalance(requiredAmount) {
1415
- const accountAddr = this.config.accountAddress;
1416
- const morphoAddr = this.config.morphoCreditAddress;
1417
- const provider = this.wallet.provider;
1418
- const usdcAddr = USDC_DOMAINS["eip155:8453"]?.address || USDC_DOMAINS["eip155:1"].address;
1419
- const usdc = new import_ethers4.ethers.Contract(usdcAddr, _X402Client.ERC20_BALANCE_ABI, provider);
1420
- const balance = await usdc.balanceOf(accountAddr);
1421
- const needed = requiredAmount + requiredAmount / 10n;
1422
- if (balance >= needed) return;
1423
- const deficit = needed - balance;
1424
- const minDraw = import_ethers4.ethers.parseUnits("10", 6);
1425
- const drawAmount = deficit > minDraw ? deficit : minDraw;
1426
- console.log(` [auto-draw] USDC balance $${import_ethers4.ethers.formatUnits(balance, 6)} < needed $${import_ethers4.ethers.formatUnits(needed, 6)}`);
1427
- const morpho = new import_ethers4.ethers.Contract(morphoAddr, _X402Client.MORPHO_DRAW_ABI, provider);
1428
- let collateralAddr = null;
1429
- for (const addr of _X402Client.AUTO_DRAW_COLLATERALS) {
1430
- try {
1431
- const pos = await morpho.getPosition(accountAddr, addr);
1432
- if (pos.collateralAmount > 0n) {
1433
- collateralAddr = addr;
1434
- break;
1435
- }
1436
- } catch {
1437
- continue;
1438
- }
1439
- }
1440
- if (!collateralAddr) {
1441
- throw new Error("autoDraw failed: no collateral deposited in Morpho. Deposit collateral first.");
1442
- }
1443
- const account = new import_ethers4.ethers.Contract(accountAddr, _X402Client.AGENT_ACCOUNT_EXEC_ABI, this.wallet);
1444
- const morphoIface = new import_ethers4.ethers.Interface(_X402Client.MORPHO_DRAW_ABI);
1445
- const calldata = morphoIface.encodeFunctionData("drawWithCollateral", [collateralAddr, drawAmount]);
1446
- console.log(` [auto-draw] Borrowing $${import_ethers4.ethers.formatUnits(drawAmount, 6)} from Morpho...`);
1447
- const tx = await account.execute(morphoAddr, 0, calldata);
1448
- await tx.wait();
1449
- console.log(` [auto-draw] \u2713 Borrowed (tx: ${tx.hash.slice(0, 14)}\u2026)`);
1450
- }
1451
1292
  // ──────────── Risk check via our backend ────────────
1452
1293
  async riskCheck(paymentPayload, reqs) {
1453
1294
  try {
@@ -1479,787 +1320,429 @@ var _X402Client = class _X402Client {
1479
1320
  }
1480
1321
  }
1481
1322
  };
1482
- // ──────────── Auto-draw (Flow 9) ────────────
1483
- //
1484
- // When autoDraw is enabled and the AgentAccount has insufficient USDC,
1485
- // automatically borrow from Morpho credit line to cover the payment.
1486
- _X402Client.MORPHO_DRAW_ABI = [
1487
- "function drawWithCollateral(address collateralToken, uint256 amount)",
1488
- "function getPosition(address account, address collateralToken) view returns (tuple(uint256 collateralAmount, uint256 borrowedAmount, uint256 borrowShares, bool isActive))"
1489
- ];
1490
- _X402Client.AGENT_ACCOUNT_EXEC_ABI = [
1491
- "function execute(address target, uint256 value, bytes data) payable returns (bytes)"
1492
- ];
1493
- _X402Client.ERC20_BALANCE_ABI = [
1494
- "function balanceOf(address) view returns (uint256)"
1495
- ];
1496
- _X402Client.AUTO_DRAW_COLLATERALS = [
1497
- "0x4200000000000000000000000000000000000006",
1498
- // WETH
1499
- "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
1500
- // wstETH
1501
- "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22"
1502
- // cbETH
1503
- ];
1504
- var X402Client = _X402Client;
1505
1323
 
1506
- // src/clients/MorphoCreditClient.ts
1507
- var import_ethers5 = require("ethers");
1508
- var BASE_COLLATERALS = {
1509
- WETH: { symbol: "WETH", address: "0x4200000000000000000000000000000000000006", decimals: 18 },
1510
- wstETH: { symbol: "wstETH", address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", decimals: 18 },
1511
- cbETH: { symbol: "cbETH", address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", decimals: 18 }
1512
- };
1513
- var _MorphoCreditClient = class _MorphoCreditClient {
1514
- constructor(config, collaterals) {
1515
- // ── Batch helper ──
1516
- this.morphoIface = new import_ethers5.ethers.Interface(MORPHO_CREDIT_ABI);
1517
- this.erc20Iface = new import_ethers5.ethers.Interface(ERC20_ABI);
1518
- this.config = config;
1519
- const provider = new import_ethers5.ethers.JsonRpcProvider(config.rpcUrl);
1520
- this.signer = new import_ethers5.ethers.Wallet(config.privateKey, provider);
1521
- this.morpho = new import_ethers5.Contract(config.contracts.morphoCredit, MORPHO_CREDIT_ABI, this.signer);
1522
- this.factory = new import_ethers5.Contract(config.contracts.accountFactory, ACCOUNT_FACTORY_ABI, this.signer);
1523
- this.collaterals = collaterals || BASE_COLLATERALS;
1524
- if (config.agentId) this.resolvedAgentId = config.agentId.toString();
1525
- }
1526
- // ── Helpers ──
1527
- resolveToken(tokenSymbol) {
1528
- const token = this.collaterals[tokenSymbol];
1529
- if (!token) {
1530
- throw new Error(`Unsupported collateral token: ${tokenSymbol}. Supported: ${Object.keys(this.collaterals).join(", ")}`);
1324
+ // src/clients/ScoringClient.ts
1325
+ var ScoringClient = class {
1326
+ constructor(config) {
1327
+ this.endpoint = config.endpoint;
1328
+ this.client = import_axios2.default.create({
1329
+ baseURL: config.endpoint,
1330
+ headers: { "Content-Type": "application/json" },
1331
+ timeout: 3e4
1332
+ });
1333
+ if (config.x402) {
1334
+ this.x402Client = new X402Client(config.x402);
1531
1335
  }
1532
- return token;
1533
1336
  }
1337
+ // ════════════════════════════════════════════════════════
1338
+ // Score (x402-gated — computes & submits on-chain)
1339
+ // ════════════════════════════════════════════════════════
1534
1340
  /**
1535
- * Get agentId from config or cache. Call register() first if not set.
1341
+ * Request a fresh score computation.
1342
+ *
1343
+ * This is x402-gated: the backend returns 402, the X402Client
1344
+ * signs an EIP-3009 payment, and the backend computes + submits
1345
+ * the score on-chain via AgentReputation.submitScore().
1346
+ *
1347
+ * Returns the ScoreResult with breakdown and txHash.
1536
1348
  */
1537
- async getAgentId() {
1538
- if (this.resolvedAgentId) return this.resolvedAgentId;
1539
- throw new Error("No agentId. Pass agentId in config or call register() first.");
1349
+ async requestScore(agentId) {
1350
+ const id = agentId.toString();
1351
+ if (!this.x402Client) {
1352
+ throw new AgetherError(
1353
+ "x402 config required for paid scoring. Provide x402 in ScoringClientConfig.",
1354
+ "X402_NOT_CONFIGURED"
1355
+ );
1356
+ }
1357
+ const result = await this.x402Client.get(
1358
+ `${this.endpoint}/score/${id}`
1359
+ );
1360
+ if (!result.success || !result.data) {
1361
+ throw new AgetherError(
1362
+ `Scoring request failed: ${result.error || "unknown error"}`,
1363
+ "SCORING_FAILED"
1364
+ );
1365
+ }
1366
+ return result.data;
1540
1367
  }
1368
+ // ════════════════════════════════════════════════════════
1369
+ // Current Score (free — reads on-chain)
1370
+ // ════════════════════════════════════════════════════════
1541
1371
  /**
1542
- * Get the AgentAccount address for the configured agentId.
1372
+ * Get the current on-chain score (free, no payment required).
1543
1373
  */
1544
- async getAccountAddress() {
1545
- if (this.accountAddress) return this.accountAddress;
1546
- const agentId = await this.getAgentId();
1547
- const addr = await this.factory.getAccount(agentId);
1548
- if (addr === import_ethers5.ethers.ZeroAddress) {
1549
- throw new Error("No AgentAccount found. Register first.");
1550
- }
1551
- this.accountAddress = addr;
1552
- return addr;
1374
+ async getCurrentScore(agentId) {
1375
+ const response = await this.client.get(`/score/${agentId.toString()}/current`);
1376
+ return response.data;
1553
1377
  }
1378
+ // ════════════════════════════════════════════════════════
1379
+ // Agent Details
1380
+ // ════════════════════════════════════════════════════════
1554
1381
  /**
1555
- * Get the AgentAccount address for any agentId.
1382
+ * Get detailed agent info from backend.
1556
1383
  */
1557
- async getAccountForAgent(agentId) {
1558
- const addr = await this.factory.getAccount(agentId.toString());
1559
- if (addr === import_ethers5.ethers.ZeroAddress) {
1560
- throw new Error(`No AgentAccount for agent #${agentId}`);
1561
- }
1562
- return addr;
1384
+ async getAgentDetails(agentId) {
1385
+ const response = await this.client.get(`/agents/${agentId.toString()}/details`);
1386
+ return response.data;
1563
1387
  }
1564
- async ensureCreditProvider(accountAddr) {
1565
- const account = new import_ethers5.Contract(accountAddr, AGENT_ACCOUNT_ABI, this.signer);
1566
- try {
1567
- const tx = await account.approveCreditProvider(this.config.contracts.morphoCredit);
1568
- await tx.wait();
1569
- } catch {
1570
- }
1388
+ // ════════════════════════════════════════════════════════
1389
+ // Service Status
1390
+ // ════════════════════════════════════════════════════════
1391
+ /**
1392
+ * Health check.
1393
+ */
1394
+ async getHealth() {
1395
+ const response = await this.client.get("/health");
1396
+ return response.data;
1571
1397
  }
1572
1398
  /**
1573
- * Approve ERC-20 token from EOA → MorphoCredit, then deposit for account.
1574
- * This is 2 EOA txs (approve + depositCollateralFor) — cannot be batched
1575
- * because both are called from EOA, not from AgentAccount.
1399
+ * Detailed status (contracts, signer, chain).
1576
1400
  */
1577
- async approveAndDeposit(accountAddr, tokenInfo, amount) {
1578
- const token = new import_ethers5.Contract(tokenInfo.address, ERC20_ABI, this.signer);
1579
- const balance = await token.balanceOf(this.signer.address);
1580
- if (balance < amount) {
1581
- throw new Error(
1582
- `Insufficient ${tokenInfo.symbol}: have ${import_ethers5.ethers.formatUnits(balance, tokenInfo.decimals)}, need ${import_ethers5.ethers.formatUnits(amount, tokenInfo.decimals)}`
1583
- );
1584
- }
1585
- const approveTx = await token.approve(this.config.contracts.morphoCredit, amount);
1586
- await approveTx.wait();
1587
- const depositTx = await this.morpho.depositCollateralFor(accountAddr, tokenInfo.address, amount);
1588
- await depositTx.wait();
1589
- return depositTx.hash;
1401
+ async getStatus() {
1402
+ const response = await this.client.get("/status");
1403
+ return response.data;
1590
1404
  }
1591
1405
  /**
1592
- * Execute multiple calls via AgentAccount.executeBatch() in a single tx.
1593
- * Each call is { target, value, data }.
1406
+ * Agent count and list.
1594
1407
  */
1595
- async batch(accountAddr, calls) {
1596
- const account = new import_ethers5.Contract(accountAddr, AGENT_ACCOUNT_ABI, this.signer);
1597
- const tx = await account.executeBatch(
1598
- calls.map((c) => c.target),
1599
- calls.map((c) => c.value),
1600
- calls.map((c) => c.data)
1601
- );
1602
- await tx.wait();
1603
- return tx.hash;
1408
+ async getAgentCount() {
1409
+ const response = await this.client.get("/agents/count");
1410
+ return response.data;
1411
+ }
1412
+ };
1413
+
1414
+ // src/clients/AgentIdentityClient.ts
1415
+ var import_ethers4 = require("ethers");
1416
+ var ERC8004_IDENTITY_ABI = [
1417
+ // Registration
1418
+ "function register() returns (uint256)",
1419
+ "function register(string agentURI) returns (uint256)",
1420
+ "function register(string agentURI, tuple(string key, bytes value)[] metadata) returns (uint256)",
1421
+ // Management
1422
+ "function setAgentURI(uint256 agentId, string newURI)",
1423
+ "function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)",
1424
+ "function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)",
1425
+ // ERC-721 standard
1426
+ "function ownerOf(uint256 tokenId) view returns (address)",
1427
+ "function balanceOf(address owner) view returns (uint256)",
1428
+ "function tokenURI(uint256 tokenId) view returns (string)",
1429
+ "function transferFrom(address from, address to, uint256 tokenId)",
1430
+ "function approve(address to, uint256 tokenId)",
1431
+ "function getApproved(uint256 tokenId) view returns (address)",
1432
+ // Events
1433
+ "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
1434
+ ];
1435
+ var ERC8004_REPUTATION_ABI = [
1436
+ // Feedback
1437
+ "function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash)",
1438
+ "function revokeFeedback(uint256 agentId, uint64 feedbackIndex)",
1439
+ // Queries
1440
+ "function readFeedback(uint256 agentId, address clientAddress, uint64 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
1441
+ "function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint64 count, int128 summaryValue, uint8 summaryValueDecimals)",
1442
+ "function getClients(uint256 agentId) view returns (address[])",
1443
+ "function getLastIndex(uint256 agentId, address clientAddress) view returns (uint64)",
1444
+ // Registry
1445
+ "function getIdentityRegistry() view returns (address)"
1446
+ ];
1447
+ var ERC8004_SEPOLIA = {
1448
+ identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
1449
+ reputationRegistry: "0x8004B663056A597Dffe9eCcC1965A193B7388713"
1450
+ };
1451
+ var AgentIdentityClient = class {
1452
+ constructor(options) {
1453
+ this.config = options.config;
1454
+ this.signer = options.signer;
1455
+ const identityAddr = options.config.contracts?.identityRegistry || ERC8004_SEPOLIA.identityRegistry;
1456
+ const reputationAddr = options.config.contracts?.reputationRegistry || ERC8004_SEPOLIA.reputationRegistry;
1457
+ this.identityRegistry = new import_ethers4.ethers.Contract(identityAddr, ERC8004_IDENTITY_ABI, this.signer);
1458
+ this.reputationRegistry = new import_ethers4.ethers.Contract(reputationAddr, ERC8004_REPUTATION_ABI, this.signer);
1604
1459
  }
1605
- // ══════════════════════════════════════════
1606
- // Register
1607
- // ══════════════════════════════════════════
1460
+ // ============ Identity Functions ============
1608
1461
  /**
1609
- * Register a new ERC-8004 agent identity and create an AgentAccount.
1610
- * If already registered (agentId in config or on-chain), returns existing info.
1611
- *
1612
- * @param name - Agent display name
1462
+ * Register a new agent (minimal - no metadata)
1613
1463
  */
1614
- async register(name) {
1615
- if (!this.config.contracts.agentRegistry) {
1616
- throw new Error("agentRegistry address required for register()");
1617
- }
1618
- const registry = new import_ethers5.Contract(
1619
- this.config.contracts.agentRegistry,
1620
- _MorphoCreditClient.ERC8004_ABI,
1621
- this.signer
1622
- );
1623
- if (this.resolvedAgentId) {
1624
- const agentId2 = BigInt(this.resolvedAgentId);
1625
- const owner = await registry.ownerOf(agentId2);
1626
- if (owner.toLowerCase() !== this.signer.address.toLowerCase()) {
1627
- throw new Error("agentId in config does not belong to this wallet");
1628
- }
1629
- const exists2 = await this.factory.accountExists(agentId2);
1630
- if (!exists2) {
1631
- const tx2 = await this.factory.createAccount(agentId2);
1632
- await tx2.wait();
1633
- }
1634
- const accountAddr2 = await this.factory.getAccount(this.resolvedAgentId);
1635
- return {
1636
- tx: "",
1637
- agentId: this.resolvedAgentId,
1638
- address: this.signer.address,
1639
- agentAccount: accountAddr2,
1640
- alreadyRegistered: true
1641
- };
1642
- }
1643
- const registrationFile = JSON.stringify({
1644
- type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
1645
- name,
1646
- description: "AI agent registered via @agether/sdk",
1647
- active: true,
1648
- registrations: [{
1649
- agentId: 0,
1650
- agentRegistry: `eip155:8453:${this.config.contracts.agentRegistry}`
1651
- }]
1652
- });
1653
- const agentURI = `data:application/json;base64,${Buffer.from(registrationFile).toString("base64")}`;
1654
- const tx = await registry["register(string)"](agentURI);
1464
+ async register() {
1465
+ const tx = await this.identityRegistry["register()"]();
1655
1466
  const receipt = await tx.wait();
1656
- const transferTopic = import_ethers5.ethers.id("Transfer(address,address,uint256)");
1657
- const transferLog = receipt.logs.find((l) => l.topics[0] === transferTopic);
1658
- if (!transferLog?.topics?.[3]) {
1659
- throw new Error("Could not parse agentId from receipt");
1660
- }
1661
- const agentId = BigInt(transferLog.topics[3]);
1662
- this.resolvedAgentId = agentId.toString();
1663
- const exists = await this.factory.accountExists(agentId);
1664
- if (!exists) {
1665
- const accTx = await this.factory.createAccount(agentId);
1666
- await accTx.wait();
1667
- }
1668
- const accountAddr = await this.factory.getAccount(this.resolvedAgentId);
1669
- this.accountAddress = accountAddr;
1670
- return {
1671
- tx: tx.hash,
1672
- agentId: this.resolvedAgentId,
1673
- address: this.signer.address,
1674
- agentAccount: accountAddr,
1675
- alreadyRegistered: false
1676
- };
1467
+ const agentId = this.parseAgentIdFromReceipt(receipt);
1468
+ return { agentId, txHash: receipt.hash };
1677
1469
  }
1678
- // ══════════════════════════════════════════
1679
- // Balances & Status
1680
- // ══════════════════════════════════════════
1681
1470
  /**
1682
- * Get ETH and USDC balances for EOA wallet and AgentAccount.
1471
+ * Register agent with IPFS/HTTP URI to metadata JSON
1472
+ * @param agentURI URI pointing to agent metadata (ipfs:// or https://)
1683
1473
  */
1684
- async getBalances() {
1685
- const provider = this.signer.provider;
1686
- const ethBal = await provider.getBalance(this.signer.address);
1687
- const usdc = new import_ethers5.Contract(this.config.contracts.usdc, ERC20_ABI, provider);
1688
- const usdcBal = await usdc.balanceOf(this.signer.address);
1689
- const result = {
1690
- address: this.signer.address,
1691
- agentId: this.resolvedAgentId || "not registered",
1692
- eth: import_ethers5.ethers.formatEther(ethBal),
1693
- usdc: import_ethers5.ethers.formatUnits(usdcBal, 6)
1694
- };
1474
+ async registerWithURI(agentURI) {
1475
+ const tx = await this.identityRegistry["register(string)"](agentURI);
1476
+ const receipt = await tx.wait();
1477
+ const agentId = this.parseAgentIdFromReceipt(receipt);
1478
+ return { agentId, txHash: receipt.hash };
1479
+ }
1480
+ /**
1481
+ * Check if the signer already owns an ERC-8004 identity token.
1482
+ * Returns true if balanceOf > 0, false otherwise.
1483
+ * Note: Cannot determine the specific agentId without enumeration —
1484
+ * use agether init <pk> --agent-id <id> if you know your agentId.
1485
+ */
1486
+ async hasExistingIdentity() {
1695
1487
  try {
1696
- const accountAddr = await this.getAccountAddress();
1697
- const accEth = await provider.getBalance(accountAddr);
1698
- const accUsdc = await usdc.balanceOf(accountAddr);
1699
- result.agentAccount = {
1700
- address: accountAddr,
1701
- eth: import_ethers5.ethers.formatEther(accEth),
1702
- usdc: import_ethers5.ethers.formatUnits(accUsdc, 6)
1703
- };
1488
+ const address = await this.signer.getAddress();
1489
+ const balance = await this.identityRegistry.balanceOf(address);
1490
+ return balance > 0n;
1704
1491
  } catch {
1492
+ return false;
1705
1493
  }
1706
- return result;
1707
1494
  }
1708
1495
  /**
1709
- * Get full Morpho credit status all positions + total debt.
1496
+ * Register only if no identity exists; otherwise throw.
1497
+ * Prevents accidental double-registration.
1710
1498
  */
1711
- async getStatus() {
1712
- const accountAddr = await this.getAccountAddress();
1713
- const positions = [];
1714
- for (const [symbol, info] of Object.entries(this.collaterals)) {
1715
- const pos = await this.morpho.getPosition(accountAddr, info.address);
1716
- if (pos.isActive || pos.collateralAmount > 0n || pos.borrowedAmount > 0n) {
1717
- positions.push({
1718
- token: symbol,
1719
- collateral: import_ethers5.ethers.formatUnits(pos.collateralAmount, info.decimals),
1720
- debt: `$${import_ethers5.ethers.formatUnits(pos.borrowedAmount, 6)}`,
1721
- active: pos.isActive
1722
- });
1723
- }
1499
+ async registerOrGet() {
1500
+ const hasIdentity = await this.hasExistingIdentity();
1501
+ if (hasIdentity) {
1502
+ throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
1724
1503
  }
1725
- const totalDebt = await this.morpho.getTotalDebt(accountAddr);
1726
- return {
1727
- agentAccount: accountAddr,
1728
- totalDebt: `$${import_ethers5.ethers.formatUnits(totalDebt, 6)}`,
1729
- positions
1730
- };
1504
+ const result = await this.register();
1505
+ return { ...result, existing: false };
1506
+ }
1507
+ /**
1508
+ * Register with URI only if no identity exists; otherwise throw.
1509
+ * Prevents accidental double-registration.
1510
+ */
1511
+ async registerOrGetWithURI(agentURI) {
1512
+ const hasIdentity = await this.hasExistingIdentity();
1513
+ if (hasIdentity) {
1514
+ throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
1515
+ }
1516
+ const result = await this.registerWithURI(agentURI);
1517
+ return { ...result, existing: false };
1518
+ }
1519
+ /**
1520
+ * Register agent with URI and on-chain metadata
1521
+ */
1522
+ async registerWithMetadata(agentURI, metadata) {
1523
+ const metadataEntries = metadata.map((m) => ({
1524
+ key: m.key,
1525
+ value: import_ethers4.ethers.toUtf8Bytes(m.value)
1526
+ }));
1527
+ const tx = await this.identityRegistry["register(string,tuple(string,bytes)[])"](
1528
+ agentURI,
1529
+ metadataEntries
1530
+ );
1531
+ const receipt = await tx.wait();
1532
+ const agentId = this.parseAgentIdFromReceipt(receipt);
1533
+ return { agentId, txHash: receipt.hash };
1534
+ }
1535
+ /**
1536
+ * Get agent owner address
1537
+ */
1538
+ async getOwner(agentId) {
1539
+ return await this.identityRegistry.ownerOf(agentId);
1731
1540
  }
1732
- // ══════════════════════════════════════════
1733
- // Fund AgentAccount
1734
- // ══════════════════════════════════════════
1735
1541
  /**
1736
- * Transfer USDC from EOA wallet into AgentAccount.
1737
- *
1738
- * @param amount - Human-readable USDC amount (e.g. "50")
1542
+ * Get agent URI (metadata JSON location)
1739
1543
  */
1740
- async fundAccount(amount) {
1741
- const amountWei = import_ethers5.ethers.parseUnits(amount, 6);
1742
- const accountAddr = await this.getAccountAddress();
1743
- const usdc = new import_ethers5.Contract(this.config.contracts.usdc, ERC20_ABI, this.signer);
1744
- const balance = await usdc.balanceOf(this.signer.address);
1745
- if (balance < amountWei) {
1746
- throw new Error(`Insufficient USDC: have $${import_ethers5.ethers.formatUnits(balance, 6)}, need $${amount}`);
1747
- }
1748
- const tx = await usdc.transfer(accountAddr, amountWei);
1749
- await tx.wait();
1750
- return { tx: tx.hash, amount, agentAccount: accountAddr };
1544
+ async getAgentURI(agentId) {
1545
+ return await this.identityRegistry.tokenURI(agentId);
1751
1546
  }
1752
1547
  /**
1753
- * Check whether a borrow would exceed max LTV.
1754
- * Call before borrow() or depositAndBorrow() to get a clear error instead of on-chain revert.
1755
- *
1756
- * @param tokenSymbol - Collateral token
1757
- * @param additionalCollateral - Additional collateral being deposited ("0" if just borrowing)
1758
- * @param borrowAmount - USDC to borrow
1548
+ * Update agent URI
1759
1549
  */
1760
- async checkLtv(tokenSymbol, additionalCollateral, borrowAmount) {
1761
- const tokenInfo = this.resolveToken(tokenSymbol);
1762
- const accountAddr = await this.getAccountAddress();
1763
- const pos = await this.morpho.getPosition(accountAddr, tokenInfo.address);
1764
- const marketInfo = await this.morpho.markets(tokenInfo.address);
1765
- const oracle = new import_ethers5.Contract(marketInfo.params.oracle, _MorphoCreditClient.ORACLE_ABI, this.signer.provider);
1766
- const oraclePrice = await oracle.price();
1767
- const addCollateralWei = import_ethers5.ethers.parseUnits(additionalCollateral, tokenInfo.decimals);
1768
- const borrowWei = import_ethers5.ethers.parseUnits(borrowAmount, 6);
1769
- const totalCollateral = pos.collateralAmount + addCollateralWei;
1770
- const totalDebt = pos.borrowedAmount + borrowWei;
1771
- const collateralValue = totalCollateral * oraclePrice / BigInt("1000000000000000000000000000000000000");
1772
- const collateralUsd = Number(collateralValue) / 1e6;
1773
- const currentDebtUsd = Number(pos.borrowedAmount) / 1e6;
1774
- const maxLtvBps = Number(marketInfo.maxLtvBps);
1775
- const currentLtv = collateralValue > 0n ? Number(pos.borrowedAmount * 10000n / collateralValue) / 100 : 0;
1776
- const newLtv = collateralValue > 0n ? Number(totalDebt * 10000n / collateralValue) / 100 : totalDebt > 0n ? Infinity : 0;
1777
- const maxLtv = maxLtvBps / 100;
1778
- const maxBorrow = collateralUsd * maxLtvBps / 1e4;
1779
- const ok = newLtv <= maxLtv;
1780
- return {
1781
- ok,
1782
- currentLtv,
1783
- newLtv,
1784
- maxLtv,
1785
- collateralUsd,
1786
- currentDebt: currentDebtUsd,
1787
- maxBorrow,
1788
- message: ok ? void 0 : `ExceedsMaxLtv: borrowing $${borrowAmount} would push LTV to ${newLtv.toFixed(1)}% (max ${maxLtv}%). Collateral: $${collateralUsd.toFixed(2)}. Current debt: $${currentDebtUsd.toFixed(3)}. Max additional borrow: $${(maxBorrow - currentDebtUsd).toFixed(2)}. Deposit more collateral or borrow less.`
1789
- };
1550
+ async setAgentURI(agentId, newURI) {
1551
+ const tx = await this.identityRegistry.setAgentURI(agentId, newURI);
1552
+ const receipt = await tx.wait();
1553
+ return receipt.hash;
1790
1554
  }
1791
- // ══════════════════════════════════════════
1792
- // Flow 3: Deposit collateral only
1793
- // ══════════════════════════════════════════
1794
1555
  /**
1795
- * Deposit collateral from EOA into Morpho for own AgentAccount.
1796
- * Does NOT borrow — use `borrow()` or `depositAndBorrow()` for that.
1797
- *
1798
- * @param tokenSymbol - Collateral token (WETH, wstETH, cbETH)
1799
- * @param amount - Human-readable amount (e.g. "0.05")
1556
+ * Set on-chain metadata (key-value)
1800
1557
  */
1801
- async deposit(tokenSymbol, amount) {
1802
- const tokenInfo = this.resolveToken(tokenSymbol);
1803
- const amountWei = import_ethers5.ethers.parseUnits(amount, tokenInfo.decimals);
1804
- const accountAddr = await this.getAccountAddress();
1805
- const txHash = await this.approveAndDeposit(accountAddr, tokenInfo, amountWei);
1806
- await this.ensureCreditProvider(accountAddr);
1807
- const pos = await this.morpho.getPosition(accountAddr, tokenInfo.address);
1808
- return {
1809
- tx: txHash,
1810
- amount: amountWei,
1811
- token: tokenSymbol,
1812
- agentAccount: accountAddr,
1813
- totalCollateral: pos.collateralAmount
1814
- };
1558
+ async setMetadata(agentId, key, value) {
1559
+ const tx = await this.identityRegistry.setMetadata(
1560
+ agentId,
1561
+ key,
1562
+ import_ethers4.ethers.toUtf8Bytes(value)
1563
+ );
1564
+ const receipt = await tx.wait();
1565
+ return receipt.hash;
1815
1566
  }
1816
- // ══════════════════════════════════════════
1817
- // Flow 2: Deposit + borrow in one call
1818
- // ══════════════════════════════════════════
1819
1567
  /**
1820
- * Deposit collateral AND borrow USDC in a single SDK call.
1821
- * USDC lands in the AgentAccount.
1822
- *
1823
- * @param tokenSymbol - Collateral token (WETH, wstETH, cbETH)
1824
- * @param collateralAmount - Human-readable collateral amount (e.g. "0.05")
1825
- * @param borrowAmount - Human-readable USDC amount to borrow (e.g. "50")
1568
+ * Get on-chain metadata
1826
1569
  */
1827
- async depositAndBorrow(tokenSymbol, collateralAmount, borrowAmount) {
1828
- const tokenInfo = this.resolveToken(tokenSymbol);
1829
- const collateralWei = import_ethers5.ethers.parseUnits(collateralAmount, tokenInfo.decimals);
1830
- const borrowWei = import_ethers5.ethers.parseUnits(borrowAmount, 6);
1831
- const accountAddr = await this.getAccountAddress();
1832
- const ltvCheck = await this.checkLtv(tokenSymbol, collateralAmount, borrowAmount);
1833
- if (!ltvCheck.ok) throw new Error(ltvCheck.message);
1834
- const depositTxHash = await this.approveAndDeposit(accountAddr, tokenInfo, collateralWei);
1835
- await this.ensureCreditProvider(accountAddr);
1836
- const account = new import_ethers5.Contract(accountAddr, AGENT_ACCOUNT_ABI, this.signer);
1837
- const calldata = this.morphoIface.encodeFunctionData("drawWithCollateral", [tokenInfo.address, borrowWei]);
1838
- const borrowTx = await account.execute(this.config.contracts.morphoCredit, 0, calldata);
1839
- await borrowTx.wait();
1840
- const totalDebt = await this.morpho.getTotalDebt(accountAddr);
1841
- return {
1842
- depositTx: depositTxHash,
1843
- borrowTx: borrowTx.hash,
1844
- collateral: { amount: collateralWei, token: tokenSymbol },
1845
- borrowed: borrowWei,
1846
- agentAccount: accountAddr,
1847
- totalDebt
1848
- };
1570
+ async getMetadata(agentId, key) {
1571
+ const value = await this.identityRegistry.getMetadata(agentId, key);
1572
+ return import_ethers4.ethers.toUtf8String(value);
1849
1573
  }
1850
- // ══════════════════════════════════════════
1851
- // Flow 8: Borrow against existing collateral
1852
- // ══════════════════════════════════════════
1853
1574
  /**
1854
- * Borrow USDC against already-deposited collateral.
1855
- * Auto-detects which collateral token has a position.
1856
- * USDC lands in AgentAccount — ready for x402 payments.
1857
- *
1858
- * @param amount - Human-readable USDC amount (e.g. "100")
1575
+ * Transfer agent to new owner
1859
1576
  */
1860
- async borrow(amount) {
1861
- const borrowWei = import_ethers5.ethers.parseUnits(amount, 6);
1862
- const accountAddr = await this.getAccountAddress();
1863
- let activeToken = null;
1864
- for (const info of Object.values(this.collaterals)) {
1865
- const pos = await this.morpho.getPosition(accountAddr, info.address);
1866
- if (pos.collateralAmount > 0n) {
1867
- activeToken = info;
1868
- break;
1869
- }
1870
- }
1871
- if (!activeToken) {
1872
- throw new Error("No collateral deposited. Use deposit() or depositAndBorrow() first.");
1873
- }
1874
- const ltvCheck = await this.checkLtv(activeToken.symbol, "0", amount);
1875
- if (!ltvCheck.ok) throw new Error(ltvCheck.message);
1876
- await this.ensureCreditProvider(accountAddr);
1877
- const account = new import_ethers5.Contract(accountAddr, AGENT_ACCOUNT_ABI, this.signer);
1878
- const calldata = this.morphoIface.encodeFunctionData("drawWithCollateral", [activeToken.address, borrowWei]);
1879
- const tx = await account.execute(this.config.contracts.morphoCredit, 0, calldata);
1880
- await tx.wait();
1881
- const totalDebt = await this.morpho.getTotalDebt(accountAddr);
1882
- return {
1883
- tx: tx.hash,
1884
- amount: borrowWei,
1885
- agentAccount: accountAddr,
1886
- totalDebt,
1887
- collateralToken: activeToken.symbol
1888
- };
1577
+ async transfer(agentId, to) {
1578
+ const from = await this.signer.getAddress();
1579
+ const tx = await this.identityRegistry.transferFrom(from, to, agentId);
1580
+ const receipt = await tx.wait();
1581
+ return receipt.hash;
1889
1582
  }
1890
- // ══════════════════════════════════════════
1891
- // Flows 4-7: Sponsor (deposit for another agent)
1892
- // ══════════════════════════════════════════
1893
1583
  /**
1894
- * Deposit collateral for another agent. Caller pays from their wallet.
1895
- * Optionally borrow USDC for the agent (only works if caller owns the AgentAccount).
1896
- *
1897
- * Supports both agentId lookup and direct address.
1898
- *
1899
- * @param target - `{ agentId: "17676" }` or `{ address: "0x..." }`
1900
- * @param tokenSymbol - Collateral token
1901
- * @param amount - Human-readable collateral amount
1902
- * @param borrowAmount - Optional: USDC to borrow (only if caller is owner)
1584
+ * Fetch and parse agent metadata from URI
1903
1585
  */
1904
- async sponsor(target, tokenSymbol, amount, borrowAmount) {
1905
- const tokenInfo = this.resolveToken(tokenSymbol);
1906
- const collateralWei = import_ethers5.ethers.parseUnits(amount, tokenInfo.decimals);
1907
- let accountAddr;
1908
- let targetAgentId;
1909
- if ("agentId" in target) {
1910
- targetAgentId = target.agentId.toString();
1911
- accountAddr = await this.getAccountForAgent(target.agentId);
1912
- } else {
1913
- accountAddr = target.address;
1914
- }
1915
- const depositTxHash = await this.approveAndDeposit(accountAddr, tokenInfo, collateralWei);
1916
- const result = {
1917
- depositTx: depositTxHash,
1918
- targetAccount: accountAddr,
1919
- targetAgentId,
1920
- collateral: { amount: collateralWei, token: tokenSymbol },
1921
- totalCollateral: 0n,
1922
- totalDebt: 0n
1923
- };
1924
- if (borrowAmount) {
1925
- const borrowWei = import_ethers5.ethers.parseUnits(borrowAmount, 6);
1926
- try {
1927
- await this.ensureCreditProvider(accountAddr);
1928
- const account = new import_ethers5.Contract(accountAddr, AGENT_ACCOUNT_ABI, this.signer);
1929
- const calldata = this.morphoIface.encodeFunctionData("drawWithCollateral", [tokenInfo.address, borrowWei]);
1930
- const tx = await account.execute(this.config.contracts.morphoCredit, 0, calldata);
1931
- await tx.wait();
1932
- result.borrowTx = tx.hash;
1933
- result.borrowed = borrowWei;
1934
- } catch (e) {
1935
- throw new Error(`Borrow failed (caller may not own this AgentAccount): ${e.message}`);
1586
+ async fetchAgentMetadata(agentId) {
1587
+ try {
1588
+ const uri = await this.getAgentURI(agentId);
1589
+ let fetchUrl = uri;
1590
+ if (uri.startsWith("ipfs://")) {
1591
+ const cid = uri.replace("ipfs://", "");
1592
+ fetchUrl = `https://ipfs.io/ipfs/${cid}`;
1936
1593
  }
1594
+ const response = await fetch(fetchUrl);
1595
+ if (!response.ok) return null;
1596
+ return await response.json();
1597
+ } catch {
1598
+ return null;
1937
1599
  }
1938
- const pos = await this.morpho.getPosition(accountAddr, tokenInfo.address);
1939
- result.totalCollateral = pos.collateralAmount;
1940
- result.totalDebt = pos.borrowedAmount;
1941
- return result;
1942
1600
  }
1943
- // ══════════════════════════════════════════
1944
- // Repay & Withdraw
1945
- // ══════════════════════════════════════════
1601
+ // ============ Reputation Functions ============
1946
1602
  /**
1947
- * Repay borrowed USDC from AgentAccount back to Morpho.
1948
- *
1949
- * @param amount - Human-readable USDC amount (e.g. "50")
1603
+ * Give feedback to an agent
1950
1604
  */
1951
- async repay(amount) {
1952
- const amountWei = import_ethers5.ethers.parseUnits(amount, 6);
1953
- const accountAddr = await this.getAccountAddress();
1954
- let collateralAddr = null;
1955
- for (const info of Object.values(this.collaterals)) {
1956
- const pos = await this.morpho.getPosition(accountAddr, info.address);
1957
- if (pos.borrowedAmount > 0n) {
1958
- collateralAddr = info.address;
1959
- break;
1960
- }
1961
- }
1962
- if (!collateralAddr) throw new Error("No Morpho debt to repay.");
1963
- const usdc = new import_ethers5.Contract(this.config.contracts.usdc, ERC20_ABI, this.signer.provider);
1964
- const balance = await usdc.balanceOf(accountAddr);
1965
- if (balance < amountWei) {
1966
- throw new Error(`Insufficient USDC in AgentAccount: $${import_ethers5.ethers.formatUnits(balance, 6)}`);
1967
- }
1968
- const txHash = await this.batch(accountAddr, [
1969
- {
1970
- target: this.config.contracts.usdc,
1971
- value: 0n,
1972
- data: this.erc20Iface.encodeFunctionData("approve", [this.config.contracts.morphoCredit, amountWei])
1973
- },
1974
- {
1975
- target: this.config.contracts.morphoCredit,
1976
- value: 0n,
1977
- data: this.morphoIface.encodeFunctionData("repayWithCollateral", [collateralAddr, amountWei])
1978
- }
1979
- ]);
1980
- const totalDebt = await this.morpho.getTotalDebt(accountAddr);
1981
- return { tx: txHash, amount: amountWei, remainingDebt: totalDebt };
1605
+ async giveFeedback(input) {
1606
+ const feedbackHash = import_ethers4.ethers.keccak256(
1607
+ import_ethers4.ethers.AbiCoder.defaultAbiCoder().encode(
1608
+ ["uint256", "int128", "uint256"],
1609
+ [input.agentId, input.value, Date.now()]
1610
+ )
1611
+ );
1612
+ const tx = await this.reputationRegistry.giveFeedback(
1613
+ input.agentId,
1614
+ input.value,
1615
+ input.decimals || 0,
1616
+ input.tag1 || "",
1617
+ input.tag2 || "",
1618
+ input.endpoint || "",
1619
+ input.feedbackURI || "",
1620
+ feedbackHash
1621
+ );
1622
+ const receipt = await tx.wait();
1623
+ return receipt.hash;
1982
1624
  }
1983
1625
  /**
1984
- * Withdraw collateral from Morpho back to EOA.
1985
- *
1986
- * @param tokenSymbol - Collateral token
1987
- * @param amount - Human-readable amount or "all"
1626
+ * Give positive feedback (shorthand)
1988
1627
  */
1989
- async withdraw(tokenSymbol, amount) {
1990
- const tokenInfo = this.resolveToken(tokenSymbol);
1991
- const accountAddr = await this.getAccountAddress();
1992
- const pos = await this.morpho.getPosition(accountAddr, tokenInfo.address);
1993
- if (pos.collateralAmount === 0n) {
1994
- throw new Error(`No ${tokenSymbol} collateral deposited.`);
1995
- }
1996
- const withdrawAmount = amount.toLowerCase() === "all" ? pos.collateralAmount : import_ethers5.ethers.parseUnits(amount, tokenInfo.decimals);
1997
- if (withdrawAmount > pos.collateralAmount) {
1998
- throw new Error(
1999
- `Cannot withdraw more than deposited: max ${import_ethers5.ethers.formatUnits(pos.collateralAmount, tokenInfo.decimals)} ${tokenSymbol}`
2000
- );
2001
- }
2002
- const txHash = await this.batch(accountAddr, [
2003
- {
2004
- target: this.config.contracts.morphoCredit,
2005
- value: 0n,
2006
- data: this.morphoIface.encodeFunctionData("withdrawCollateral", [tokenInfo.address, withdrawAmount])
2007
- },
2008
- {
2009
- target: tokenInfo.address,
2010
- value: 0n,
2011
- data: this.erc20Iface.encodeFunctionData("transfer", [this.signer.address, withdrawAmount])
2012
- }
2013
- ]);
2014
- const newPos = await this.morpho.getPosition(accountAddr, tokenInfo.address);
2015
- return {
2016
- tx: txHash,
2017
- amount: withdrawAmount,
2018
- token: tokenSymbol,
2019
- destination: this.signer.address,
2020
- remainingCollateral: newPos.collateralAmount
2021
- };
1628
+ async givePosisitiveFeedback(agentId, value = 100, tags = {}) {
1629
+ return this.giveFeedback({
1630
+ agentId,
1631
+ value: Math.abs(value),
1632
+ ...tags
1633
+ });
2022
1634
  }
2023
- // ══════════════════════════════════════════
2024
- // View methods
2025
- // ══════════════════════════════════════════
2026
1635
  /**
2027
- * Get position for a specific collateral token.
1636
+ * Give negative feedback (e.g., for defaults)
2028
1637
  */
2029
- async getPosition(tokenSymbol) {
2030
- const tokenInfo = this.resolveToken(tokenSymbol);
2031
- const accountAddr = await this.getAccountAddress();
2032
- const pos = await this.morpho.getPosition(accountAddr, tokenInfo.address);
2033
- return {
2034
- token: tokenSymbol,
2035
- collateralAmount: pos.collateralAmount,
2036
- borrowedAmount: pos.borrowedAmount,
2037
- borrowShares: pos.borrowShares,
2038
- isActive: pos.isActive
2039
- };
1638
+ async giveNegativeFeedback(agentId, value = -100, tags = {}) {
1639
+ return this.giveFeedback({
1640
+ agentId,
1641
+ value: -Math.abs(value),
1642
+ ...tags
1643
+ });
2040
1644
  }
2041
1645
  /**
2042
- * Get all active positions across all collateral tokens.
1646
+ * Record credit default in reputation system
2043
1647
  */
2044
- async getAllPositions() {
2045
- const accountAddr = await this.getAccountAddress();
2046
- const positions = [];
2047
- for (const [symbol, info] of Object.entries(this.collaterals)) {
2048
- const pos = await this.morpho.getPosition(accountAddr, info.address);
2049
- if (pos.isActive || pos.collateralAmount > 0n || pos.borrowedAmount > 0n) {
2050
- positions.push({
2051
- token: symbol,
2052
- collateralAmount: pos.collateralAmount,
2053
- borrowedAmount: pos.borrowedAmount,
2054
- borrowShares: pos.borrowShares,
2055
- isActive: pos.isActive
2056
- });
2057
- }
2058
- }
2059
- return positions;
1648
+ async recordCreditDefault(agentId, creditLineId) {
1649
+ return this.giveFeedback({
1650
+ agentId,
1651
+ value: -100,
1652
+ tag1: "credit",
1653
+ tag2: "default",
1654
+ feedbackURI: `agether://default/${creditLineId}`
1655
+ });
2060
1656
  }
2061
1657
  /**
2062
- * Get total debt across all positions.
1658
+ * Get reputation summary for an agent
2063
1659
  */
2064
- async getTotalDebt() {
2065
- const accountAddr = await this.getAccountAddress();
2066
- return this.morpho.getTotalDebt(accountAddr);
1660
+ async getReputation(agentId, tag1 = "", tag2 = "") {
1661
+ const clients = await this.reputationRegistry.getClients(agentId);
1662
+ if (clients.length === 0) {
1663
+ return {
1664
+ count: 0,
1665
+ totalValue: 0,
1666
+ averageValue: 0,
1667
+ clients: []
1668
+ };
1669
+ }
1670
+ const [count, summaryValue, decimals] = await this.reputationRegistry.getSummary(
1671
+ agentId,
1672
+ clients,
1673
+ tag1,
1674
+ tag2
1675
+ );
1676
+ const divisor = 10 ** Number(decimals);
1677
+ const total = Number(summaryValue) / divisor;
1678
+ return {
1679
+ count: Number(count),
1680
+ totalValue: total,
1681
+ averageValue: Number(count) > 0 ? total / Number(count) : 0,
1682
+ clients: clients.map((c) => c)
1683
+ };
2067
1684
  }
2068
1685
  /**
2069
- * Get USDC balance in the AgentAccount.
1686
+ * Check if agent has negative credit reputation
2070
1687
  */
2071
- async getAccountUSDC() {
2072
- const accountAddr = await this.getAccountAddress();
2073
- const usdc = new import_ethers5.Contract(this.config.contracts.usdc, ERC20_ABI, this.signer.provider);
2074
- return usdc.balanceOf(accountAddr);
2075
- }
2076
- /** Get the wallet (signer) address */
2077
- getAddress() {
2078
- return this.signer.address;
2079
- }
2080
- };
2081
- _MorphoCreditClient.ERC8004_ABI = [
2082
- "function register(string agentURI) returns (uint256)",
2083
- "function ownerOf(uint256 tokenId) view returns (address)",
2084
- "function balanceOf(address owner) view returns (uint256)"
2085
- ];
2086
- // ══════════════════════════════════════════
2087
- // LTV Pre-check
2088
- // ══════════════════════════════════════════
2089
- _MorphoCreditClient.ORACLE_ABI = ["function price() view returns (uint256)"];
2090
- var MorphoCreditClient = _MorphoCreditClient;
2091
-
2092
- // src/clients/WalletClient.ts
2093
- var import_ethers6 = require("ethers");
2094
- var WalletClient = class {
2095
- constructor(config) {
2096
- this.privateKey = null;
2097
- this.factoryAddress = config.factoryAddress;
2098
- this.usdcAddress = config.usdcAddress || import_ethers6.ethers.ZeroAddress;
2099
- this.chainId = config.chainId;
2100
- this.rpcUrl = config.rpcUrl;
2101
- this.provider = new import_ethers6.ethers.JsonRpcProvider(config.rpcUrl);
2102
- this.privateKey = config.privateKey || null;
1688
+ async hasNegativeCreditReputation(agentId) {
1689
+ const rep = await this.getReputation(agentId, "credit", "");
1690
+ return rep.count > 0 && rep.averageValue < 0;
2103
1691
  }
1692
+ // ============ Agether Integration ============
2104
1693
  /**
2105
- * Create a fresh signer to avoid nonce caching issues
1694
+ * Verify agent is eligible for Agether credit
1695
+ * Checks:
1696
+ * 1. Agent exists in ERC-8004 registry
1697
+ * 2. Agent has no negative credit reputation
2106
1698
  */
2107
- getFreshSigner() {
2108
- if (!this.privateKey) {
2109
- throw new Error("Signer not configured - provide privateKey");
2110
- }
2111
- const freshProvider = new import_ethers6.ethers.JsonRpcProvider(this.rpcUrl);
2112
- return new import_ethers6.Wallet(this.privateKey, freshProvider);
2113
- }
2114
- getFactoryContract() {
2115
- if (this.privateKey) {
2116
- return new import_ethers6.Contract(this.factoryAddress, ACCOUNT_FACTORY_ABI, this.getFreshSigner());
2117
- }
2118
- return new import_ethers6.Contract(this.factoryAddress, ACCOUNT_FACTORY_ABI, this.provider);
2119
- }
2120
- getAccountContract(accountAddress) {
2121
- if (this.privateKey) {
2122
- return new import_ethers6.Contract(accountAddress, AGENT_ACCOUNT_ABI, this.getFreshSigner());
2123
- }
2124
- return new import_ethers6.Contract(accountAddress, AGENT_ACCOUNT_ABI, this.provider);
2125
- }
2126
- getChainId() {
2127
- return this.chainId;
2128
- }
2129
- // ============ Account Factory ============
2130
- async accountExists(agentId) {
2131
- const factory = this.getFactoryContract();
2132
- return factory.accountExists(agentId);
2133
- }
2134
- async getAccount(agentId) {
2135
- const factory = this.getFactoryContract();
2136
- const address = await factory.getAccount(agentId);
2137
- if (address === import_ethers6.ethers.ZeroAddress) {
2138
- return null;
2139
- }
2140
- return address;
2141
- }
2142
- async predictAddress(agentId) {
2143
- const factory = this.getFactoryContract();
2144
- return factory.predictAddress(agentId);
2145
- }
2146
- async createAccount(agentId) {
2147
- if (!this.privateKey) {
2148
- throw new Error("Signer not configured - provide privateKey");
2149
- }
2150
- const factory = this.getFactoryContract();
2151
- const tx = await factory.createAccount(agentId);
2152
- await tx.wait();
2153
- const account = await this.getAccount(agentId);
2154
- if (!account) {
2155
- throw new Error("Account creation failed");
1699
+ async verifyForCredit(agentId) {
1700
+ let owner;
1701
+ try {
1702
+ owner = await this.getOwner(agentId);
1703
+ } catch {
1704
+ return { eligible: false, reason: "Agent not found in ERC-8004 registry" };
2156
1705
  }
2157
- return account;
2158
- }
2159
- async totalAccounts() {
2160
- const factory = this.getFactoryContract();
2161
- return factory.totalAccounts();
2162
- }
2163
- // ============ Account Info ============
2164
- async getWalletInfo(accountAddress) {
2165
- const account = this.getAccountContract(accountAddress);
2166
- const [agentId, owner, ethBalance] = await Promise.all([
2167
- account.agentId(),
2168
- account.owner(),
2169
- account.ethBalance()
2170
- ]);
2171
- let usdcBalance = 0n;
2172
- if (this.usdcAddress !== import_ethers6.ethers.ZeroAddress) {
2173
- usdcBalance = await account.balanceOf(this.usdcAddress);
1706
+ const reputation = await this.getReputation(agentId, "credit", "");
1707
+ if (reputation.count > 0 && reputation.averageValue < 0) {
1708
+ return {
1709
+ eligible: false,
1710
+ reason: "Agent has negative credit reputation",
1711
+ owner,
1712
+ reputation
1713
+ };
2174
1714
  }
2175
1715
  return {
2176
- address: accountAddress,
2177
- agentId: BigInt(agentId),
1716
+ eligible: true,
2178
1717
  owner,
2179
- ethBalance: BigInt(ethBalance),
2180
- usdcBalance: BigInt(usdcBalance)
2181
- };
2182
- }
2183
- async getProviderStatus(accountAddress, creditProvider) {
2184
- const provider = new import_ethers6.Contract(
2185
- creditProvider,
2186
- CREDIT_PROVIDER_ABI,
2187
- this.provider
2188
- );
2189
- const [isEligible, totalDebt, maxDrawable] = await Promise.all([
2190
- provider.isEligible(accountAddress),
2191
- provider.getTotalDebt(accountAddress),
2192
- provider.maxDrawable(accountAddress)
2193
- ]);
2194
- return {
2195
- provider: creditProvider,
2196
- isEligible,
2197
- totalDebt: BigInt(totalDebt),
2198
- maxDrawable: BigInt(maxDrawable)
1718
+ reputation
2199
1719
  };
2200
1720
  }
2201
- // ============ Fund / Withdraw ============
2202
- async fundAccount(accountAddress, tokenAddress, amount) {
2203
- if (!this.privateKey) {
2204
- throw new Error("Signer not configured");
2205
- }
2206
- const signer = this.getFreshSigner();
2207
- const token = new import_ethers6.Contract(tokenAddress, ERC20_ABI, signer);
2208
- const approveTx = await token.approve(accountAddress, amount);
2209
- await approveTx.wait();
2210
- const signer2 = this.getFreshSigner();
2211
- const account = new import_ethers6.Contract(accountAddress, AGENT_ACCOUNT_ABI, signer2);
2212
- const tx = await account.fund(tokenAddress, amount);
2213
- const receipt = await tx.wait();
2214
- return receipt.hash;
2215
- }
2216
- async withdraw(accountAddress, tokenAddress, amount, to) {
2217
- if (!this.privateKey) {
2218
- throw new Error("Signer not configured");
2219
- }
2220
- const account = this.getAccountContract(accountAddress);
2221
- const recipient = to || await this.getFreshSigner().getAddress();
2222
- const tx = await account.withdraw(tokenAddress, amount, recipient);
2223
- const receipt = await tx.wait();
2224
- return receipt.hash;
2225
- }
2226
- // ============ Credit Operations ============
2227
- async drawCredit(accountAddress, creditProvider, amount) {
2228
- if (!this.privateKey) {
2229
- throw new Error("Signer not configured");
2230
- }
2231
- const account = this.getAccountContract(accountAddress);
2232
- const tx = await account.drawCredit(creditProvider, amount);
2233
- const receipt = await tx.wait();
2234
- return receipt.hash;
2235
- }
2236
- async repayCredit(accountAddress, creditProvider, amount) {
2237
- if (!this.privateKey) {
2238
- throw new Error("Signer not configured");
2239
- }
2240
- const account = this.getAccountContract(accountAddress);
2241
- const tx = await account.repayCredit(creditProvider, amount);
2242
- const receipt = await tx.wait();
2243
- return receipt.hash;
2244
- }
2245
- // ============ Generic Execute ============
2246
- async execute(accountAddress, target, value, data) {
2247
- if (!this.privateKey) {
2248
- throw new Error("Signer not configured");
1721
+ // ============ Helpers ============
1722
+ parseAgentIdFromReceipt(receipt) {
1723
+ for (const log of receipt.logs) {
1724
+ try {
1725
+ const parsed = this.identityRegistry.interface.parseLog({
1726
+ topics: log.topics,
1727
+ data: log.data
1728
+ });
1729
+ if (parsed?.name === "Transfer") {
1730
+ return parsed.args[2];
1731
+ }
1732
+ } catch {
1733
+ continue;
1734
+ }
2249
1735
  }
2250
- const account = this.getAccountContract(accountAddress);
2251
- const tx = await account.execute(target, value, data);
2252
- const receipt = await tx.wait();
2253
- return receipt.hash;
1736
+ return 0n;
2254
1737
  }
2255
- // ============ Utility ============
2256
- generatePaymentMessageHash(accountAddress, recipient, amount, nonce, deadline, chainId) {
2257
- return import_ethers6.ethers.keccak256(
2258
- import_ethers6.ethers.AbiCoder.defaultAbiCoder().encode(
2259
- ["address", "uint256", "uint256", "uint256", "uint256", "address"],
2260
- [recipient, amount, nonce, deadline, chainId, accountAddress]
2261
- )
2262
- );
1738
+ /**
1739
+ * Get contract addresses
1740
+ */
1741
+ getContractAddresses() {
1742
+ return {
1743
+ identity: this.identityRegistry.target,
1744
+ reputation: this.reputationRegistry.target
1745
+ };
2263
1746
  }
2264
1747
  };
2265
1748
 
@@ -2311,24 +1794,18 @@ function rateToBps(rate) {
2311
1794
  AGENT_ACCOUNT_ABI,
2312
1795
  AGENT_REPUTATION_ABI,
2313
1796
  AgentIdentityClient,
1797
+ AgentNotApprovedError,
2314
1798
  AgetherClient,
2315
1799
  AgetherError,
2316
- CREDIT_PROVIDER_ABI,
2317
1800
  ChainId,
2318
- CreditNotActiveError,
2319
- CreditStatus,
2320
1801
  ERC20_ABI,
2321
1802
  IDENTITY_REGISTRY_ABI,
2322
- InsufficientCreditError,
2323
- LP_VAULT_ABI,
2324
- MORPHO_CREDIT_ABI,
2325
- MorphoCreditClient,
2326
- REPUTATION_CREDIT_ABI,
1803
+ InsufficientBalanceError,
1804
+ MORPHO_BLUE_ABI,
1805
+ MorphoClient,
2327
1806
  ScoringClient,
2328
1807
  ScoringRejectedError,
2329
1808
  VALIDATION_REGISTRY_ABI,
2330
- VaultClient,
2331
- WalletClient,
2332
1809
  X402Client,
2333
1810
  bpsToRate,
2334
1811
  createConfig,
@@ -2340,7 +1817,6 @@ function rateToBps(rate) {
2340
1817
  formatUSD,
2341
1818
  formatUnits,
2342
1819
  getDefaultConfig,
2343
- getUSDCAddress,
2344
1820
  parseUnits,
2345
1821
  rateToBps
2346
1822
  });