@agether/sdk 2.15.1 → 2.16.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/cli.js CHANGED
@@ -30,6 +30,57 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  mod
31
31
  ));
32
32
 
33
+ // src/utils/retry.ts
34
+ function isRetriable(error) {
35
+ const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
36
+ return RETRIABLE_PATTERNS.some((p) => msg.includes(p.toLowerCase()));
37
+ }
38
+ async function withRetry(fn, options = {}) {
39
+ const {
40
+ maxRetries = 3,
41
+ baseDelay = 1e3,
42
+ maxDelay = 15e3,
43
+ onRetry
44
+ } = options;
45
+ let lastError;
46
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
47
+ try {
48
+ return await fn();
49
+ } catch (error) {
50
+ lastError = error instanceof Error ? error : new Error(String(error));
51
+ if (attempt >= maxRetries || !isRetriable(error)) {
52
+ throw lastError;
53
+ }
54
+ const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
55
+ const jitter = delay * (0.5 + Math.random() * 0.5);
56
+ onRetry?.(attempt, lastError);
57
+ await new Promise((resolve) => setTimeout(resolve, jitter));
58
+ }
59
+ }
60
+ throw lastError;
61
+ }
62
+ var RETRIABLE_PATTERNS;
63
+ var init_retry = __esm({
64
+ "src/utils/retry.ts"() {
65
+ "use strict";
66
+ RETRIABLE_PATTERNS = [
67
+ "ECONNRESET",
68
+ "ECONNREFUSED",
69
+ "ENOTFOUND",
70
+ "ETIMEDOUT",
71
+ "fetch failed",
72
+ "network error",
73
+ "socket hang up",
74
+ "rate limit",
75
+ "429",
76
+ "502",
77
+ "503",
78
+ "504",
79
+ "timeout"
80
+ ];
81
+ }
82
+ });
83
+
33
84
  // src/types/index.ts
34
85
  var AgetherError;
35
86
  var init_types = __esm({
@@ -280,6 +331,7 @@ var init_MorphoClient = __esm({
280
331
  "use strict";
281
332
  import_ethers = require("ethers");
282
333
  import_axios = __toESM(require("axios"));
334
+ init_retry();
283
335
  init_types();
284
336
  init_abis();
285
337
  init_config();
@@ -429,7 +481,10 @@ var init_MorphoClient = __esm({
429
481
  }
430
482
  }`;
431
483
  try {
432
- const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
484
+ const resp = await withRetry(
485
+ () => import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 }),
486
+ { maxRetries: 3, onRetry: (n, e) => console.warn(`[agether] Morpho API retry ${n}:`, e.message) }
487
+ );
433
488
  const items = resp.data?.data?.markets?.items ?? [];
434
489
  this._discoveredMarkets = items.map((m) => ({
435
490
  uniqueKey: m.uniqueKey,
@@ -613,7 +668,10 @@ var init_MorphoClient = __esm({
613
668
  }
614
669
  }
615
670
  }`;
616
- const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 15e3 });
671
+ const resp = await withRetry(
672
+ () => import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 15e3 }),
673
+ { maxRetries: 3, onRetry: (n, e) => console.warn(`[agether] Morpho position query retry ${n}:`, e.message) }
674
+ );
617
675
  const items = resp.data?.data?.marketPositions?.items ?? [];
618
676
  for (const item of items) {
619
677
  const supplyShares = BigInt(item.supplyShares ?? "0");
@@ -832,7 +890,10 @@ var init_MorphoClient = __esm({
832
890
  }
833
891
  }`;
834
892
  try {
835
- const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
893
+ const resp = await withRetry(
894
+ () => import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 }),
895
+ { maxRetries: 3, onRetry: (n, e) => console.warn(`[agether] Morpho API retry ${n}:`, e.message) }
896
+ );
836
897
  let items = resp.data?.data?.markets?.items ?? [];
837
898
  if (searchTerm && collateralSymbolOrAddress && !collateralSymbolOrAddress.startsWith("0x")) {
838
899
  const sym = collateralSymbolOrAddress.toUpperCase();
@@ -898,7 +959,10 @@ var init_MorphoClient = __esm({
898
959
  }
899
960
  }`;
900
961
  try {
901
- const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
962
+ const resp = await withRetry(
963
+ () => import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 }),
964
+ { maxRetries: 3, onRetry: (n, e) => console.warn(`[agether] Morpho API retry ${n}:`, e.message) }
965
+ );
902
966
  let items = resp.data?.data?.markets?.items ?? [];
