@agether/sdk 1.5.2 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,26 +1,207 @@
1
- import {
2
- ACCOUNT_FACTORY_ABI,
3
- AGENT_ACCOUNT_ABI,
4
- AGENT_REPUTATION_ABI,
5
- AgentNotApprovedError,
6
- AgetherError,
7
- ChainId,
8
- ERC20_ABI,
9
- IDENTITY_REGISTRY_ABI,
10
- InsufficientBalanceError,
11
- MORPHO_BLUE_ABI,
12
- MorphoClient,
13
- ScoringRejectedError,
14
- VALIDATION_REGISTRY_ABI,
15
- createConfig,
16
- getDefaultConfig
17
- } from "./chunk-GAFDBPDW.mjs";
18
- import {
19
- X402Client
20
- } from "./chunk-PTXYOTCG.mjs";
21
-
22
1
  // src/clients/AgetherClient.ts
23
2
  import { ethers, Contract } from "ethers";
3
+
4
+ // src/types/index.ts
5
+ var ChainId = /* @__PURE__ */ ((ChainId3) => {
6
+ ChainId3[ChainId3["Ethereum"] = 1] = "Ethereum";
7
+ ChainId3[ChainId3["Base"] = 8453] = "Base";
8
+ ChainId3[ChainId3["BaseSepolia"] = 84532] = "BaseSepolia";
9
+ ChainId3[ChainId3["Sepolia"] = 11155111] = "Sepolia";
10
+ ChainId3[ChainId3["Hardhat"] = 31337] = "Hardhat";
11
+ return ChainId3;
12
+ })(ChainId || {});
13
+ var AgetherError = class extends Error {
14
+ constructor(message, code, details) {
15
+ super(message);
16
+ this.code = code;
17
+ this.details = details;
18
+ this.name = "AgetherError";
19
+ }
20
+ };
21
+ var InsufficientBalanceError = class extends AgetherError {
22
+ constructor(available, required) {
23
+ super(
24
+ `Insufficient balance: available ${available}, required ${required}`,
25
+ "INSUFFICIENT_BALANCE",
26
+ { available: available.toString(), required: required.toString() }
27
+ );
28
+ }
29
+ };
30
+ var ScoringRejectedError = class extends AgetherError {
31
+ constructor(riskScore, reason) {
32
+ super(
33
+ `Scoring rejected: risk score ${riskScore}${reason ? `, ${reason}` : ""}`,
34
+ "SCORING_REJECTED",
35
+ { riskScore, reason }
36
+ );
37
+ }
38
+ };
39
+ var AgentNotApprovedError = class extends AgetherError {
40
+ constructor(agentId) {
41
+ super(
42
+ `Agent ${agentId} is not KYA-approved. Submit code for validation first.`,
43
+ "AGENT_NOT_APPROVED",
44
+ { agentId: agentId.toString() }
45
+ );
46
+ }
47
+ };
48
+
49
+ // src/utils/abis.ts
50
+ var IDENTITY_REGISTRY_ABI = [
51
+ "function ownerOf(uint256 agentId) view returns (address)",
52
+ "function balanceOf(address owner) view returns (uint256)",
53
+ "function totalSupply() view returns (uint256)",
54
+ "function exists(uint256 agentId) view returns (bool)",
55
+ "function register() returns (uint256 agentId)",
56
+ "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
57
+ ];
58
+ var ACCOUNT_FACTORY_ABI = [
59
+ "function getAccount(uint256 agentId) view returns (address)",
60
+ "function accountExists(uint256 agentId) view returns (bool)",
61
+ "function predictAddress(uint256 agentId) view returns (address)",
62
+ "function totalAccounts() view returns (uint256)",
63
+ "function getAgentId(address account) view returns (uint256)",
64
+ "function createAccount(uint256 agentId) returns (address account)",
65
+ "event AccountCreated(uint256 indexed agentId, address indexed account, address indexed owner)"
66
+ ];
67
+ var AGENT_ACCOUNT_ABI = [
68
+ "function agentId() view returns (uint256)",
69
+ "function owner() view returns (address)",
70
+ "function factory() view returns (address)",
71
+ "function validationRegistry() view returns (address)",
72
+ "function identityRegistry() view returns (address)",
73
+ "function balanceOf(address token) view returns (uint256)",
74
+ "function ethBalance() view returns (uint256)",
75
+ "function execute(address target, uint256 value, bytes data) payable returns (bytes)",
76
+ "function executeBatch(address[] targets, uint256[] values, bytes[] datas) payable returns (bytes[])",
77
+ "function fund(address token, uint256 amount)",
78
+ "function withdraw(address token, uint256 amount, address to)",
79
+ "function withdrawETH(uint256 amount, address to)",
80
+ "function isValidSignature(bytes32 hash, bytes signature) view returns (bytes4)"
81
+ ];
82
+ var AGENT_REPUTATION_ABI = [
83
+ "function getCreditScore(uint256 agentId) view returns (uint256)",
84
+ "function getAttestation(uint256 agentId) view returns (tuple(uint256 score, uint256 timestamp, address signer))",
85
+ "function isScoreFresh(uint256 agentId) view returns (bool fresh, uint256 age)",
86
+ "function isEligible(uint256 agentId, uint256 minScore) view returns (bool eligible, uint256 currentScore)",
87
+ "function oracleSigner() view returns (address)",
88
+ "function submitScore(uint256 agentId, uint256 score_, uint256 timestamp_, bytes signature)",
89
+ "function setOracleSigner(address signer_)",
90
+ "event ScoreUpdated(uint256 indexed agentId, uint256 score, uint256 timestamp, address signer)"
91
+ ];
92
+ var VALIDATION_REGISTRY_ABI = [
93
+ "function isAgentCodeApproved(uint256 agentId) view returns (bool)",
94
+ "function isAgentCodeApprovedForTag(uint256 agentId, string tag) view returns (bool)",
95
+ "function getAgentValidations(uint256 agentId) view returns (bytes32[])",
96
+ "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))"
97
+ ];
98
+ var MORPHO_BLUE_ABI = [
99
+ // Supply & Withdraw (lending side)
100
+ "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)",
101
+ "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)",
102
+ // Collateral
103
+ "function supplyCollateral(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, bytes data)",
104
+ "function withdrawCollateral(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, address receiver)",
105
+ // Borrow & Repay
106
+ "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)",
107
+ "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)",
108
+ // Authorization
109
+ "function setAuthorization(address authorized, bool newIsAuthorized)",
110
+ "function isAuthorized(address authorizer, address authorized) view returns (bool)",
111
+ // Views
112
+ "function position(bytes32 id, address user) view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral)",
113
+ "function market(bytes32 id) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)",
114
+ "function idToMarketParams(bytes32 id) view returns (tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv))"
115
+ ];
116
+ var ERC20_ABI = [
117
+ "function balanceOf(address account) view returns (uint256)",
118
+ "function allowance(address owner, address spender) view returns (uint256)",
119
+ "function approve(address spender, uint256 amount) returns (bool)",
120
+ "function transfer(address to, uint256 amount) returns (bool)",
121
+ "function transferFrom(address from, address to, uint256 amount) returns (bool)",
122
+ "function decimals() view returns (uint8)",
123
+ "function symbol() view returns (string)",
124
+ "function name() view returns (string)"
125
+ ];
126
+
127
+ // src/utils/config.ts
128
+ var CONTRACT_ADDRESSES = {
129
+ [1 /* Ethereum */]: {
130
+ accountFactory: "0x0000000000000000000000000000000000000000",
131
+ validationRegistry: "0x0000000000000000000000000000000000000000",
132
+ agentReputation: "0x0000000000000000000000000000000000000000",
133
+ usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
134
+ identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
135
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
136
+ },
137
+ [8453 /* Base */]: {
138
+ accountFactory: "0x7D5D56416bAEA06a9DCBe3092eF335724C6320a0",
139
+ validationRegistry: "0xd196C32D2149270F56E209ba7aEE67CE9ceD2001",
140
+ agentReputation: "0x65c9cA1211809D3CF3A2707558198eb2b2bE623c",
141
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
142
+ identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
143
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
144
+ },
145
+ [84532 /* BaseSepolia */]: {
146
+ accountFactory: "0x0000000000000000000000000000000000000000",
147
+ validationRegistry: "0x0000000000000000000000000000000000000000",
148
+ agentReputation: "0x0000000000000000000000000000000000000000",
149
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
150
+ identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
151
+ morphoBlue: "0x0000000000000000000000000000000000000000"
152
+ },
153
+ [11155111 /* Sepolia */]: {
154
+ accountFactory: "0x0000000000000000000000000000000000000000",
155
+ validationRegistry: "0x0000000000000000000000000000000000000000",
156
+ agentReputation: "0x0000000000000000000000000000000000000000",
157
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
158
+ identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
159
+ morphoBlue: "0x0000000000000000000000000000000000000000"
160
+ },
161
+ [31337 /* Hardhat */]: {
162
+ accountFactory: "0x0000000000000000000000000000000000000000",
163
+ validationRegistry: "0x0000000000000000000000000000000000000000",
164
+ agentReputation: "0x0000000000000000000000000000000000000000",
165
+ usdc: "0x56d4d6aEe0278c5Df2FA23Ecb32eC146C9446FDf",
166
+ identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
167
+ morphoBlue: "0x0000000000000000000000000000000000000000"
168
+ }
169
+ };
170
+ var RPC_URLS = {
171
+ [1 /* Ethereum */]: "https://ethereum-rpc.publicnode.com",
172
+ [8453 /* Base */]: "https://base-rpc.publicnode.com",
173
+ [84532 /* BaseSepolia */]: "https://sepolia.base.org",
174
+ [11155111 /* Sepolia */]: "https://rpc.sepolia.org",
175
+ [31337 /* Hardhat */]: "http://127.0.0.1:8545"
176
+ };
177
+ var SCORING_ENDPOINTS = {
178
+ [1 /* Ethereum */]: "https://scoring.agether.ai/v1",
179
+ [8453 /* Base */]: "http://95.179.189.214:3001",
180
+ [84532 /* BaseSepolia */]: "http://95.179.189.214:3001",
181
+ [11155111 /* Sepolia */]: "https://scoring-testnet.agether.ai/v1",
182
+ [31337 /* Hardhat */]: "http://127.0.0.1:3001"
183
+ };
184
+ function getDefaultConfig(chainId) {
185
+ return {
186
+ chainId,
187
+ rpcUrl: RPC_URLS[chainId],
188
+ contracts: CONTRACT_ADDRESSES[chainId],
189
+ scoringEndpoint: SCORING_ENDPOINTS[chainId]
190
+ };
191
+ }
192
+ function createConfig(chainId, options) {
193
+ const defaultConfig = getDefaultConfig(chainId);
194
+ return {
195
+ ...defaultConfig,
196
+ ...options,
197
+ contracts: {
198
+ ...defaultConfig.contracts,
199
+ ...options?.contracts
200
+ }
201
+ };
202
+ }
203
+
204
+ // src/clients/AgetherClient.ts
24
205
  var AgetherClient = class _AgetherClient {
25
206
  constructor(options) {
26
207
  this.config = options.config;
@@ -198,12 +379,893 @@ var AgetherClient = class _AgetherClient {
198
379
  }
199
380
  };
200
381
 
201
- // src/clients/ScoringClient.ts
382
+ // src/clients/MorphoClient.ts
383
+ import { ethers as ethers2, Contract as Contract2 } from "ethers";
202
384
  import axios from "axios";
385
+ var BASE_COLLATERALS = {
386
+ WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
387
+ wstETH: { address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", symbol: "wstETH", decimals: 18 },
388
+ cbETH: { address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", symbol: "cbETH", decimals: 18 }
389
+ };
390
+ var MORPHO_API_URL = "https://api.morpho.org/graphql";
391
+ var morphoIface = new ethers2.Interface(MORPHO_BLUE_ABI);
392
+ var erc20Iface = new ethers2.Interface(ERC20_ABI);
393
+ var MorphoClient = class {
394
+ constructor(config) {
395
+ this._marketCache = /* @__PURE__ */ new Map();
396
+ this._discoveredAt = 0;
397
+ const chainId = config.chainId ?? 8453 /* Base */;
398
+ const defaultCfg = getDefaultConfig(chainId);
399
+ this.config = defaultCfg;
400
+ this.agentId = config.agentId;
401
+ this.provider = new ethers2.JsonRpcProvider(config.rpcUrl || defaultCfg.rpcUrl);
402
+ this.wallet = new ethers2.Wallet(config.privateKey, this.provider);
403
+ const addrs = { ...defaultCfg.contracts, ...config.contracts };
404
+ this.accountFactory = new Contract2(addrs.accountFactory, ACCOUNT_FACTORY_ABI, this.wallet);
405
+ this.morphoBlue = new Contract2(addrs.morphoBlue, MORPHO_BLUE_ABI, this.provider);
406
+ this.agentReputation = new Contract2(addrs.agentReputation, AGENT_REPUTATION_ABI, this.wallet);
407
+ this.identityRegistry = new Contract2(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this.wallet);
408
+ }
409
+ // ════════════════════════════════════════════════════════
410
+ // Account Management
411
+ // ════════════════════════════════════════════════════════
412
+ /** Resolve the AgentAccount address (cached). */
413
+ async getAccountAddress() {
414
+ if (this._accountAddress) return this._accountAddress;
415
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
416
+ const addr = await this.accountFactory.getAccount(BigInt(this.agentId));
417
+ if (addr === ethers2.ZeroAddress) {
418
+ throw new AgetherError("No AgentAccount found. Call register() first.", "NO_ACCOUNT");
419
+ }
420
+ this._accountAddress = addr;
421
+ return addr;
422
+ }
423
+ getAgentId() {
424
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
425
+ return this.agentId;
426
+ }
427
+ getWalletAddress() {
428
+ return this.wallet.address;
429
+ }
430
+ /** Mint a new ERC-8004 identity and return the agentId. */
431
+ async _mintNewIdentity() {
432
+ const regTx = await this.identityRegistry.register();
433
+ const regReceipt = await regTx.wait();
434
+ let agentId = 0n;
435
+ for (const log of regReceipt.logs) {
436
+ try {
437
+ const parsed = this.identityRegistry.interface.parseLog({ topics: log.topics, data: log.data });
438
+ if (parsed?.name === "Transfer") {
439
+ agentId = parsed.args[2];
440
+ break;
441
+ }
442
+ } catch {
443
+ continue;
444
+ }
445
+ }
446
+ if (agentId === 0n) throw new AgetherError("Failed to parse agentId from registration", "PARSE_ERROR");
447
+ return agentId;
448
+ }
449
+ /**
450
+ * Register: create ERC-8004 identity + AgentAccount in one flow.
451
+ * If already registered, returns existing state.
452
+ */
453
+ async register(_name) {
454
+ const eoaAddr = this.wallet.address;
455
+ if (this.agentId) {
456
+ const exists = await this.accountFactory.accountExists(BigInt(this.agentId));
457
+ if (exists) {
458
+ const acct = await this.accountFactory.getAccount(BigInt(this.agentId));
459
+ this._accountAddress = acct;
460
+ return { agentId: this.agentId, address: eoaAddr, agentAccount: acct, alreadyRegistered: true };
461
+ }
462
+ }
463
+ let agentId;
464
+ if (this.agentId) {
465
+ const balance = await this.identityRegistry.balanceOf(eoaAddr);
466
+ if (balance > 0n) {
467
+ agentId = BigInt(this.agentId);
468
+ } else {
469
+ agentId = await this._mintNewIdentity();
470
+ }
471
+ } else {
472
+ agentId = await this._mintNewIdentity();
473
+ }
474
+ this.agentId = agentId.toString();
475
+ const acctExists = await this.accountFactory.accountExists(agentId);
476
+ let txHash;
477
+ if (!acctExists) {
478
+ const tx = await this.accountFactory.createAccount(agentId);
479
+ const receipt = await tx.wait();
480
+ txHash = receipt.hash;
481
+ }
482
+ const acctAddr = await this.accountFactory.getAccount(agentId);
483
+ this._accountAddress = acctAddr;
484
+ return {
485
+ agentId: this.agentId,
486
+ address: eoaAddr,
487
+ agentAccount: acctAddr,
488
+ alreadyRegistered: acctExists,
489
+ tx: txHash
490
+ };
491
+ }
492
+ /** Get ETH / USDC balances for EOA and AgentAccount. */
493
+ async getBalances() {
494
+ const eoaAddr = this.wallet.address;
495
+ const usdc = new Contract2(this.config.contracts.usdc, ERC20_ABI, this.provider);
496
+ const ethBal = await this.provider.getBalance(eoaAddr);
497
+ const usdcBal = await usdc.balanceOf(eoaAddr);
498
+ const result = {
499
+ agentId: this.agentId || "?",
500
+ address: eoaAddr,
501
+ eth: ethers2.formatEther(ethBal),
502
+ usdc: ethers2.formatUnits(usdcBal, 6)
503
+ };
504
+ try {
505
+ const acctAddr = await this.getAccountAddress();
506
+ const acctEth = await this.provider.getBalance(acctAddr);
507
+ const acctUsdc = await usdc.balanceOf(acctAddr);
508
+ result.agentAccount = {
509
+ address: acctAddr,
510
+ eth: ethers2.formatEther(acctEth),
511
+ usdc: ethers2.formatUnits(acctUsdc, 6)
512
+ };
513
+ } catch {
514
+ }
515
+ return result;
516
+ }
517
+ /** Transfer USDC from EOA to AgentAccount. */
518
+ async fundAccount(usdcAmount) {
519
+ const acctAddr = await this.getAccountAddress();
520
+ const usdc = new Contract2(this.config.contracts.usdc, ERC20_ABI, this.wallet);
521
+ const amount = ethers2.parseUnits(usdcAmount, 6);
522
+ const tx = await usdc.transfer(acctAddr, amount);
523
+ const receipt = await tx.wait();
524
+ return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
525
+ }
526
+ // ════════════════════════════════════════════════════════
527
+ // Market Discovery (Morpho GraphQL API)
528
+ // ════════════════════════════════════════════════════════
529
+ /**
530
+ * Fetch USDC borrow markets on Base from Morpho API.
531
+ * Caches results for 5 minutes.
532
+ */
533
+ async getMarkets(forceRefresh = false) {
534
+ if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
535
+ return this._discoveredMarkets;
536
+ }
537
+ const chainId = this.config.chainId;
538
+ const usdcAddr = this.config.contracts.usdc.toLowerCase();
539
+ const query = `{
540
+ markets(
541
+ first: 50
542
+ orderBy: SupplyAssetsUsd
543
+ orderDirection: Desc
544
+ where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"] }
545
+ ) {
546
+ items {
547
+ uniqueKey
548
+ lltv
549
+ oracleAddress
550
+ irmAddress
551
+ loanAsset { address symbol decimals }
552
+ collateralAsset { address symbol decimals }
553
+ state {
554
+ borrowAssets
555
+ supplyAssets
556
+ utilization
557
+ }
558
+ }
559
+ }
560
+ }`;
561
+ try {
562
+ const resp = await axios.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
563
+ const items = resp.data?.data?.markets?.items ?? [];
564
+ this._discoveredMarkets = items.map((m) => ({
565
+ uniqueKey: m.uniqueKey,
566
+ loanAsset: m.loanAsset,
567
+ collateralAsset: m.collateralAsset ?? { address: ethers2.ZeroAddress, symbol: "N/A", decimals: 0 },
568
+ oracle: m.oracleAddress,
569
+ irm: m.irmAddress,
570
+ lltv: BigInt(m.lltv),
571
+ totalSupplyAssets: BigInt(m.state?.supplyAssets ?? "0"),
572
+ totalBorrowAssets: BigInt(m.state?.borrowAssets ?? "0"),
573
+ utilization: m.state?.utilization ? Number(m.state.utilization) : 0
574
+ }));
575
+ this._discoveredAt = Date.now();
576
+ for (const mi of this._discoveredMarkets) {
577
+ if (mi.collateralAsset.address !== ethers2.ZeroAddress) {
578
+ this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
579
+ loanToken: mi.loanAsset.address,
580
+ collateralToken: mi.collateralAsset.address,
581
+ oracle: mi.oracle,
582
+ irm: mi.irm,
583
+ lltv: mi.lltv
584
+ });
585
+ }
586
+ }
587
+ return this._discoveredMarkets;
588
+ } catch {
589
+ return this._discoveredMarkets ?? [];
590
+ }
591
+ }
592
+ /**
593
+ * Get MarketParams for a collateral token.
594
+ * Tries cache → API → on-chain idToMarketParams.
595
+ */
596
+ async findMarketForCollateral(collateralSymbolOrAddress) {
597
+ const colInfo = BASE_COLLATERALS[collateralSymbolOrAddress];
598
+ const colAddr = (colInfo?.address ?? collateralSymbolOrAddress).toLowerCase();
599
+ const cached = this._marketCache.get(colAddr);
600
+ if (cached) return cached;
601
+ await this.getMarkets();
602
+ const fromApi = this._marketCache.get(colAddr);
603
+ if (fromApi) return fromApi;
604
+ throw new AgetherError(
605
+ `No Morpho market found for collateral ${collateralSymbolOrAddress}`,
606
+ "MARKET_NOT_FOUND"
607
+ );
608
+ }
609
+ /** Read MarketParams on-chain by market ID (bytes32). */
610
+ async getMarketParams(marketId) {
611
+ const result = await this.morphoBlue.idToMarketParams(marketId);
612
+ return {
613
+ loanToken: result.loanToken,
614
+ collateralToken: result.collateralToken,
615
+ oracle: result.oracle,
616
+ irm: result.irm,
617
+ lltv: result.lltv
618
+ };
619
+ }
620
+ // ════════════════════════════════════════════════════════
621
+ // Position Reads
622
+ // ════════════════════════════════════════════════════════
623
+ /** Read on-chain position for a specific market. */
624
+ async getPosition(marketId) {
625
+ const acctAddr = await this.getAccountAddress();
626
+ const pos = await this.morphoBlue.position(marketId, acctAddr);
627
+ return {
628
+ supplyShares: pos.supplyShares,
629
+ borrowShares: pos.borrowShares,
630
+ collateral: pos.collateral
631
+ };
632
+ }
633
+ /**
634
+ * Full status: positions across all discovered markets.
635
+ */
636
+ async getStatus() {
637
+ const acctAddr = await this.getAccountAddress();
638
+ const markets = await this.getMarkets();
639
+ const positions = [];
640
+ let totalDebt = 0n;
641
+ for (const m of markets) {
642
+ if (!m.collateralAsset || m.collateralAsset.address === ethers2.ZeroAddress) continue;
643
+ try {
644
+ const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
645
+ if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
646
+ let debt = 0n;
647
+ if (pos.borrowShares > 0n) {
648
+ try {
649
+ const mkt = await this.morphoBlue.market(m.uniqueKey);
650
+ const totalBorrowShares = BigInt(mkt.totalBorrowShares);
651
+ const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
652
+ debt = totalBorrowShares > 0n ? BigInt(pos.borrowShares) * totalBorrowAssets / totalBorrowShares : 0n;
653
+ totalDebt += debt;
654
+ } catch {
655
+ }
656
+ }
657
+ positions.push({
658
+ marketId: m.uniqueKey,
659
+ collateralToken: m.collateralAsset.symbol,
660
+ collateral: ethers2.formatUnits(pos.collateral, m.collateralAsset.decimals),
661
+ borrowShares: pos.borrowShares.toString(),
662
+ supplyShares: pos.supplyShares.toString(),
663
+ debt: ethers2.formatUnits(debt, 6)
664
+ });
665
+ } catch {
666
+ continue;
667
+ }
668
+ }
669
+ return {
670
+ agentId: this.agentId || "?",
671
+ agentAccount: acctAddr,
672
+ totalDebt: ethers2.formatUnits(totalDebt, 6),
673
+ positions
674
+ };
675
+ }
676
+ // ════════════════════════════════════════════════════════
677
+ // Lending Operations (all via AgentAccount.executeBatch)
678
+ // ════════════════════════════════════════════════════════
679
+ /**
680
+ * Deposit collateral into Morpho Blue.
681
+ *
682
+ * Flow:
683
+ * 1. EOA transfers collateral to AgentAccount
684
+ * 2. AgentAccount.executeBatch:
685
+ * [collateral.approve(MorphoBlue), Morpho.supplyCollateral(params)]
686
+ */
687
+ async supplyCollateral(tokenSymbol, amount, marketParams) {
688
+ const acctAddr = await this.getAccountAddress();
689
+ const colInfo = BASE_COLLATERALS[tokenSymbol];
690
+ if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
691
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
692
+ const weiAmount = ethers2.parseUnits(amount, colInfo.decimals);
693
+ const morphoAddr = this.config.contracts.morphoBlue;
694
+ const colToken = new Contract2(colInfo.address, ERC20_ABI, this.wallet);
695
+ const transferTx = await colToken.transfer(acctAddr, weiAmount);
696
+ await transferTx.wait();
697
+ const targets = [colInfo.address, morphoAddr];
698
+ const values = [0n, 0n];
699
+ const datas = [
700
+ erc20Iface.encodeFunctionData("approve", [morphoAddr, weiAmount]),
701
+ morphoIface.encodeFunctionData("supplyCollateral", [
702
+ this._toTuple(params),
703
+ weiAmount,
704
+ acctAddr,
705
+ "0x"
706
+ ])
707
+ ];
708
+ const receipt = await this.batch(targets, values, datas);
709
+ return {
710
+ tx: receipt.hash,
711
+ collateralToken: tokenSymbol,
712
+ amount,
713
+ agentAccount: acctAddr
714
+ };
715
+ }
716
+ /**
717
+ * Borrow USDC against existing collateral.
718
+ *
719
+ * AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
720
+ *
721
+ * @param usdcAmount - USDC amount (e.g. '100')
722
+ * @param tokenSymbol - collateral symbol to identify which market (default: first with collateral)
723
+ */
724
+ async borrow(usdcAmount, tokenSymbol, marketParams) {
725
+ const acctAddr = await this.getAccountAddress();
726
+ const amount = ethers2.parseUnits(usdcAmount, 6);
727
+ const morphoAddr = this.config.contracts.morphoBlue;
728
+ let params;
729
+ let usedToken = tokenSymbol || "WETH";
730
+ if (marketParams) {
731
+ params = marketParams;
732
+ } else if (tokenSymbol) {
733
+ params = await this.findMarketForCollateral(tokenSymbol);
734
+ } else {
735
+ const { params: p, symbol } = await this._findActiveMarket();
736
+ params = p;
737
+ usedToken = symbol;
738
+ }
739
+ const data = morphoIface.encodeFunctionData("borrow", [
740
+ this._toTuple(params),
741
+ amount,
742
+ 0n,
743
+ acctAddr,
744
+ acctAddr
745
+ ]);
746
+ const receipt = await this.exec(morphoAddr, data);
747
+ return {
748
+ tx: receipt.hash,
749
+ amount: usdcAmount,
750
+ collateralToken: usedToken,
751
+ agentAccount: acctAddr
752
+ };
753
+ }
754
+ /**
755
+ * Deposit collateral AND borrow USDC in one batched transaction.
756
+ *
757
+ * AgentAccount.executeBatch:
758
+ * [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
759
+ *
760
+ * The collateral must be transferred to AgentAccount first.
761
+ */
762
+ async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
763
+ const acctAddr = await this.getAccountAddress();
764
+ const colInfo = BASE_COLLATERALS[tokenSymbol];
765
+ if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
766
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
767
+ const colWei = ethers2.parseUnits(collateralAmount, colInfo.decimals);
768
+ const borrowWei = ethers2.parseUnits(borrowUsdcAmount, 6);
769
+ const morphoAddr = this.config.contracts.morphoBlue;
770
+ const colToken = new Contract2(colInfo.address, ERC20_ABI, this.wallet);
771
+ const transferTx = await colToken.transfer(acctAddr, colWei);
772
+ await transferTx.wait();
773
+ const targets = [colInfo.address, morphoAddr, morphoAddr];
774
+ const values = [0n, 0n, 0n];
775
+ const datas = [
776
+ erc20Iface.encodeFunctionData("approve", [morphoAddr, colWei]),
777
+ morphoIface.encodeFunctionData("supplyCollateral", [
778
+ this._toTuple(params),
779
+ colWei,
780
+ acctAddr,
781
+ "0x"
782
+ ]),
783
+ morphoIface.encodeFunctionData("borrow", [
784
+ this._toTuple(params),
785
+ borrowWei,
786
+ 0n,
787
+ acctAddr,
788
+ acctAddr
789
+ ])
790
+ ];
791
+ const receipt = await this.batch(targets, values, datas);
792
+ return {
793
+ tx: receipt.hash,
794
+ collateralToken: tokenSymbol,
795
+ collateralAmount,
796
+ borrowAmount: borrowUsdcAmount,
797
+ agentAccount: acctAddr
798
+ };
799
+ }
800
+ /**
801
+ * Repay borrowed USDC from AgentAccount.
802
+ *
803
+ * AgentAccount.executeBatch:
804
+ * [USDC.approve(MorphoBlue), Morpho.repay(params)]
805
+ */
806
+ async repay(usdcAmount, tokenSymbol, marketParams) {
807
+ const acctAddr = await this.getAccountAddress();
808
+ const amount = ethers2.parseUnits(usdcAmount, 6);
809
+ const morphoAddr = this.config.contracts.morphoBlue;
810
+ const usdcAddr = this.config.contracts.usdc;
811
+ let params;
812
+ if (marketParams) {
813
+ params = marketParams;
814
+ } else if (tokenSymbol) {
815
+ params = await this.findMarketForCollateral(tokenSymbol);
816
+ } else {
817
+ const { params: p } = await this._findActiveMarket();
818
+ params = p;
819
+ }
820
+ const targets = [usdcAddr, morphoAddr];
821
+ const values = [0n, 0n];
822
+ const datas = [
823
+ erc20Iface.encodeFunctionData("approve", [morphoAddr, amount]),
824
+ morphoIface.encodeFunctionData("repay", [
825
+ this._toTuple(params),
826
+ amount,
827
+ 0n,
828
+ acctAddr,
829
+ "0x"
830
+ ])
831
+ ];
832
+ const receipt = await this.batch(targets, values, datas);
833
+ let remainingDebt = "0";
834
+ try {
835
+ const status = await this.getStatus();
836
+ remainingDebt = status.totalDebt;
837
+ } catch {
838
+ }
839
+ return { tx: receipt.hash, amount: usdcAmount, remainingDebt };
840
+ }
841
+ /**
842
+ * Withdraw collateral from Morpho Blue.
843
+ *
844
+ * AgentAccount.execute: Morpho.withdrawCollateral(params, amount, account, receiver)
845
+ *
846
+ * @param receiver - defaults to EOA wallet
847
+ */
848
+ async withdrawCollateral(tokenSymbol, amount, marketParams, receiver) {
849
+ const acctAddr = await this.getAccountAddress();
850
+ const colInfo = BASE_COLLATERALS[tokenSymbol];
851
+ if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
852
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
853
+ const morphoAddr = this.config.contracts.morphoBlue;
854
+ const dest = receiver || this.wallet.address;
855
+ let weiAmount;
856
+ if (amount === "all") {
857
+ const markets = await this.getMarkets();
858
+ const market = markets.find(
859
+ (m) => m.collateralAsset.address.toLowerCase() === colInfo.address.toLowerCase()
860
+ );
861
+ if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
862
+ const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
863
+ weiAmount = pos.collateral;
864
+ if (weiAmount === 0n) throw new AgetherError("No collateral to withdraw", "NO_COLLATERAL");
865
+ } else {
866
+ weiAmount = ethers2.parseUnits(amount, colInfo.decimals);
867
+ }
868
+ const data = morphoIface.encodeFunctionData("withdrawCollateral", [
869
+ this._toTuple(params),
870
+ weiAmount,
871
+ acctAddr,
872
+ dest
873
+ ]);
874
+ const receipt = await this.exec(morphoAddr, data);
875
+ let remainingCollateral = "0";
876
+ try {
877
+ const markets = await this.getMarkets();
878
+ const market = markets.find(
879
+ (m) => m.collateralAsset.address.toLowerCase() === colInfo.address.toLowerCase()
880
+ );
881
+ if (market) {
882
+ const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
883
+ remainingCollateral = ethers2.formatUnits(pos.collateral, colInfo.decimals);
884
+ }
885
+ } catch {
886
+ }
887
+ return {
888
+ tx: receipt.hash,
889
+ token: tokenSymbol,
890
+ amount: amount === "all" ? ethers2.formatUnits(weiAmount, colInfo.decimals) : amount,
891
+ remainingCollateral,
892
+ destination: dest
893
+ };
894
+ }
895
+ /**
896
+ * Sponsor: transfer collateral to another agent's AgentAccount.
897
+ * (The agent must then supplyCollateral themselves via their own account.)
898
+ */
899
+ async sponsor(target, tokenSymbol, amount) {
900
+ const colInfo = BASE_COLLATERALS[tokenSymbol];
901
+ if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
902
+ let targetAddr;
903
+ if (target.address) {
904
+ targetAddr = target.address;
905
+ } else if (target.agentId) {
906
+ targetAddr = await this.accountFactory.getAccount(BigInt(target.agentId));
907
+ if (targetAddr === ethers2.ZeroAddress) throw new AgetherError("Target agent has no account", "NO_ACCOUNT");
908
+ } else {
909
+ throw new AgetherError("Provide agentId or address", "INVALID_TARGET");
910
+ }
911
+ const weiAmount = ethers2.parseUnits(amount, colInfo.decimals);
912
+ const colToken = new Contract2(colInfo.address, ERC20_ABI, this.wallet);
913
+ const tx = await colToken.transfer(targetAddr, weiAmount);
914
+ const receipt = await tx.wait();
915
+ return { tx: receipt.hash, targetAccount: targetAddr, targetAgentId: target.agentId };
916
+ }
917
+ // ════════════════════════════════════════════════════════
918
+ // Reputation (AgentReputation contract)
919
+ // ════════════════════════════════════════════════════════
920
+ async getCreditScore() {
921
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
922
+ return this.agentReputation.getCreditScore(BigInt(this.agentId));
923
+ }
924
+ async getAttestation() {
925
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
926
+ const att = await this.agentReputation.getAttestation(BigInt(this.agentId));
927
+ return { score: att.score, timestamp: att.timestamp, signer: att.signer };
928
+ }
929
+ async isEligible(minScore = 500n) {
930
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
931
+ const [eligible, currentScore] = await this.agentReputation.isEligible(BigInt(this.agentId), minScore);
932
+ return { eligible, currentScore };
933
+ }
934
+ async isScoreFresh() {
935
+ if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
936
+ const [fresh, age] = await this.agentReputation.isScoreFresh(BigInt(this.agentId));
937
+ return { fresh, age };
938
+ }
939
+ // ════════════════════════════════════════════════════════
940
+ // Internal Helpers
941
+ // ════════════════════════════════════════════════════════
942
+ /**
943
+ * Execute a single call via AgentAccount.execute.
944
+ */
945
+ async exec(target, data, value = 0n) {
946
+ const acctAddr = await this.getAccountAddress();
947
+ const account = new Contract2(acctAddr, AGENT_ACCOUNT_ABI, this.wallet);
948
+ let gasLimit;
949
+ try {
950
+ const estimate = await account.execute.estimateGas(target, value, data);
951
+ gasLimit = estimate * 130n / 100n;
952
+ } catch {
953
+ gasLimit = 500000n;
954
+ }
955
+ const tx = await account.execute(target, value, data, { gasLimit });
956
+ return tx.wait();
957
+ }
958
+ /**
959
+ * Execute multiple calls via AgentAccount.executeBatch.
960
+ */
961
+ async batch(targets, values, datas) {
962
+ const acctAddr = await this.getAccountAddress();
963
+ const account = new Contract2(acctAddr, AGENT_ACCOUNT_ABI, this.wallet);
964
+ let gasLimit;
965
+ try {
966
+ const estimate = await account.executeBatch.estimateGas(targets, values, datas);
967
+ gasLimit = estimate * 130n / 100n;
968
+ } catch {
969
+ gasLimit = 800000n;
970
+ }
971
+ const tx = await account.executeBatch(targets, values, datas, { gasLimit });
972
+ return tx.wait();
973
+ }
974
+ /** Convert MorphoMarketParams to Solidity tuple. */
975
+ _toTuple(p) {
976
+ return [p.loanToken, p.collateralToken, p.oracle, p.irm, p.lltv];
977
+ }
978
+ /** Find the first market where the agent has collateral deposited. */
979
+ async _findActiveMarket() {
980
+ const acctAddr = await this.getAccountAddress();
981
+ const markets = await this.getMarkets();
982
+ for (const m of markets) {
983
+ if (!m.collateralAsset || m.collateralAsset.address === ethers2.ZeroAddress) continue;
984
+ try {
985
+ const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
986
+ if (pos.collateral > 0n) {
987
+ return {
988
+ params: {
989
+ loanToken: m.loanAsset.address,
990
+ collateralToken: m.collateralAsset.address,
991
+ oracle: m.oracle,
992
+ irm: m.irm,
993
+ lltv: m.lltv
994
+ },
995
+ symbol: m.collateralAsset.symbol
996
+ };
997
+ }
998
+ } catch {
999
+ continue;
1000
+ }
1001
+ }
1002
+ const params = await this.findMarketForCollateral("WETH");
1003
+ return { params, symbol: "WETH" };
1004
+ }
1005
+ };
1006
+
1007
+ // src/clients/ScoringClient.ts
1008
+ import axios2 from "axios";
1009
+
1010
+ // src/clients/X402Client.ts
1011
+ import { ethers as ethers3 } from "ethers";
1012
+ var USDC_DOMAINS = {
1013
+ "eip155:1": { name: "USD Coin", version: "2", address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
1014
+ "eip155:8453": { name: "USD Coin", version: "2", address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
1015
+ "eip155:84532": { name: "USD Coin", version: "2", address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e" },
1016
+ "eip155:42161": { name: "USD Coin", version: "2", address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" },
1017
+ "eip155:10": { name: "USD Coin", version: "2", address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85" }
1018
+ };
1019
+ function chainIdFromNetwork(network) {
1020
+ const m = network.match(/^eip155:(\d+)$/);
1021
+ return m ? Number(m[1]) : 1;
1022
+ }
1023
+ var X402Client = class {
1024
+ constructor(config) {
1025
+ this.config = config;
1026
+ const provider = new ethers3.JsonRpcProvider(config.rpcUrl);
1027
+ this.wallet = new ethers3.Wallet(config.privateKey, provider);
1028
+ }
1029
+ async get(url, opts) {
1030
+ return this.request(url, { ...opts, method: "GET" });
1031
+ }
1032
+ async post(url, body, opts) {
1033
+ return this.request(url, {
1034
+ ...opts,
1035
+ method: "POST",
1036
+ body: body ? JSON.stringify(body) : void 0,
1037
+ headers: { "Content-Type": "application/json", ...opts?.headers }
1038
+ });
1039
+ }
1040
+ getAddress() {
1041
+ return this.wallet.address;
1042
+ }
1043
+ // ──────────── Core request / 402-retry loop ────────────
1044
+ async request(url, options) {
1045
+ try {
1046
+ console.log(" [1/4] Calling resource server\u2026");
1047
+ const response = await fetch(url, {
1048
+ ...options,
1049
+ headers: {
1050
+ ...options?.headers,
1051
+ "X-Agent-Id": this.config.agentId || ""
1052
+ }
1053
+ });
1054
+ if (response.ok) {
1055
+ const data = await response.json();
1056
+ return { success: true, data };
1057
+ }
1058
+ if (response.status !== 402) {
1059
+ return { success: false, error: `HTTP ${response.status}: ${await response.text()}` };
1060
+ }
1061
+ console.log(" [2/4] 402 received \u2014 parsing payment requirements\u2026");
1062
+ const parsed = await this.parsePaymentRequired(response);
1063
+ if (!parsed) {
1064
+ return { success: false, error: "Could not parse payment requirements from 402 response" };
1065
+ }
1066
+ const { requirements, resource } = parsed;
1067
+ console.log(` scheme : ${requirements.scheme}`);
1068
+ console.log(` network : ${requirements.network}`);
1069
+ console.log(` amount : ${requirements.amount} (atomic)`);
1070
+ console.log(` asset : ${requirements.asset}`);
1071
+ console.log(` payTo : ${requirements.payTo}`);
1072
+ console.log(" [3/4] Signing EIP-3009 transferWithAuthorization\u2026");
1073
+ const paymentPayload = await this.buildPaymentPayload(requirements, resource, url);
1074
+ const paymentB64 = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
1075
+ await this.riskCheck(paymentPayload, requirements);
1076
+ console.log(" [4/4] Retrying with PAYMENT-SIGNATURE header\u2026");
1077
+ const paidResponse = await fetch(url, {
1078
+ ...options,
1079
+ headers: {
1080
+ ...options?.headers,
1081
+ "X-Agent-Id": this.config.agentId || "",
1082
+ // v2 header
1083
+ "PAYMENT-SIGNATURE": paymentB64,
1084
+ // v1 compat header (some servers still use this)
1085
+ "X-PAYMENT": paymentB64
1086
+ }
1087
+ });
1088
+ if (paidResponse.ok) {
1089
+ const data = await paidResponse.json();
1090
+ const settlementHeader = paidResponse.headers.get("PAYMENT-RESPONSE") || paidResponse.headers.get("X-PAYMENT-RESPONSE");
1091
+ let txHash;
1092
+ if (settlementHeader) {
1093
+ try {
1094
+ const settlement = JSON.parse(Buffer.from(settlementHeader, "base64").toString("utf-8"));
1095
+ txHash = settlement.transaction;
1096
+ } catch {
1097
+ }
1098
+ }
1099
+ return {
1100
+ success: true,
1101
+ data,
1102
+ paymentInfo: {
1103
+ amount: requirements.amount,
1104
+ asset: requirements.extra?.name || "USDC",
1105
+ network: requirements.network,
1106
+ txHash
1107
+ }
1108
+ };
1109
+ }
1110
+ const errBody = await paidResponse.text();
1111
+ return { success: false, error: `Payment rejected (HTTP ${paidResponse.status}): ${errBody}` };
1112
+ } catch (error) {
1113
+ return { success: false, error: `Request failed: ${error instanceof Error ? error.message : String(error)}` };
1114
+ }
1115
+ }
1116
+ // ──────────── Parse 402 response ────────────
1117
+ async parsePaymentRequired(response) {
1118
+ const prHeader = response.headers.get("PAYMENT-REQUIRED") || response.headers.get("x-payment-required");
1119
+ if (prHeader) {
1120
+ try {
1121
+ const decoded = JSON.parse(Buffer.from(prHeader, "base64").toString("utf-8"));
1122
+ if (decoded.accepts?.length) {
1123
+ return { requirements: decoded.accepts[0], resource: decoded.resource };
1124
+ }
1125
+ } catch {
1126
+ }
1127
+ }
1128
+ try {
1129
+ const body = await response.json();
1130
+ if (body.accepts && Array.isArray(body.accepts) && body.accepts.length > 0) {
1131
+ return { requirements: body.accepts[0], resource: body.resource };
1132
+ }
1133
+ if (body.paymentRequirements) {
1134
+ const pr = Array.isArray(body.paymentRequirements) ? body.paymentRequirements[0] : body.paymentRequirements;
1135
+ return { requirements: pr, resource: body.resource };
1136
+ }
1137
+ if (body.scheme && body.network) {
1138
+ return { requirements: body, resource: body.resource };
1139
+ }
1140
+ } catch {
1141
+ }
1142
+ const wwwAuth = response.headers.get("WWW-Authenticate");
1143
+ if (wwwAuth) {
1144
+ const m = wwwAuth.match(/x402[^"]*"([^"]+)"/);
1145
+ if (m) {
1146
+ try {
1147
+ const decoded = JSON.parse(Buffer.from(m[1], "base64").toString("utf-8"));
1148
+ const reqs = Array.isArray(decoded) ? decoded[0] : decoded;
1149
+ return { requirements: reqs };
1150
+ } catch {
1151
+ }
1152
+ }
1153
+ }
1154
+ return null;
1155
+ }
1156
+ // ──────────── Build x402 v2 PaymentPayload with EIP-3009 ────────────
1157
+ //
1158
+ // If an AgentAccount is configured, we use it as the `from` address
1159
+ // (smart wallet pays directly). The AgentAccount implements EIP-1271
1160
+ // so USDC's transferWithAuthorization will call isValidSignature()
1161
+ // to verify the owner's ECDSA signature. The facilitator detects
1162
+ // the >65-byte or smart-wallet case and uses the bytes overload.
1163
+ async buildPaymentPayload(reqs, resource, url) {
1164
+ const now = Math.floor(Date.now() / 1e3);
1165
+ const validAfter = String(now - 60);
1166
+ const validBefore = String(now + (reqs.maxTimeoutSeconds || 300));
1167
+ const nonce = ethers3.hexlify(ethers3.randomBytes(32));
1168
+ const chainId = chainIdFromNetwork(reqs.network);
1169
+ const usdcDomain = USDC_DOMAINS[reqs.network] || USDC_DOMAINS["eip155:1"];
1170
+ const payerAddress = this.config.accountAddress || this.wallet.address;
1171
+ const isSmartWallet = !!this.config.accountAddress;
1172
+ const domain = {
1173
+ name: usdcDomain.name,
1174
+ version: usdcDomain.version,
1175
+ chainId,
1176
+ verifyingContract: reqs.asset || usdcDomain.address
1177
+ };
1178
+ const types = {
1179
+ TransferWithAuthorization: [
1180
+ { name: "from", type: "address" },
1181
+ { name: "to", type: "address" },
1182
+ { name: "value", type: "uint256" },
1183
+ { name: "validAfter", type: "uint256" },
1184
+ { name: "validBefore", type: "uint256" },
1185
+ { name: "nonce", type: "bytes32" }
1186
+ ]
1187
+ };
1188
+ const value = {
1189
+ from: payerAddress,
1190
+ // AgentAccount or EOA
1191
+ to: reqs.payTo,
1192
+ value: reqs.amount,
1193
+ validAfter,
1194
+ validBefore,
1195
+ nonce
1196
+ };
1197
+ let signature = await this.wallet.signTypedData(domain, types, value);
1198
+ if (isSmartWallet) {
1199
+ signature = signature + "00";
1200
+ }
1201
+ if (isSmartWallet) {
1202
+ console.log(` \u2713 Signed for AgentAccount ${payerAddress.slice(0, 10)}\u2026 (EIP-1271, chain=${chainId})`);
1203
+ } else {
1204
+ console.log(` \u2713 Signed (from=${payerAddress.slice(0, 10)}\u2026, chain=${chainId})`);
1205
+ }
1206
+ return {
1207
+ x402Version: 2,
1208
+ resource: resource || { url, description: "", mimeType: "application/json" },
1209
+ accepted: {
1210
+ scheme: reqs.scheme,
1211
+ network: reqs.network,
1212
+ amount: reqs.amount,
1213
+ asset: reqs.asset,
1214
+ payTo: reqs.payTo,
1215
+ maxTimeoutSeconds: reqs.maxTimeoutSeconds,
1216
+ extra: reqs.extra || {}
1217
+ },
1218
+ payload: {
1219
+ signature,
1220
+ authorization: {
1221
+ from: payerAddress,
1222
+ // AgentAccount address — facilitator checks balance here
1223
+ to: reqs.payTo,
1224
+ value: reqs.amount,
1225
+ validAfter,
1226
+ validBefore,
1227
+ nonce
1228
+ }
1229
+ }
1230
+ };
1231
+ }
1232
+ // ──────────── Risk check via our backend ────────────
1233
+ async riskCheck(paymentPayload, reqs) {
1234
+ try {
1235
+ const verifyUrl = `${this.config.backendUrl}/x402/verify`;
1236
+ const resp = await fetch(verifyUrl, {
1237
+ method: "POST",
1238
+ headers: {
1239
+ "Content-Type": "application/json",
1240
+ "X-Agent-Id": this.config.agentId || "",
1241
+ ...this.config.accountAddress ? { "X-Agent-Account": this.config.accountAddress } : {}
1242
+ },
1243
+ body: JSON.stringify({
1244
+ x402Version: 2,
1245
+ paymentPayload,
1246
+ paymentRequirements: reqs
1247
+ }),
1248
+ signal: AbortSignal.timeout(5e3)
1249
+ });
1250
+ if (resp.ok) {
1251
+ const result = await resp.json();
1252
+ const decision = resp.headers.get("X-Risk-Decision") || (result.isValid ? "allow" : "unknown");
1253
+ const score = resp.headers.get("X-Risk-Score") || "?";
1254
+ console.log(` \u2713 Risk check: ${decision} (score=${score})`);
1255
+ } else {
1256
+ console.log(` \u26A0 Risk check failed (HTTP ${resp.status}) \u2014 continuing anyway`);
1257
+ }
1258
+ } catch {
1259
+ console.log(" \u26A0 Risk check unavailable \u2014 continuing");
1260
+ }
1261
+ }
1262
+ };
1263
+
1264
+ // src/clients/ScoringClient.ts
203
1265
  var ScoringClient = class {
204
1266
  constructor(config) {
205
1267
  this.endpoint = config.endpoint;
206
- this.client = axios.create({
1268
+ this.client = axios2.create({
207
1269
  baseURL: config.endpoint,
208
1270
  headers: { "Content-Type": "application/json" },
209
1271
  timeout: 3e4
@@ -290,7 +1352,7 @@ var ScoringClient = class {
290
1352
  };
291
1353
 
292
1354
  // src/clients/AgentIdentityClient.ts
293
- import { ethers as ethers2 } from "ethers";
1355
+ import { ethers as ethers4 } from "ethers";
294
1356
  var ERC8004_IDENTITY_ABI = [
295
1357
  // Registration
296
1358
  "function register() returns (uint256)",
@@ -332,8 +1394,8 @@ var AgentIdentityClient = class {
332
1394
  this.signer = options.signer;
333
1395
  const identityAddr = options.config.contracts?.identityRegistry || ERC8004_SEPOLIA.identityRegistry;
334
1396
  const reputationAddr = options.config.contracts?.reputationRegistry || ERC8004_SEPOLIA.reputationRegistry;
335
- this.identityRegistry = new ethers2.Contract(identityAddr, ERC8004_IDENTITY_ABI, this.signer);
336
- this.reputationRegistry = new ethers2.Contract(reputationAddr, ERC8004_REPUTATION_ABI, this.signer);
1397
+ this.identityRegistry = new ethers4.Contract(identityAddr, ERC8004_IDENTITY_ABI, this.signer);
1398
+ this.reputationRegistry = new ethers4.Contract(reputationAddr, ERC8004_REPUTATION_ABI, this.signer);
337
1399
  }
338
1400
  // ============ Identity Functions ============
339
1401
  /**
@@ -400,7 +1462,7 @@ var AgentIdentityClient = class {
400
1462
  async registerWithMetadata(agentURI, metadata) {
401
1463
  const metadataEntries = metadata.map((m) => ({
402
1464
  key: m.key,
403
- value: ethers2.toUtf8Bytes(m.value)
1465
+ value: ethers4.toUtf8Bytes(m.value)
404
1466
  }));
405
1467
  const tx = await this.identityRegistry["register(string,tuple(string,bytes)[])"](
406
1468
  agentURI,
@@ -437,7 +1499,7 @@ var AgentIdentityClient = class {
437
1499
  const tx = await this.identityRegistry.setMetadata(
438
1500
  agentId,
439
1501
  key,
440
- ethers2.toUtf8Bytes(value)
1502
+ ethers4.toUtf8Bytes(value)
441
1503
  );
442
1504
  const receipt = await tx.wait();
443
1505
  return receipt.hash;
@@ -447,7 +1509,7 @@ var AgentIdentityClient = class {
447
1509
  */
448
1510
  async getMetadata(agentId, key) {
449
1511
  const value = await this.identityRegistry.getMetadata(agentId, key);
450
- return ethers2.toUtf8String(value);
1512
+ return ethers4.toUtf8String(value);
451
1513
  }
452
1514
  /**
453
1515
  * Transfer agent to new owner
@@ -481,8 +1543,8 @@ var AgentIdentityClient = class {
481
1543
  * Give feedback to an agent
482
1544
  */
483
1545
  async giveFeedback(input) {
484
- const feedbackHash = ethers2.keccak256(
485
- ethers2.AbiCoder.defaultAbiCoder().encode(
1546
+ const feedbackHash = ethers4.keccak256(
1547
+ ethers4.AbiCoder.defaultAbiCoder().encode(
486
1548
  ["uint256", "int128", "uint256"],
487
1549
  [input.agentId, input.value, Date.now()]
488
1550
  )