903
967
  items = items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers.ethers.ZeroAddress);
904
968
  const searchUpper = search.toUpperCase();
@@ -2493,13 +2557,64 @@ var init_AgetherClient = __esm({
2493
2557
  return this.agether4337Factory.accountExists(id);
2494
2558
  }
2495
2559
  // ════════════════════════════════════════════════════════
2560
+ // Token Discovery (for dynamic balance queries)
2561
+ // ════════════════════════════════════════════════════════
2562
+ /**
2563
+ * Discover token addresses from active Morpho Blue positions.
2564
+ * Queries the Morpho GraphQL API for markets involving this agent's account,
2565
+ * then extracts collateral and loan token info.
2566
+ */
2567
+ async _discoverPositionTokens() {
2568
+ const tokens = {};
2569
+ try {
2570
+ const acctAddr = await this.getAccountAddress();
2571
+ const chainId = this.config.chainId;
2572
+ const query = `{
2573
+ marketPositions(
2574
+ where: { userAddress_in: ["${acctAddr.toLowerCase()}"], chainId_in: [${chainId}] }
2575
+ first: 20
2576
+ ) {
2577
+ items {
2578
+ market {
2579
+ collateralAsset { address symbol decimals }
2580
+ loanAsset { address symbol decimals }
2581
+ }
2582
+ }
2583
+ }
2584
+ }`;
2585
+ const resp = await fetch("https://blue-api.morpho.org/graphql", {
2586
+ method: "POST",
2587
+ headers: { "Content-Type": "application/json" },
2588
+ body: JSON.stringify({ query }),
2589
+ signal: AbortSignal.timeout(5e3)
2590
+ });
2591
+ if (!resp.ok) return tokens;
2592
+ const data = await resp.json();
2593
+ const items = data?.data?.marketPositions?.items ?? [];
2594
+ for (const item of items) {
2595
+ const col = item?.market?.collateralAsset;
2596
+ const loan = item?.market?.loanAsset;
2597
+ if (col?.symbol && col?.address) {
2598
+ tokens[col.symbol] = { address: col.address, symbol: col.symbol, decimals: col.decimals ?? 18 };
2599
+ }
2600
+ if (loan?.symbol && loan?.address) {
2601
+ tokens[loan.symbol] = { address: loan.address, symbol: loan.symbol, decimals: loan.decimals ?? 18 };
2602
+ }
2603
+ }
2604
+ } catch {
2605
+ }
2606
+ return tokens;
2607
+ }
2608
+ // ════════════════════════════════════════════════════════
2496
2609
  // Balances
2497
2610
  // ════════════════════════════════════════════════════════
2498
2611
  /**
2499
- * Get ETH, USDC, and collateral token balances for EOA and Safe account.
2612
+ * Get ETH, USDC, and all token balances for EOA and Safe account.
2500
2613
  *
2501
- * Collateral tokens are resolved from a built-in registry of well-known
2502
- * tokens per chain (WETH, wstETH, cbETH).
2614
+ * Tokens are resolved from:
2615
+ * 1. Built-in registry of well-known tokens (WETH, wstETH, cbETH)
2616
+ * 2. Dynamic discovery from active Morpho positions (loan + collateral tokens)
2617
+ * This ensures tokens like LCAP or any new Morpho market tokens appear in balances.
2503
2618
  */
2504
2619
  async getBalances() {
2505
2620
  const provider = this.signer.provider;
@@ -2507,7 +2622,18 @@ var init_AgetherClient = __esm({
2507
2622
  const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, provider);
2508
2623
  const ethBal = await provider.getBalance(eoaAddr);
2509
2624
  const usdcBal = await usdc.balanceOf(eoaAddr);
2510
- const knownTokens = KNOWN_TOKENS[this.config.chainId] ?? {};
2625
+ const knownTokens = {
2626
+ ...KNOWN_TOKENS[this.config.chainId] ?? {}
2627
+ };
2628
+ try {
2629
+ const positions = await this._discoverPositionTokens();
2630
+ for (const [symbol, info] of Object.entries(positions)) {
2631
+ if (!knownTokens[symbol]) {
2632
+ knownTokens[symbol] = info;
2633
+ }
2634
+ }
2635
+ } catch {
2636
+ }
2511
2637
  const eoaCollateral = {};
2512
2638
  for (const [symbol, info] of Object.entries(knownTokens)) {
2513
2639
  try {
package/dist/index.d.mts CHANGED
@@ -244,10 +244,18 @@ declare class AgetherClient {
244
244
  /** Check whether the Safe account has been deployed. */
245
245
  accountExists(): Promise<boolean>;
246
246
  /**
247
- * Get ETH, USDC, and collateral token balances for EOA and Safe account.
247
+ * Discover token addresses from active Morpho Blue positions.
248
+ * Queries the Morpho GraphQL API for markets involving this agent's account,
249
+ * then extracts collateral and loan token info.
250
+ */
251
+ private _discoverPositionTokens;
252
+ /**
253
+ * Get ETH, USDC, and all token balances for EOA and Safe account.
248
254
  *
249
- * Collateral tokens are resolved from a built-in registry of well-known
250
- * tokens per chain (WETH, wstETH, cbETH).
255
+ * Tokens are resolved from:
256
+ * 1. Built-in registry of well-known tokens (WETH, wstETH, cbETH)
257
+ * 2. Dynamic discovery from active Morpho positions (loan + collateral tokens)
258
+ * This ensures tokens like LCAP or any new Morpho market tokens appear in balances.
251
259
  */
252
260
  getBalances(): Promise<BalancesResult>;
253
261
  /**
@@ -1413,7 +1421,7 @@ declare class AgentIdentityClient {
1413
1421
  /**
1414
1422
  * Give positive feedback (shorthand)
1415
1423
  */
1416
- givePosisitiveFeedback(agentId: bigint, value?: number, tags?: {
1424
+ givePositiveFeedback(agentId: bigint, value?: number, tags?: {
1417
1425
  tag1?: string;
1418
1426
  tag2?: string;
1419
1427
  }): Promise<string>;
@@ -1567,4 +1575,26 @@ declare function getMorphoBlueAddress(chainId: ChainId): string;
1567
1575
  */
1568
1576
  declare function getContractAddresses(chainId: ChainId): ContractAddresses;
1569
1577
 
1570
- export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, type MarketFilter, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawFromAccountResult, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getContractAddresses, getDefaultConfig, getMorphoBlueAddress, getUSDCAddress, parseUnits, rateToBps };
1578
+ /**
1579
+ * Retry utility with exponential backoff for network operations.
1580
+ *
1581
+ * Phase 2: Added to handle transient RPC failures, rate limits,
1582
+ * and network timeouts gracefully instead of failing immediately.
1583
+ */
1584
+ interface RetryOptions {
1585
+ /** Maximum number of attempts (default: 3) */
1586
+ maxRetries?: number;
1587
+ /** Base delay in ms (default: 1000) */
1588
+ baseDelay?: number;
1589
+ /** Maximum delay in ms (default: 15000) */
1590
+ maxDelay?: number;
1591
+ /** Called before each retry with attempt number and error */
1592
+ onRetry?: (attempt: number, error: Error) => void;
1593
+ }
1594
+ /**
1595
+ * Execute an async function with retry and exponential backoff.
1596
+ * Only retries on transient network errors, not business logic errors.
1597
+ */
1598
+ declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
1599
+
1600
+ export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, type MarketFilter, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, type RetryOptions, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawFromAccountResult, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getContractAddresses, getDefaultConfig, getMorphoBlueAddress, getUSDCAddress, parseUnits, rateToBps, withRetry };
package/dist/index.d.ts CHANGED
@@ -244,10 +244,18 @@ declare class AgetherClient {
244
244
  /** Check whether the Safe account has been deployed. */
245
245
  accountExists(): Promise<boolean>;
246
246
  /**
247
- * Get ETH, USDC, and collateral token balances for EOA and Safe account.
247
+ * Discover token addresses from active Morpho Blue positions.
248
+ * Queries the Morpho GraphQL API for markets involving this agent's account,
249
+ * then extracts collateral and loan token info.
250
+ */
251
+ private _discoverPositionTokens;
252
+ /**
253
+ * Get ETH, USDC, and all token balances for EOA and Safe account.
248
254
  *
249
- * Collateral tokens are resolved from a built-in registry of well-known
250
- * tokens per chain (WETH, wstETH, cbETH).
255
+ * Tokens are resolved from:
256
+ * 1. Built-in registry of well-known tokens (WETH, wstETH, cbETH)
257
+ * 2. Dynamic discovery from active Morpho positions (loan + collateral tokens)
258
+ * This ensures tokens like LCAP or any new Morpho market tokens appear in balances.
251
259
  */
252
260
  getBalances(): Promise<BalancesResult>;
253
261
  /**
@@ -1413,7 +1421,7 @@ declare class AgentIdentityClient {
1413
1421
  /**
1414
1422
  * Give positive feedback (shorthand)
1415
1423
  */
1416
- givePosisitiveFeedback(agentId: bigint, value?: number, tags?: {
1424
+ givePositiveFeedback(agentId: bigint, value?: number, tags?: {
1417
1425
  tag1?: string;
1418
1426
  tag2?: string;
1419
1427
  }): Promise<string>;
@@ -1567,4 +1575,26 @@ declare function getMorphoBlueAddress(chainId: ChainId): string;
1567
1575
  */
1568
1576
  declare function getContractAddresses(chainId: ChainId): ContractAddresses;
1569
1577
 
1570
- export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, type MarketFilter, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawFromAccountResult, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getContractAddresses, getDefaultConfig, getMorphoBlueAddress, getUSDCAddress, parseUnits, rateToBps };
1578
+ /**
1579
+ * Retry utility with exponential backoff for network operations.
1580
+ *
1581
+ * Phase 2: Added to handle transient RPC failures, rate limits,
1582
+ * and network timeouts gracefully instead of failing immediately.
1583
+ */
1584
+ interface RetryOptions {
1585
+ /** Maximum number of attempts (default: 3) */
1586
+ maxRetries?: number;
1587
+ /** Base delay in ms (default: 1000) */
1588
+ baseDelay?: number;
1589
+ /** Maximum delay in ms (default: 15000) */
1590
+ maxDelay?: number;
1591
+ /** Called before each retry with attempt number and error */
1592
+ onRetry?: (attempt: number, error: Error) => void;
1593
+ }
1594
+ /**
1595
+ * Execute an async function with retry and exponential backoff.
1596
+ * Only retries on transient network errors, not business logic errors.
1597
+ */
1598
+ declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
1599
+
1600
+ export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, type MarketFilter, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, type RetryOptions, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawFromAccountResult, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getContractAddresses, getDefaultConfig, getMorphoBlueAddress, getUSDCAddress, parseUnits, rateToBps, withRetry };
package/dist/index.js CHANGED
@@ -69,7 +69,8 @@ __export(index_exports, {
69
69
  getMorphoBlueAddress: () => getMorphoBlueAddress,
70
70
  getUSDCAddress: () => getUSDCAddress,
71
71
  parseUnits: () => parseUnits,
72
- rateToBps: () => rateToBps
72
+ rateToBps: () => rateToBps,
73
+ withRetry: () => withRetry
73
74
  });
74
75
  module.exports = __toCommonJS(index_exports);
75
76
 
@@ -618,13 +619,64 @@ var AgetherClient = class _AgetherClient {
618
619
  return this.agether4337Factory.accountExists(id);
619
620
  }
620
621
  // ════════════════════════════════════════════════════════
622
+ // Token Discovery (for dynamic balance queries)
623
+ // ════════════════════════════════════════════════════════
624
+ /**
625
+ * Discover token addresses from active Morpho Blue positions.
626
+ * Queries the Morpho GraphQL API for markets involving this agent's account,
627
+ * then extracts collateral and loan token info.
628
+ */
629
+ async _discoverPositionTokens() {
630
+ const tokens = {};
631
+ try {
632
+ const acctAddr = await this.getAccountAddress();
633
+ const chainId = this.config.chainId;
634
+ const query = `{
635
+ marketPositions(
636
+ where: { userAddress_in: ["${acctAddr.toLowerCase()}"], chainId_in: [${chainId}] }
637
+ first: 20
638
+ ) {
639
+ items {
640
+ market {
641
+ collateralAsset { address symbol decimals }
642
+ loanAsset { address symbol decimals }
643
+ }
644
+ }
645
+ }
646
+ }`;
647
+ const resp = await fetch("https://blue-api.morpho.org/graphql", {
648
+ method: "POST",
649
+ headers: { "Content-Type": "application/json" },
650
+ body: JSON.stringify({ query }),
651
+ signal: AbortSignal.timeout(5e3)
652
+ });
653
+ if (!resp.ok) return tokens;
654
+ const data = await resp.json();
655
+ const items = data?.data?.marketPositions?.items ?? [];
656
+ for (const item of items) {
657
+ const col = item?.market?.collateralAsset;
658
+ const loan = item?.market?.loanAsset;
659
+ if (col?.symbol && col?.address) {
660
+ tokens[col.symbol] = { address: col.address, symbol: col.symbol, decimals: col.decimals ?? 18 };
661
+ }
662
+ if (loan?.symbol && loan?.address) {
663
+ tokens[loan.symbol] = { address: loan.address, symbol: loan.symbol, decimals: loan.decimals ?? 18 };
664
+ }
665
+ }
666
+ } catch {
667
+ }
668
+ return tokens;
669
+ }
670
+ // ════════════════════════════════════════════════════════
621
671
  // Balances
622
672
  // ════════════════════════════════════════════════════════
623
673
  /**
624
- * Get ETH, USDC, and collateral token balances for EOA and Safe account.
674
+ * Get ETH, USDC, and all token balances for EOA and Safe account.
625
675
  *
626
- * Collateral tokens are resolved from a built-in registry of well-known
627
- * tokens per chain (WETH, wstETH, cbETH).
676
+ * Tokens are resolved from:
677
+ * 1. Built-in registry of well-known tokens (WETH, wstETH, cbETH)
678
+ * 2. Dynamic discovery from active Morpho positions (loan + collateral tokens)
679
+ * This ensures tokens like LCAP or any new Morpho market tokens appear in balances.
628
680
  */
629
681
  async getBalances() {
630
682
  const provider = this.signer.provider;
@@ -632,7 +684,18 @@ var AgetherClient = class _AgetherClient {
632
684
  const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, provider);
633
685
  const ethBal = await provider.getBalance(eoaAddr);
634
686
  const usdcBal = await usdc.balanceOf(eoaAddr);
635
- const knownTokens = KNOWN_TOKENS[this.config.chainId] ?? {};
687
+ const knownTokens = {
688
+ ...KNOWN_TOKENS[this.config.chainId] ?? {}
689
+ };
690
+ try {
691
+ const positions = await this._discoverPositionTokens();
692
+ for (const [symbol, info] of Object.entries(positions)) {
693
+ if (!knownTokens[symbol]) {
694
+ knownTokens[symbol] = info;
695
+ }
696
+ }
697
+ } catch {
698
+ }
636
699
  const eoaCollateral = {};
637
700
  for (const [symbol, info] of Object.entries(knownTokens)) {
638
701
  try {
@@ -1200,6 +1263,53 @@ var AgetherClient = class _AgetherClient {
1200
1263
  // src/clients/MorphoClient.ts
1201
1264
  var import_ethers2 = require("ethers");
1202
1265
  var import_axios2 = __toESM(require("axios"));
1266
+
1267
+ // src/utils/retry.ts
1268
+ var RETRIABLE_PATTERNS = [
1269
+ "ECONNRESET",
1270
+ "ECONNREFUSED",
1271
+ "ENOTFOUND",
1272
+ "ETIMEDOUT",
1273
+ "fetch failed",
1274
+ "network error",
1275
+ "socket hang up",
1276
+ "rate limit",
1277
+ "429",
1278
+ "502",
1279
+ "503",
1280
+ "504",
1281
+ "timeout"
1282
+ ];
1283
+ function isRetriable(error) {
1284
+ const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
1285
+ return RETRIABLE_PATTERNS.some((p) => msg.includes(p.toLowerCase()));
1286
+ }
1287
+ async function withRetry(fn, options = {}) {
1288
+ const {
1289
+ maxRetries = 3,
1290
+ baseDelay = 1e3,
1291
+ maxDelay = 15e3,
1292
+ onRetry
1293
+ } = options;
1294
+ let lastError;
1295
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
1296
+ try {
1297
+ return await fn();
1298
+ } catch (error) {
1299
+ lastError = error instanceof Error ? error : new Error(String(error));
1300
+ if (attempt >= maxRetries || !isRetriable(error)) {
1301
+ throw lastError;
1302
+ }
1303
+ const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
1304
+ const jitter = delay * (0.5 + Math.random() * 0.5);
1305
+ onRetry?.(attempt, lastError);
1306
+ await new Promise((resolve) => setTimeout(resolve, jitter));
1307
+ }
1308
+ }
1309
+ throw lastError;
1310
+ }
1311
+
1312
+ // src/clients/MorphoClient.ts
1203
1313
  var MORPHO_API_URL2 = "https://api.morpho.org/graphql";
1204
1314
  var MODE_SINGLE2 = "0x0000000000000000000000000000000000000000000000000000000000000000";
1205
1315
  var MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
@@ -1346,7 +1456,10 @@ var MorphoClient = class {
1346
1456
  }
1347
1457
  }`;
1348
1458
  try {
1349
- const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
1459
+ const resp = await withRetry(
1460
+ () => import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 }),
1461
+ { maxRetries: 3, onRetry: (n, e) => console.warn(`[agether] Morpho API retry ${n}:`, e.message) }
1462
+ );
1350
1463
  const items = resp.data?.data?.markets?.items ?? [];
1351
1464
  this._discoveredMarkets = items.map((m) => ({
1352
1465
  uniqueKey: m.uniqueKey,
@@ -1530,7 +1643,10 @@ var MorphoClient = class {
1530
1643
  }
1531
1644
  }
1532
1645
  }`;
1533
- const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: posQuery }, { timeout: 15e3 });
1646
+ const resp = await withRetry(
1647
+ () => import_axios2.default.post(MORPHO_API_URL2, { query: posQuery }, { timeout: 15e3 }),
1648
+ { maxRetries: 3, onRetry: (n, e) => console.warn(`[agether] Morpho position query retry ${n}:`, e.message) }
1649
+ );
1534
1650
  const items = resp.data?.data?.marketPositions?.items ?? [];
1535
1651
  for (const item of items) {
1536
1652
  const supplyShares = BigInt(item.supplyShares ?? "0");
@@ -1749,7 +1865,10 @@ var MorphoClient = class {
1749
1865
  }
1750
1866
  }`;
1751
1867
  try {
1752
- const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
1868
+ const resp = await withRetry(
1869
+ () => import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 }),
1870
+ { maxRetries: 3, onRetry: (n, e) => console.warn(`[agether] Morpho API retry ${n}:`, e.message) }
1871
+ );
1753
1872
  let items = resp.data?.data?.markets?.items ?? [];
1754
1873
  if (searchTerm && collateralSymbolOrAddress && !collateralSymbolOrAddress.startsWith("0x")) {
1755
1874
  const sym = collateralSymbolOrAddress.toUpperCase();
@@ -1815,7 +1934,10 @@ var MorphoClient = class {
1815
1934
  }
1816
1935
  }`;
1817
1936
  try {
1818
- const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
1937
+ const resp = await withRetry(
1938
+ () => import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 }),
1939
+ { maxRetries: 3, onRetry: (n, e) => console.warn(`[agether] Morpho API retry ${n}:`, e.message) }
1940
+ );
1819
1941
  let items = resp.data?.data?.markets?.items ?? [];
1820
1942
  items = items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress);
1821
1943
  const searchUpper = search.toUpperCase();
@@ -3826,7 +3948,7 @@ var AgentIdentityClient = class {
3826
3948
  /**
3827
3949
  * Give positive feedback (shorthand)
3828
3950
  */
3829
- async givePosisitiveFeedback(agentId, value = 100, tags = {}) {
3951
+ async givePositiveFeedback(agentId, value = 100, tags = {}) {
3830
3952
  return this.giveFeedback({
3831
3953
  agentId,
3832
3954
  value: Math.abs(value),
@@ -4032,5 +4154,6 @@ function rateToBps(rate) {
4032
4154
  getMorphoBlueAddress,
4033
4155
  getUSDCAddress,
4034
4156
  parseUnits,
4035
- rateToBps
4157
+ rateToBps,
4158
+ withRetry
4036
4159
  });