@owney/sdk 0.7.16-beta.0 → 0.7.16-beta.2

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.cjs CHANGED
@@ -26,6 +26,7 @@ __export(index_exports, {
26
26
  NotConnectedError: () => NotConnectedError,
27
27
  OwneyError: () => OwneyError,
28
28
  OwneySDK: () => OwneySDK,
29
+ YieldseekerAgent: () => YieldseekerAgent,
29
30
  createOwneySIWX: () => createOwneySIWX,
30
31
  setOwneyDebug: () => setOwneyDebug
31
32
  });
@@ -83,23 +84,6 @@ var SupportedAssets = [
83
84
  }
84
85
  ];
85
86
 
86
- // src/lib/debug.ts
87
- var configuredDebug = false;
88
- function setOwneyDebug(enabled) {
89
- configuredDebug = enabled;
90
- }
91
- function isOwneyDebug() {
92
- return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
93
- }
94
- function debugLog(scope, message, data) {
95
- if (!isOwneyDebug()) return;
96
- if (data === void 0) {
97
- console.log(`[${scope}] ${message}`);
98
- } else {
99
- console.log(`[${scope}] ${message}`, data);
100
- }
101
- }
102
-
103
87
  // src/errors.ts
104
88
  var OwneyError = class extends Error {
105
89
  code;
@@ -197,18 +181,18 @@ function tokenDecimals(symbol, explicit) {
197
181
  return explicit;
198
182
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
199
183
  }
200
- function mapDeposit(raw) {
184
+ function mapDeposit(raw2) {
201
185
  return {
202
- txHash: raw.txHash,
203
- smartWallet: raw.smartWallet,
204
- amount: raw.amount
186
+ txHash: raw2.txHash,
187
+ smartWallet: raw2.smartWallet,
188
+ amount: raw2.amount
205
189
  };
206
190
  }
207
- function mapWithdraw(raw) {
191
+ function mapWithdraw(raw2) {
208
192
  return {
209
- txHash: raw.txHash,
210
- type: raw.type,
211
- amount: raw.amount
193
+ txHash: raw2.txHash,
194
+ type: raw2.type,
195
+ amount: raw2.amount
212
196
  };
213
197
  }
214
198
  var CHAIN_ID_TO_NAME = {
@@ -227,10 +211,10 @@ function resolveChainId(chain) {
227
211
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
228
212
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
229
213
  }
230
- function mapPendingAllocations(raw) {
231
- if (!Array.isArray(raw)) return void 0;
214
+ function mapPendingAllocations(raw2) {
215
+ if (!Array.isArray(raw2)) return void 0;
232
216
  const pending = [];
233
- for (const entry of raw) {
217
+ for (const entry of raw2) {
234
218
  if (typeof entry !== "object" || entry === null) continue;
235
219
  const e = entry;
236
220
  if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
@@ -253,8 +237,8 @@ function mapPendingAllocations(raw) {
253
237
  }
254
238
  return pending.length > 0 ? pending : void 0;
255
239
  }
256
- function mapBalances(raw, _chainId, smartWallet) {
257
- const portfolio = raw.portfolio;
240
+ function mapBalances(raw2, _chainId, smartWallet) {
241
+ const portfolio = raw2.portfolio;
258
242
  const portfolioByChain = portfolio.portfolioByChain ?? {};
259
243
  let totalBalance = 0;
260
244
  const tokens = [];
@@ -299,7 +283,7 @@ function mapBalances(raw, _chainId, smartWallet) {
299
283
  )
300
284
  )
301
285
  ),
302
- apy: netApy(p.pool_apy_withFee, p.pool_apy, "pool_apy_withFee"),
286
+ apy: p.pool_apy,
303
287
  tvl: p.pool_tvl,
304
288
  // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
305
289
  // structural read keeps it undefined today and auto-populates later.
@@ -323,8 +307,8 @@ function sumTokenValues(tokens) {
323
307
  function sumTokenEarnings(tokens) {
324
308
  return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
325
309
  }
326
- function mapEarnings(raw, smartWallet) {
327
- const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
310
+ function mapEarnings(raw2, smartWallet) {
311
+ const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
328
312
  const tokens = [];
329
313
  for (const [chainIdKey, tokensBySymbol] of Object.entries(
330
314
  totalEarningsByChain
@@ -343,15 +327,15 @@ function mapEarnings(raw, smartWallet) {
343
327
  return {
344
328
  smartWallet,
345
329
  lifetimeEarnings: sumTokenEarnings(
346
- raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
330
+ raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
347
331
  ),
348
332
  tokens
349
333
  };
350
334
  }
351
- function mapWeightedApyByChain(raw) {
352
- if (!raw) return void 0;
335
+ function mapWeightedApyByChain(raw2) {
336
+ if (!raw2) return void 0;
353
337
  const out = {};
354
- for (const [chainKey, tokenApy] of Object.entries(raw)) {
338
+ for (const [chainKey, tokenApy] of Object.entries(raw2)) {
355
339
  const chainId = Number(chainKey);
356
340
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
357
341
  const perAsset = {};
@@ -364,19 +348,6 @@ function mapWeightedApyByChain(raw) {
364
348
  }
365
349
  return Object.keys(out).length > 0 ? out : void 0;
366
350
  }
367
- var warnedGrossApyFallbacks = /* @__PURE__ */ new Set();
368
- function netApy(net, gross, field) {
369
- if (net !== void 0 && net !== null) return net;
370
- if (gross === void 0 || gross === null) return void 0;
371
- if (!warnedGrossApyFallbacks.has(field)) {
372
- warnedGrossApyFallbacks.add(field);
373
- console.warn(
374
- `[owney] @zyfai/sdk omitted "${field}"; falling back to the gross APY, which does not deduct Zyfai's performance fee and so reads high.`
375
- );
376
- }
377
- debugLog("zyfai:apy", `gross fallback for ${field}`, { gross });
378
- return gross;
379
- }
380
351
  function rawPoolApyForChain(entry, chainId, tokenSymbol) {
381
352
  let weightedSum = 0;
382
353
  let totalBalance = 0;
@@ -384,22 +355,21 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
384
355
  if (p.chainId !== chainId) continue;
385
356
  if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
386
357
  const balance = p.balance ?? 0;
387
- const apy = netApy(p.apy_withFee, p.apy, "apy_withFee");
388
- if (balance <= 0 || typeof apy !== "number") continue;
389
- weightedSum += apy * balance;
358
+ if (balance <= 0 || typeof p.apy !== "number") continue;
359
+ weightedSum += p.apy * balance;
390
360
  totalBalance += balance;
391
361
  }
392
362
  return totalBalance > 0 ? weightedSum / totalBalance : null;
393
363
  }
394
- function mapApyHistory(raw, chainId, tokenSymbol) {
395
- const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
364
+ function mapApyHistory(raw2, chainId, tokenSymbol) {
365
+ const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
396
366
  date,
397
367
  apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
398
368
  })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
399
369
  return {
400
- walletAddress: raw.walletAddress,
401
- weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
402
- apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
370
+ walletAddress: raw2.walletAddress,
371
+ weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
372
+ apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
403
373
  history
404
374
  };
405
375
  }
@@ -434,12 +404,8 @@ function mapEntries(rawEntries, chainId) {
434
404
  const matched = positions.find(
435
405
  (p) => p.protocol_name ? rawLog.newOpportunity.includes(`${p.protocol_name} (${p.pool})`) : false
436
406
  );
437
- oldApy = Number(
438
- netApy(rawLog.oldApy_withFee, rawLog.oldApy, "oldApy_withFee")
439
- );
440
- newApy = Number(
441
- netApy(rawLog.newApy_withFee, rawLog.newApy, "newApy_withFee")
442
- );
407
+ oldApy = Number(rawLog.oldApy);
408
+ newApy = Number(rawLog.newApy);
443
409
  const from = splitOpportunity(rawLog.oldOpportunity);
444
410
  const to = splitOpportunity(rawLog.newOpportunity);
445
411
  rebalanceLog = [
@@ -495,23 +461,23 @@ function mapEntries(rawEntries, chainId) {
495
461
  };
496
462
  });
497
463
  }
498
- function mapUserProfile(raw, userAddress) {
464
+ function mapUserProfile(raw2, userAddress) {
499
465
  return {
500
466
  address: userAddress,
501
- smartWallet: raw.smartWallet || "",
502
- chains: raw.chains || [],
503
- strategy: raw.strategy,
504
- hasActiveSessionKey: raw.hasActiveSessionKey || false,
505
- protocols: raw.protocols || [],
506
- splitting: raw.splitting,
507
- minSplits: raw.minSplits
467
+ smartWallet: raw2.smartWallet || "",
468
+ chains: raw2.chains || [],
469
+ strategy: raw2.strategy,
470
+ hasActiveSessionKey: raw2.hasActiveSessionKey || false,
471
+ protocols: raw2.protocols || [],
472
+ splitting: raw2.splitting,
473
+ minSplits: raw2.minSplits
508
474
  };
509
475
  }
510
- function mapApyByStrategy(raw) {
476
+ function mapApyByStrategy(raw2) {
511
477
  const apyPerAsset = {};
512
478
  let apySum = 0;
513
479
  let apyCount = 0;
514
- for (const entry of raw.data) {
480
+ for (const entry of raw2.data) {
515
481
  const supported = SupportedAssets.find(
516
482
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
517
483
  );
@@ -519,11 +485,7 @@ function mapApyByStrategy(raw) {
519
485
  const chainKey = supported.chainId;
520
486
  const symbolKey = supported.symbol;
521
487
  const bucket = apyPerAsset[chainKey] ?? {};
522
- const apy = netApy(
523
- entry.average_apy_withFee,
524
- entry.average_apy,
525
- "average_apy_withFee"
526
- ) ?? entry.average_apy;
488
+ const apy = entry.average_apy_with_fee ?? entry.average_apy;
527
489
  bucket[symbolKey] = apy;
528
490
  apyPerAsset[chainKey] = bucket;
529
491
  apySum += apy;
@@ -682,15 +644,15 @@ var readSession = (address, _chainId) => {
682
644
  if (typeof window === "undefined") return null;
683
645
  const key2 = buildKey(address);
684
646
  const store = storage();
685
- let raw = null;
647
+ let raw2 = null;
686
648
  try {
687
- raw = store?.getItem(key2) ?? null;
649
+ raw2 = store?.getItem(key2) ?? null;
688
650
  } catch {
689
- raw = null;
651
+ raw2 = null;
690
652
  }
691
- if (raw) {
653
+ if (raw2) {
692
654
  try {
693
- const parsed = JSON.parse(raw);
655
+ const parsed = JSON.parse(raw2);
694
656
  if (isFreshSession(parsed)) return parsed;
695
657
  } catch {
696
658
  }
@@ -858,8 +820,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
858
820
  }
859
821
  return result;
860
822
  }
861
- function flattenAvailablePools(raw) {
862
- const byChain = raw ?? {};
823
+ function flattenAvailablePools(raw2) {
824
+ const byChain = raw2 ?? {};
863
825
  const names = [];
864
826
  for (const byToken of Object.values(byChain ?? {})) {
865
827
  for (const entry of Object.values(byToken ?? {})) {
@@ -883,6 +845,23 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
883
845
  return !protocolListsEqual(current.protocols, desiredProtocols);
884
846
  }
885
847
 
848
+ // src/lib/debug.ts
849
+ var configuredDebug = false;
850
+ function setOwneyDebug(enabled) {
851
+ configuredDebug = enabled;
852
+ }
853
+ function isOwneyDebug() {
854
+ return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
855
+ }
856
+ function debugLog(scope, message, data) {
857
+ if (!isOwneyDebug()) return;
858
+ if (data === void 0) {
859
+ console.log(`[${scope}] ${message}`);
860
+ } else {
861
+ console.log(`[${scope}] ${message}`, data);
862
+ }
863
+ }
864
+
886
865
  // src/agents/zyfai/zyfai.agent.ts
887
866
  var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem.parseAbi)([
888
867
  "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
@@ -1353,8 +1332,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1353
1332
  const poolResults = await Promise.all(
1354
1333
  universe.map(async (protocol) => {
1355
1334
  try {
1356
- const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1357
- return [protocol.id, flattenAvailablePools(raw)];
1335
+ const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
1336
+ return [protocol.id, flattenAvailablePools(raw2)];
1358
1337
  } catch (error) {
1359
1338
  console.warn(
1360
1339
  `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
@@ -1420,14 +1399,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1420
1399
  async readWalletState(ownerAddress) {
1421
1400
  try {
1422
1401
  const { portfolio } = await this.sdk.getPositions(ownerAddress);
1423
- const raw = portfolio;
1402
+ const raw2 = portfolio;
1424
1403
  debugLog("zyfai:onboard", "wallet state from getPositions", {
1425
- predeployed: raw?.predeployed,
1426
- hasActiveSessionKey: raw?.hasActiveSessionKey
1404
+ predeployed: raw2?.predeployed,
1405
+ hasActiveSessionKey: raw2?.hasActiveSessionKey
1427
1406
  });
1428
1407
  return {
1429
- predeployed: raw?.predeployed,
1430
- hasActiveSessionKey: raw?.hasActiveSessionKey
1408
+ predeployed: raw2?.predeployed,
1409
+ hasActiveSessionKey: raw2?.hasActiveSessionKey
1431
1410
  };
1432
1411
  } catch (error) {
1433
1412
  console.warn(
@@ -1707,14 +1686,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1707
1686
  return { txHash, smartWallet, amount };
1708
1687
  }
1709
1688
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
1710
- const raw = await this.sdk.depositFunds(
1689
+ const raw2 = await this.sdk.depositFunds(
1711
1690
  this.getAddress(),
1712
1691
  validChainId,
1713
1692
  amount,
1714
1693
  asset,
1715
1694
  "aggressive"
1716
1695
  );
1717
- return mapDeposit(raw);
1696
+ return mapDeposit(raw2);
1718
1697
  } catch (error) {
1719
1698
  throw error;
1720
1699
  }
@@ -1723,32 +1702,32 @@ var ZyfaiAgent = class _ZyfaiAgent {
1723
1702
  async withdraw(state, chainId, token, amount) {
1724
1703
  const validChainId = isValidChainId(chainId);
1725
1704
  await this.ensureConnected(state, validChainId);
1726
- const raw = await this.sdk.withdrawFunds(
1705
+ const raw2 = await this.sdk.withdrawFunds(
1727
1706
  this.getAddress(),
1728
1707
  validChainId,
1729
1708
  amount,
1730
1709
  token
1731
1710
  );
1732
- if (!raw.success) {
1711
+ if (!raw2.success) {
1733
1712
  throw new OwneyError(
1734
1713
  "WITHDRAW_FAILED",
1735
- raw.message || "Zyfai withdraw failed.",
1736
- { chainId: validChainId, token, amount, response: raw },
1714
+ raw2.message || "Zyfai withdraw failed.",
1715
+ { chainId: validChainId, token, amount, response: raw2 },
1737
1716
  this.id
1738
1717
  );
1739
1718
  }
1740
- return mapWithdraw(raw);
1719
+ return mapWithdraw(raw2);
1741
1720
  }
1742
1721
  // --- IAgent: Portfolio reads ---
1743
1722
  async getBalances(state, chainId) {
1744
1723
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
1745
- const raw = await this.sdk.getPortfolio(this.getAddress());
1746
- return mapBalances(raw, validChainId, smartWallet);
1724
+ const raw2 = await this.sdk.getPortfolio(this.getAddress());
1725
+ return mapBalances(raw2, validChainId, smartWallet);
1747
1726
  }
1748
1727
  async getEarnings(state, chainId) {
1749
1728
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1750
- const raw = await this.sdk.getOnchainEarnings(smartWallet);
1751
- return mapEarnings(raw, smartWallet);
1729
+ const raw2 = await this.sdk.getOnchainEarnings(smartWallet);
1730
+ return mapEarnings(raw2, smartWallet);
1752
1731
  }
1753
1732
  async refreshEarnings(state, chainId) {
1754
1733
  if (this.earningsRefreshInFlight) return this.earningsRefreshInFlight;
@@ -1768,8 +1747,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1768
1747
  }
1769
1748
  async getAccountApy(state, chainId, days, tokenSymbol) {
1770
1749
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1771
- const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
1772
- return mapApyHistory(raw, chainId, tokenSymbol);
1750
+ const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
1751
+ return mapApyHistory(raw2, chainId, tokenSymbol);
1773
1752
  }
1774
1753
  /**
1775
1754
  * Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
@@ -1804,7 +1783,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1804
1783
  const matched = [];
1805
1784
  let backendExhausted = false;
1806
1785
  for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
1807
- const raw = await this.sdk.getHistory(smartWallet, validChainId, {
1786
+ const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
1808
1787
  limit: backendPageSize,
1809
1788
  offset,
1810
1789
  fromDate: options?.fromDate,
@@ -1815,13 +1794,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
1815
1794
  // asset's rows and handing back a page that filters to nothing.
1816
1795
  assetType
1817
1796
  });
1818
- raw.data.forEach((entry, idx) => {
1797
+ raw2.data.forEach((entry, idx) => {
1819
1798
  if (entry.chainId === validChainId) {
1820
1799
  matched.push({ entry, rawIdx: offset + idx });
1821
1800
  }
1822
1801
  });
1823
- offset += raw.data.length;
1824
- if (raw.data.length < backendPageSize) {
1802
+ offset += raw2.data.length;
1803
+ if (raw2.data.length < backendPageSize) {
1825
1804
  backendExhausted = true;
1826
1805
  break;
1827
1806
  }
@@ -1843,18 +1822,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
1843
1822
  }
1844
1823
  async getUserProfile(state, chainId) {
1845
1824
  await this.connectAuth(state, chainId);
1846
- const raw = await this.sdk.getUserDetails();
1825
+ const raw2 = await this.sdk.getUserDetails();
1847
1826
  debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
1848
1827
  asset: "USDC (default \u2014 no asset passed)",
1849
- splitting: raw.splitting,
1850
- minSplits: raw.minSplits,
1851
- strategy: raw.strategy,
1852
- chains: raw.chains,
1853
- protocolCount: raw.protocols?.length,
1854
- hasActiveSessionKey: raw.hasActiveSessionKey,
1855
- smartWallet: raw.smartWallet
1828
+ splitting: raw2.splitting,
1829
+ minSplits: raw2.minSplits,
1830
+ strategy: raw2.strategy,
1831
+ chains: raw2.chains,
1832
+ protocolCount: raw2.protocols?.length,
1833
+ hasActiveSessionKey: raw2.hasActiveSessionKey,
1834
+ smartWallet: raw2.smartWallet
1856
1835
  });
1857
- return mapUserProfile(raw, this.connectedAddress);
1836
+ return mapUserProfile(raw2, this.connectedAddress);
1858
1837
  }
1859
1838
  async ensureAutoSelectProtocols(state, chainId, asset) {
1860
1839
  await this.connectAuth(state, chainId);
@@ -1873,321 +1852,184 @@ var ZyfaiAgent = class _ZyfaiAgent {
1873
1852
  }
1874
1853
  // --- IAgent: Discovery (no wallet required) ---
1875
1854
  async getAgentApy(days, options) {
1876
- const raw = await this.sdk.getAPYPerStrategy(
1855
+ const raw2 = await this.sdk.getAPYPerStrategy(
1877
1856
  false,
1878
1857
  DayFilterMapping[days],
1879
1858
  "aggressive",
1880
1859
  options?.chainId,
1881
1860
  options?.tokenSymbol
1882
1861
  );
1883
- return mapApyByStrategy(raw);
1862
+ return mapApyByStrategy(raw2);
1884
1863
  }
1885
1864
  };
1886
1865
 
1887
- // src/lib/routing-api.ts
1888
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1889
- async function fetchOrgAgentConfig(apiKey) {
1890
- const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
1866
+ // src/agents/yieldseeker/yieldseeker.agent.ts
1867
+ var import_viem6 = require("viem");
1868
+ var import_chains3 = require("viem/chains");
1869
+
1870
+ // src/lib/chain-guard.ts
1871
+ var CHAIN_NAMES = {
1872
+ 1: "Ethereum",
1873
+ 8453: "Base",
1874
+ 42161: "Arbitrum"
1875
+ };
1876
+ function chainName(chainId) {
1877
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
1878
+ }
1879
+ async function ensureWalletOnChain(pub, wallet, expected) {
1880
+ const actual = await pub.getChainId();
1881
+ if (actual === expected) return;
1891
1882
  try {
1892
- const res = await fetch(url, {
1893
- method: "GET",
1894
- headers: {
1895
- "Content-Type": "application/json",
1896
- "x-owney-api-key": `${apiKey}`
1897
- }
1898
- });
1899
- if (!res.ok) {
1900
- if (res.status !== 404) {
1901
- console.warn(
1902
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
1903
- );
1883
+ await wallet.switchChain({ id: expected });
1884
+ } catch (error) {
1885
+ throw new OwneyError(
1886
+ "CHAIN_MISMATCH",
1887
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
1888
+ {
1889
+ expectedChainId: expected,
1890
+ actualChainId: actual,
1891
+ cause: error instanceof Error ? error.message : String(error)
1904
1892
  }
1905
- return null;
1906
- }
1907
- const json = await res.json();
1908
- const policy = json.success ? json.data ?? null : null;
1909
- debugLog(
1910
- "owney-sdk",
1911
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
1912
- policy ?? void 0
1913
1893
  );
1914
- return policy;
1915
- } catch (error) {
1916
- console.warn(
1917
- "[owney-sdk] Could not read org agent config (non-fatal):",
1918
- error instanceof Error ? error.message : String(error)
1894
+ }
1895
+ const after = await pub.getChainId();
1896
+ if (after !== expected) {
1897
+ throw new OwneyError(
1898
+ "CHAIN_MISMATCH",
1899
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
1900
+ { expectedChainId: expected, actualChainId: after }
1919
1901
  );
1920
- return null;
1921
1902
  }
1922
1903
  }
1923
- async function fetchAgentKeys(apiKey) {
1924
- const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
1925
- const res = await fetch(url, {
1926
- method: "GET",
1927
- headers: {
1928
- "Content-Type": "application/json",
1929
- "x-owney-api-key": `${apiKey}`
1930
- }
1931
- });
1932
- if (!res.ok) {
1933
- const text = await res.text().catch(() => "");
1904
+
1905
+ // src/lib/transfer-auth.ts
1906
+ var import_viem2 = require("viem");
1907
+ var ERC20_META_ABI = [
1908
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
1909
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
1910
+ ];
1911
+ function buildTransferWithAuthorizationTypedData(input) {
1912
+ return {
1913
+ domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
1914
+ types: {
1915
+ TransferWithAuthorization: [
1916
+ { name: "from", type: "address" },
1917
+ { name: "to", type: "address" },
1918
+ { name: "value", type: "uint256" },
1919
+ { name: "validAfter", type: "uint256" },
1920
+ { name: "validBefore", type: "uint256" },
1921
+ { name: "nonce", type: "bytes32" }
1922
+ ]
1923
+ },
1924
+ primaryType: "TransferWithAuthorization",
1925
+ message: input.message
1926
+ };
1927
+ }
1928
+ async function readTokenMeta(publicClient, token) {
1929
+ const [tokenName, tokenVersion] = await Promise.all([
1930
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
1931
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1932
+ ]);
1933
+ return { tokenName, tokenVersion };
1934
+ }
1935
+ function randomAuthNonce() {
1936
+ const bytes = new Uint8Array(32);
1937
+ globalThis.crypto.getRandomValues(bytes);
1938
+ return (0, import_viem2.bytesToHex)(bytes);
1939
+ }
1940
+
1941
+ // src/lib/sponsor-client.ts
1942
+ var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1943
+ async function postPaymasterIntent(input) {
1944
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1945
+ let res;
1946
+ try {
1947
+ res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
1948
+ method: "POST",
1949
+ headers: {
1950
+ "content-type": "application/json",
1951
+ "x-owney-api-key": input.apiKey,
1952
+ Authorization: `Signature ${input.yieldseekerSignature}`
1953
+ },
1954
+ body: JSON.stringify(input.body)
1955
+ });
1956
+ } catch (networkError) {
1934
1957
  throw new OwneyError(
1935
- "API_ROUTING_ERROR",
1936
- `Routing API error ${res.status}: ${text}`,
1937
- { statusCode: res.status, responseBody: text }
1958
+ "SPONSOR_REQUEST_FAILED",
1959
+ `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1960
+ { cause: String(networkError), safeToFallback: true }
1938
1961
  );
1939
1962
  }
1940
- const json = await res.json();
1941
- if (!json.success) {
1963
+ const text = await res.text();
1964
+ let parsed = null;
1965
+ try {
1966
+ parsed = JSON.parse(text);
1967
+ } catch {
1968
+ }
1969
+ if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
1942
1970
  throw new OwneyError(
1943
- "API_ROUTING_FAILED",
1944
- `Routing API request failed: ${json.message}`,
1945
- { message: json.message }
1971
+ "SPONSOR_REQUEST_FAILED",
1972
+ `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1973
+ {
1974
+ statusCode: res.status,
1975
+ responseBody: text.slice(0, 500),
1976
+ safeToFallback: true
1977
+ }
1946
1978
  );
1947
1979
  }
1948
- return json.data;
1980
+ return parsed.data;
1949
1981
  }
1950
-
1951
- // src/lib/health-report.ts
1952
- var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1953
- async function reportAgentFailure(apiKey, agentType, errorCode) {
1982
+ async function postSponsorTransferAuth(input) {
1983
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1984
+ let res;
1954
1985
  try {
1955
- await fetch(`${ROUTING_API_BASE_URL2}/api/v1/agent/health-report`, {
1986
+ res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
1956
1987
  method: "POST",
1957
1988
  headers: {
1958
- "Content-Type": "application/json",
1959
- "x-owney-api-key": apiKey
1989
+ "content-type": "application/json",
1990
+ "x-owney-api-key": input.apiKey,
1991
+ ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
1960
1992
  },
1961
- body: JSON.stringify({
1962
- agent_type: agentType,
1963
- error_code: errorCode,
1964
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1965
- })
1993
+ body: JSON.stringify(input.body)
1966
1994
  });
1967
- } catch (err) {
1968
- console.warn(
1969
- `[owney-sdk] health-report failed for agent "${agentType}":`,
1970
- err instanceof Error ? err.message : err
1995
+ } catch (networkError) {
1996
+ throw new OwneyError(
1997
+ "SPONSOR_REQUEST_FAILED",
1998
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1999
+ { cause: String(networkError) }
1971
2000
  );
1972
2001
  }
1973
- }
1974
- async function withFailureReporting(apiKey, agentType, fn) {
2002
+ const text = await res.text();
2003
+ let parsed = null;
1975
2004
  try {
1976
- return await fn();
1977
- } catch (err) {
1978
- const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
1979
- void reportAgentFailure(apiKey, agentType, errorCode);
1980
- throw err;
2005
+ parsed = JSON.parse(text);
2006
+ } catch {
1981
2007
  }
1982
- }
1983
-
1984
- // src/lib/helpers/withdraw-helper.ts
1985
- var import_viem2 = require("viem");
1986
- function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1987
- const target = asset.toUpperCase();
1988
- return agents.map((agent) => {
1989
- const agentBalance = aggregated[agent.id];
1990
- const tokenBalance = agentBalance?.tokens.find(
1991
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
2008
+ if (!res.ok || !parsed?.success || !parsed.data) {
2009
+ throw new OwneyError(
2010
+ "SPONSOR_REQUEST_FAILED",
2011
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2012
+ {
2013
+ statusCode: res.status,
2014
+ responseBody: text.slice(0, 500),
2015
+ // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
2016
+ // before broadcast, so it is safe to fall back to a user-paid deposit.
2017
+ safeToFallback: res.status === 503
2018
+ }
1992
2019
  );
1993
- if (!tokenBalance) return { agent, balance: 0n };
1994
- return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
1995
- });
1996
- }
1997
- function planProportionalShares(balances, requested, totalAvailable) {
1998
- const plans = balances.map(({ agent, balance }) => ({
1999
- agent,
2000
- balance,
2001
- planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
2002
- }));
2003
- const assigned = plans.reduce((s, p) => s + p.planned, 0n);
2004
- let remainder = requested - assigned;
2005
- const byHeadroom = [...plans].sort((a, b) => {
2006
- const diff = b.balance - b.planned - (a.balance - a.planned);
2007
- return diff > 0n ? 1 : diff < 0n ? -1 : 0;
2008
- });
2009
- for (const p of byHeadroom) {
2010
- if (remainder === 0n) break;
2011
- const headroom = p.balance - p.planned;
2012
- if (headroom <= 0n) continue;
2013
- const take = headroom < remainder ? headroom : remainder;
2014
- p.planned += take;
2015
- remainder -= take;
2016
2020
  }
2017
- return plans;
2018
- }
2019
- function planDisabledDrain(disabled, requested) {
2020
- const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
2021
- const plans = [];
2022
- let remaining = requested;
2023
- for (const { agent, balance } of sorted) {
2024
- if (remaining === 0n) {
2025
- plans.push({ agent, balance, planned: 0n });
2026
- continue;
2027
- }
2028
- const take = balance < remaining ? balance : remaining;
2029
- plans.push({ agent, balance, planned: take });
2030
- remaining -= take;
2031
- }
2032
- return { plans, remaining };
2033
- }
2034
- function redistributeShare(plans, fromIndex, amount, candidatePool) {
2035
- const pool = candidatePool ?? plans.slice(fromIndex + 1);
2036
- const candidates = pool.filter((c) => c.balance - c.planned > 0n);
2037
- const totalHeadroom = candidates.reduce(
2038
- (s, c) => s + (c.balance - c.planned),
2039
- 0n
2040
- );
2041
- if (totalHeadroom === 0n) return;
2042
- let distributed = 0n;
2043
- for (const c of candidates) {
2044
- const headroom = c.balance - c.planned;
2045
- const proportional = headroom * amount / totalHeadroom;
2046
- const give = proportional > headroom ? headroom : proportional;
2047
- c.planned += give;
2048
- distributed += give;
2049
- }
2050
- let leftover = amount - distributed;
2051
- for (const c of candidates) {
2052
- if (leftover === 0n) break;
2053
- const headroom = c.balance - c.planned;
2054
- if (headroom <= 0n) continue;
2055
- const take = headroom < leftover ? headroom : leftover;
2056
- c.planned += take;
2057
- leftover -= take;
2058
- }
2059
- }
2060
- function sumWithdrawnAmount(results) {
2061
- return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
2062
- }
2063
-
2064
- // src/lib/helpers/account-apy-helper.ts
2065
- function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2066
- const sums = {};
2067
- const weights = {};
2068
- for (const id of Object.keys(agentApys)) {
2069
- const cells = agentApys[id].apyByChainAndAsset;
2070
- const balance = agentBalances[id] ?? 0;
2071
- if (!cells || balance <= 0) continue;
2072
- for (const [chainKey, perAsset] of Object.entries(cells)) {
2073
- if (!perAsset) continue;
2074
- const chainId = Number(chainKey);
2075
- for (const [asset, apyValue] of Object.entries(perAsset)) {
2076
- const apy = Number(apyValue ?? 0);
2077
- if (apy === 0) continue;
2078
- sums[chainId] ??= {};
2079
- weights[chainId] ??= {};
2080
- sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
2081
- weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2082
- }
2083
- }
2084
- }
2085
- const out = {};
2086
- for (const chainKey of Object.keys(sums)) {
2087
- const chainId = Number(chainKey);
2088
- const perAssetOut = {};
2089
- for (const asset of Object.keys(sums[chainId])) {
2090
- const w = weights[chainId][asset];
2091
- if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
2092
- }
2093
- if (Object.keys(perAssetOut).length > 0) {
2094
- out[chainId] = perAssetOut;
2095
- }
2096
- }
2097
- return out;
2098
- }
2099
-
2100
- // src/client.ts
2101
- var import_viem6 = require("viem");
2102
- var import_chains2 = require("viem/chains");
2103
-
2104
- // src/lib/transfer-auth.ts
2105
- var import_viem3 = require("viem");
2106
- var ERC20_META_ABI = [
2107
- { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
2108
- { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
2109
- ];
2110
- function buildTransferWithAuthorizationTypedData(input) {
2111
- return {
2112
- domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
2113
- types: {
2114
- TransferWithAuthorization: [
2115
- { name: "from", type: "address" },
2116
- { name: "to", type: "address" },
2117
- { name: "value", type: "uint256" },
2118
- { name: "validAfter", type: "uint256" },
2119
- { name: "validBefore", type: "uint256" },
2120
- { name: "nonce", type: "bytes32" }
2121
- ]
2122
- },
2123
- primaryType: "TransferWithAuthorization",
2124
- message: input.message
2125
- };
2126
- }
2127
- async function readTokenMeta(publicClient, token) {
2128
- const [tokenName, tokenVersion] = await Promise.all([
2129
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
2130
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
2131
- ]);
2132
- return { tokenName, tokenVersion };
2133
- }
2134
- function randomAuthNonce() {
2135
- const bytes = new Uint8Array(32);
2136
- globalThis.crypto.getRandomValues(bytes);
2137
- return (0, import_viem3.bytesToHex)(bytes);
2138
- }
2139
-
2140
- // src/lib/sponsor-client.ts
2141
- var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2142
- async function postSponsorTransferAuth(input) {
2143
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2144
- let res;
2145
- try {
2146
- res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
2147
- method: "POST",
2148
- headers: {
2149
- "content-type": "application/json",
2150
- "x-owney-api-key": input.apiKey
2151
- },
2152
- body: JSON.stringify(input.body)
2153
- });
2154
- } catch (networkError) {
2155
- throw new OwneyError(
2156
- "SPONSOR_REQUEST_FAILED",
2157
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2158
- { cause: String(networkError) }
2159
- );
2160
- }
2161
- const text = await res.text();
2162
- let parsed = null;
2163
- try {
2164
- parsed = JSON.parse(text);
2165
- } catch {
2166
- }
2167
- if (!res.ok || !parsed?.success || !parsed.data) {
2168
- throw new OwneyError(
2169
- "SPONSOR_REQUEST_FAILED",
2170
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2171
- {
2172
- statusCode: res.status,
2173
- responseBody: text.slice(0, 500),
2174
- // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
2175
- // before broadcast, so it is safe to fall back to a user-paid deposit.
2176
- safeToFallback: res.status === 503
2177
- }
2178
- );
2179
- }
2180
- return parsed.data;
2021
+ return parsed.data;
2181
2022
  }
2182
2023
  async function postSponsorPermit2Transfer(input) {
2183
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2024
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2184
2025
  let res;
2185
2026
  try {
2186
- res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
2027
+ res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
2187
2028
  method: "POST",
2188
2029
  headers: {
2189
2030
  "content-type": "application/json",
2190
- "x-owney-api-key": input.apiKey
2031
+ "x-owney-api-key": input.apiKey,
2032
+ ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
2191
2033
  },
2192
2034
  body: JSON.stringify(input.body)
2193
2035
  });
@@ -2218,11 +2060,11 @@ async function postSponsorPermit2Transfer(input) {
2218
2060
  return parsed.data;
2219
2061
  }
2220
2062
  async function getSponsorRelayerAddress(input) {
2221
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2063
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2222
2064
  let res;
2223
2065
  try {
2224
2066
  res = await fetch(
2225
- `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2067
+ `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2226
2068
  {
2227
2069
  headers: { "x-owney-api-key": input.apiKey }
2228
2070
  }
@@ -2255,7 +2097,7 @@ async function getSponsorRelayerAddress(input) {
2255
2097
  }
2256
2098
 
2257
2099
  // src/lib/permit2.ts
2258
- var import_viem4 = require("viem");
2100
+ var import_viem3 = require("viem");
2259
2101
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2260
2102
  var MAX_UINT256 = 2n ** 256n - 1n;
2261
2103
  var ERC20_ALLOWANCE_ABI = [
@@ -2313,7 +2155,7 @@ function buildPermitTransferFromTypedData(input) {
2313
2155
  function randomPermit2Nonce() {
2314
2156
  const bytes = new Uint8Array(32);
2315
2157
  globalThis.crypto.getRandomValues(bytes);
2316
- return BigInt((0, import_viem4.bytesToHex)(bytes));
2158
+ return BigInt((0, import_viem3.bytesToHex)(bytes));
2317
2159
  }
2318
2160
  async function readPermit2Allowance(publicClient, token, owner) {
2319
2161
  return publicClient.readContract({
@@ -2331,205 +2173,1864 @@ async function readErc20Balance(publicClient, token, owner) {
2331
2173
  args: [owner]
2332
2174
  });
2333
2175
  }
2334
-
2335
- // src/lib/chain-guard.ts
2336
- var CHAIN_NAMES = {
2337
- 1: "Ethereum",
2338
- 8453: "Base",
2339
- 42161: "Arbitrum"
2340
- };
2341
- function chainName(chainId) {
2342
- return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
2176
+
2177
+ // src/lib/sponsored-deposit.ts
2178
+ var AUTH_WINDOW_SECONDS = 15 * 60;
2179
+ var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
2180
+ function provideDepositVerificationContext(callback, context) {
2181
+ callback[verificationSetter]?.(context);
2182
+ }
2183
+ function makeVerificationAwareDepositCallback(implementation) {
2184
+ let nextVerification;
2185
+ const callback = async (smartWallet, chainId, amount) => {
2186
+ const verification = nextVerification;
2187
+ nextVerification = void 0;
2188
+ return implementation(smartWallet, chainId, amount, verification);
2189
+ };
2190
+ Object.defineProperty(callback, verificationSetter, {
2191
+ value: (context) => {
2192
+ nextVerification = context;
2193
+ }
2194
+ });
2195
+ return callback;
2196
+ }
2197
+ function makeSponsoredDepositCallback(deps) {
2198
+ const post = deps.httpPost ?? postSponsorTransferAuth;
2199
+ return makeVerificationAwareDepositCallback(
2200
+ async (smartWallet, chainId, amount, verification) => {
2201
+ const cid = chainId;
2202
+ const token = deps.tokenAddressByChain[cid];
2203
+ if (!token) {
2204
+ throw new OwneyError(
2205
+ "CHAIN_UNSUPPORTED",
2206
+ `No sponsored token configured for chain ${chainId}`
2207
+ );
2208
+ }
2209
+ const pub = deps.getPublicClient(cid);
2210
+ const wallet = deps.getWalletClient(cid);
2211
+ await ensureWalletOnChain(pub, wallet, cid);
2212
+ try {
2213
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2214
+ if (balance < BigInt(amount)) {
2215
+ throw new OwneyError(
2216
+ "DEPOSIT_INSUFFICIENT_BALANCE",
2217
+ "Insufficient balance for this deposit.",
2218
+ { token, chainId: cid, balance: balance.toString(), amount }
2219
+ );
2220
+ }
2221
+ } catch (err) {
2222
+ if (err instanceof OwneyError) throw err;
2223
+ console.warn(
2224
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2225
+ err instanceof Error ? err.message : String(err)
2226
+ );
2227
+ }
2228
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2229
+ const validAfter = 0n;
2230
+ const validBefore = BigInt(
2231
+ Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2232
+ );
2233
+ const nonce = randomAuthNonce();
2234
+ const typedData = buildTransferWithAuthorizationTypedData({
2235
+ token,
2236
+ chainId: cid,
2237
+ tokenName,
2238
+ tokenVersion,
2239
+ message: {
2240
+ from: deps.ownerAddress,
2241
+ to: smartWallet,
2242
+ value: BigInt(amount),
2243
+ validAfter,
2244
+ validBefore,
2245
+ nonce
2246
+ }
2247
+ });
2248
+ const authSignature = await wallet.signTypedData({
2249
+ account: deps.ownerAddress,
2250
+ ...typedData
2251
+ });
2252
+ deps.onApproved?.();
2253
+ const result = await post({
2254
+ baseUrl: deps.baseUrl,
2255
+ apiKey: deps.apiKey,
2256
+ ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
2257
+ body: {
2258
+ chainId: cid,
2259
+ token,
2260
+ from: deps.ownerAddress,
2261
+ to: smartWallet,
2262
+ value: amount,
2263
+ validAfter: validAfter.toString(),
2264
+ validBefore: validBefore.toString(),
2265
+ nonce,
2266
+ authSignature,
2267
+ tokenName,
2268
+ tokenVersion,
2269
+ ...verification?.agentId === "yieldseeker" ? {
2270
+ yieldseekerUserId: verification.userId,
2271
+ yieldseekerAgentId: verification.yieldseekerAgentId
2272
+ } : {}
2273
+ }
2274
+ });
2275
+ return result.txHash;
2276
+ }
2277
+ );
2278
+ }
2279
+
2280
+ // src/agents/yieldseeker/yieldseeker.auth.ts
2281
+ var import_siwe = require("siwe");
2282
+ var import_viem4 = require("viem");
2283
+ var import_chains2 = require("viem/chains");
2284
+
2285
+ // src/agents/yieldseeker/yieldseeker.auth-cache.ts
2286
+ var KEY_PREFIX2 = "owney.yieldseeker.session.v4";
2287
+ var INVALIDATED_KEY_PREFIXES = [
2288
+ "owney.yieldseeker.session",
2289
+ "owney.yieldseeker.session.v3"
2290
+ ];
2291
+ var storage2 = () => {
2292
+ if (typeof window === "undefined") return null;
2293
+ try {
2294
+ return window.localStorage;
2295
+ } catch {
2296
+ return null;
2297
+ }
2298
+ };
2299
+ var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2300
+ var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
2301
+ (prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
2302
+ );
2303
+ var clearInvalidatedSessions = (store, address, chainId) => {
2304
+ for (const key2 of invalidatedKeys(address, chainId)) {
2305
+ memorySessions2.delete(key2);
2306
+ try {
2307
+ store?.removeItem(key2);
2308
+ } catch {
2309
+ }
2310
+ }
2311
+ };
2312
+ var memorySessions2 = /* @__PURE__ */ new Map();
2313
+ var isValidSession = (session) => {
2314
+ if (!session?.token) return false;
2315
+ try {
2316
+ const parsed = JSON.parse(atob(session.token));
2317
+ return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
2318
+ } catch {
2319
+ return false;
2320
+ }
2321
+ };
2322
+ var readYieldseekerSession = (address, chainId) => {
2323
+ if (typeof window === "undefined") return null;
2324
+ const key2 = buildKey2(address, chainId);
2325
+ const store = storage2();
2326
+ clearInvalidatedSessions(store, address, chainId);
2327
+ let raw2 = null;
2328
+ try {
2329
+ raw2 = store?.getItem(key2) ?? null;
2330
+ } catch {
2331
+ raw2 = null;
2332
+ }
2333
+ if (raw2) {
2334
+ try {
2335
+ const parsed = JSON.parse(raw2);
2336
+ if (isValidSession(parsed)) return parsed.token;
2337
+ } catch {
2338
+ }
2339
+ memorySessions2.delete(key2);
2340
+ try {
2341
+ store?.removeItem(key2);
2342
+ } catch {
2343
+ }
2344
+ return null;
2345
+ }
2346
+ const cached = memorySessions2.get(key2);
2347
+ if (isValidSession(cached)) return cached.token;
2348
+ if (cached) memorySessions2.delete(key2);
2349
+ return null;
2350
+ };
2351
+ var writeYieldseekerSession = (address, chainId, token) => {
2352
+ if (typeof window === "undefined") return;
2353
+ const session = { token };
2354
+ if (!isValidSession(session)) return;
2355
+ const key2 = buildKey2(address, chainId);
2356
+ memorySessions2.set(key2, session);
2357
+ const store = storage2();
2358
+ try {
2359
+ store?.setItem(key2, JSON.stringify(session));
2360
+ } catch {
2361
+ }
2362
+ };
2363
+ var clearYieldseekerSession = (address, chainId) => {
2364
+ const key2 = buildKey2(address, chainId);
2365
+ memorySessions2.delete(key2);
2366
+ const store = storage2();
2367
+ clearInvalidatedSessions(store, address, chainId);
2368
+ try {
2369
+ store?.removeItem(key2);
2370
+ } catch {
2371
+ }
2372
+ };
2373
+
2374
+ // src/agents/yieldseeker/yieldseeker.auth.ts
2375
+ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2376
+ return new import_siwe.SiweMessage({
2377
+ domain: "yieldseeker.xyz",
2378
+ address: (0, import_viem4.getAddress)(address),
2379
+ uri: "https://yieldseeker.xyz",
2380
+ version: "1",
2381
+ chainId,
2382
+ nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
2383
+ issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
2384
+ }).prepareMessage();
2385
+ }
2386
+ function encodeYieldseekerAuthToken(token) {
2387
+ const bytes = new TextEncoder().encode(JSON.stringify(token));
2388
+ let binary = "";
2389
+ for (const byte of bytes) binary += String.fromCharCode(byte);
2390
+ return btoa(binary);
2391
+ }
2392
+ var YieldseekerAuth = class {
2393
+ constructor(dependencies = {}) {
2394
+ this.dependencies = dependencies;
2395
+ }
2396
+ dependencies;
2397
+ tokens = /* @__PURE__ */ new Map();
2398
+ pending = /* @__PURE__ */ new Map();
2399
+ scopes = /* @__PURE__ */ new Map();
2400
+ key(state, chainId) {
2401
+ return `${state.walletAddress.toLowerCase()}:${chainId}`;
2402
+ }
2403
+ async getToken(state, chainId) {
2404
+ const key2 = this.key(state, chainId);
2405
+ const scope = { address: state.walletAddress, chainId };
2406
+ this.scopes.set(key2, scope);
2407
+ const cached = this.tokens.get(key2);
2408
+ if (cached) return cached;
2409
+ const persisted = readYieldseekerSession(scope.address, scope.chainId);
2410
+ if (persisted) {
2411
+ this.tokens.set(key2, persisted);
2412
+ return persisted;
2413
+ }
2414
+ const inFlight = this.pending.get(key2);
2415
+ if (inFlight) return inFlight;
2416
+ const request = this.sign(state, chainId).then((token) => {
2417
+ this.tokens.set(key2, token);
2418
+ writeYieldseekerSession(scope.address, scope.chainId, token);
2419
+ return token;
2420
+ });
2421
+ this.pending.set(key2, request);
2422
+ try {
2423
+ return await request;
2424
+ } finally {
2425
+ this.pending.delete(key2);
2426
+ }
2427
+ }
2428
+ clear(state, chainId) {
2429
+ if (!state || chainId === void 0) {
2430
+ for (const scope of this.scopes.values()) {
2431
+ clearYieldseekerSession(scope.address, scope.chainId);
2432
+ }
2433
+ this.tokens.clear();
2434
+ this.pending.clear();
2435
+ this.scopes.clear();
2436
+ return;
2437
+ }
2438
+ const key2 = this.key(state, chainId);
2439
+ this.tokens.delete(key2);
2440
+ this.pending.delete(key2);
2441
+ this.scopes.delete(key2);
2442
+ clearYieldseekerSession(state.walletAddress, chainId);
2443
+ }
2444
+ async sign(state, chainId) {
2445
+ const account = (0, import_viem4.getAddress)(state.walletAddress);
2446
+ const publicClient = (0, import_viem4.createPublicClient)({
2447
+ chain: import_chains2.base,
2448
+ transport: (0, import_viem4.custom)(state.provider)
2449
+ });
2450
+ const walletClient = (0, import_viem4.createWalletClient)({
2451
+ account,
2452
+ chain: import_chains2.base,
2453
+ transport: (0, import_viem4.custom)(state.provider)
2454
+ });
2455
+ await ensureWalletOnChain(
2456
+ publicClient,
2457
+ walletClient,
2458
+ 8453
2459
+ );
2460
+ const message = createYieldseekerSiweMessage(
2461
+ account,
2462
+ chainId,
2463
+ this.dependencies
2464
+ );
2465
+ const signature = await walletClient.signMessage({ account, message });
2466
+ return encodeYieldseekerAuthToken({ message, signature });
2467
+ }
2468
+ };
2469
+
2470
+ // src/agents/yieldseeker/yieldseeker.identity-cache.ts
2471
+ var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
2472
+ var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
2473
+ var memoryIdentities = /* @__PURE__ */ new Map();
2474
+ var storage3 = () => {
2475
+ if (typeof window === "undefined") return null;
2476
+ try {
2477
+ return window.localStorage;
2478
+ } catch {
2479
+ return null;
2480
+ }
2481
+ };
2482
+ var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
2483
+ function valid(value, walletAddress, chainId, now) {
2484
+ return Boolean(
2485
+ value && typeof value.userId === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(value.userId) && value.walletAddress.toLowerCase() === walletAddress.toLowerCase() && value.chainId === chainId && Number.isSafeInteger(value.expiresAt) && value.expiresAt > now
2486
+ );
2487
+ }
2488
+ function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
2489
+ if (typeof window === "undefined") return null;
2490
+ const key2 = keyFor(walletAddress, chainId);
2491
+ const store = storage3();
2492
+ let parsed = null;
2493
+ try {
2494
+ const raw2 = store?.getItem(key2);
2495
+ parsed = raw2 ? JSON.parse(raw2) : null;
2496
+ } catch {
2497
+ parsed = null;
2498
+ }
2499
+ const candidate = parsed ?? memoryIdentities.get(key2);
2500
+ if (valid(candidate, walletAddress, chainId, now)) {
2501
+ memoryIdentities.set(key2, candidate);
2502
+ return { userId: candidate.userId };
2503
+ }
2504
+ memoryIdentities.delete(key2);
2505
+ try {
2506
+ store?.removeItem(key2);
2507
+ } catch {
2508
+ }
2509
+ return null;
2510
+ }
2511
+ function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
2512
+ if (typeof window === "undefined") return;
2513
+ const identity = {
2514
+ userId,
2515
+ walletAddress,
2516
+ chainId,
2517
+ expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
2518
+ };
2519
+ if (!valid(identity, walletAddress, chainId, now)) return;
2520
+ const key2 = keyFor(walletAddress, chainId);
2521
+ memoryIdentities.set(key2, identity);
2522
+ try {
2523
+ storage3()?.setItem(key2, JSON.stringify(identity));
2524
+ } catch {
2525
+ }
2526
+ }
2527
+ function clearYieldseekerIdentity(walletAddress, chainId) {
2528
+ const key2 = keyFor(walletAddress, chainId);
2529
+ memoryIdentities.delete(key2);
2530
+ try {
2531
+ storage3()?.removeItem(key2);
2532
+ } catch {
2533
+ }
2534
+ }
2535
+
2536
+ // src/agents/yieldseeker/yieldseeker.client.ts
2537
+ var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2538
+ function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
2539
+ return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
2540
+ }
2541
+ var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
2542
+ var YieldseekerApiError = class extends Error {
2543
+ constructor(status, providerCode, responseFields) {
2544
+ super(`Yieldseeker request failed (${status}): ${providerCode}`);
2545
+ this.status = status;
2546
+ this.providerCode = providerCode;
2547
+ this.responseFields = responseFields;
2548
+ this.name = "YieldseekerApiError";
2549
+ }
2550
+ status;
2551
+ providerCode;
2552
+ responseFields;
2553
+ get isAuthenticationError() {
2554
+ return this.status === 401 || this.status === 403;
2555
+ }
2556
+ };
2557
+ function providerError(body, fallback) {
2558
+ if (!body || typeof body !== "object") return { code: fallback };
2559
+ const record = body;
2560
+ return {
2561
+ code: typeof record.message === "string" ? record.message : fallback,
2562
+ fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
2563
+ };
2564
+ }
2565
+ var YieldseekerApiClient = class {
2566
+ constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
2567
+ this.owneyApiKey = owneyApiKey;
2568
+ this.baseUrl = baseUrl;
2569
+ this.fetchFn = fetchFn;
2570
+ }
2571
+ owneyApiKey;
2572
+ baseUrl;
2573
+ fetchFn;
2574
+ async request(path, options = {}) {
2575
+ const controller = new AbortController();
2576
+ const timer = setTimeout(
2577
+ () => controller.abort(),
2578
+ options.timeoutMs ?? 15e3
2579
+ );
2580
+ try {
2581
+ const response = await this.fetchFn(`${this.baseUrl}${path}`, {
2582
+ method: options.method ?? "GET",
2583
+ headers: {
2584
+ "Content-Type": "application/json",
2585
+ "x-owney-api-key": this.owneyApiKey,
2586
+ ...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
2587
+ },
2588
+ body: options.body ? JSON.stringify(options.body) : void 0,
2589
+ signal: controller.signal
2590
+ });
2591
+ const payload = await response.json().catch(() => null);
2592
+ if (!response.ok) {
2593
+ const error = providerError(payload, `HTTP_${response.status}`);
2594
+ throw new YieldseekerApiError(
2595
+ response.status,
2596
+ error.code,
2597
+ error.fields
2598
+ );
2599
+ }
2600
+ if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
2601
+ return payload.data;
2602
+ }
2603
+ return payload;
2604
+ } catch (error) {
2605
+ if (error instanceof YieldseekerApiError) throw error;
2606
+ if (error instanceof DOMException && error.name === "AbortError") {
2607
+ throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
2608
+ }
2609
+ throw new YieldseekerApiError(0, "NETWORK_ERROR", {
2610
+ cause: error instanceof Error ? error.message : String(error)
2611
+ });
2612
+ } finally {
2613
+ clearTimeout(timer);
2614
+ }
2615
+ }
2616
+ };
2617
+
2618
+ // src/agents/yieldseeker/yieldseeker.mapper.ts
2619
+ var import_viem5 = require("viem");
2620
+
2621
+ // src/agents/yieldseeker/yieldseeker.types.ts
2622
+ var YIELDSEEKER_ASSET_METADATA = {
2623
+ USDC: {
2624
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2625
+ decimals: 6
2626
+ },
2627
+ WETH: {
2628
+ address: "0x4200000000000000000000000000000000000006",
2629
+ decimals: 18
2630
+ }
2631
+ };
2632
+
2633
+ // src/agents/yieldseeker/yieldseeker.mapper.ts
2634
+ function invalid(endpoint, detail) {
2635
+ throw new OwneyError(
2636
+ "AGENT_INVALID_RESPONSE",
2637
+ `Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
2638
+ { endpoint, detail },
2639
+ "yieldseeker"
2640
+ );
2641
+ }
2642
+ function raw(value, endpoint) {
2643
+ if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
2644
+ return invalid(endpoint, "expected a base-10 integer string");
2645
+ }
2646
+ return BigInt(value);
2647
+ }
2648
+ function decimal(value, decimals, endpoint) {
2649
+ return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
2650
+ }
2651
+ function usd(rawAmount, decimals, price) {
2652
+ return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
2653
+ }
2654
+ function percent(value) {
2655
+ const result = Number(value);
2656
+ return Number.isFinite(result) ? result * 100 : 0;
2657
+ }
2658
+ var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
2659
+ function publicApyAfterYieldseekerFee(value) {
2660
+ const grossPercent = percent(value);
2661
+ if (grossPercent <= 0) return grossPercent;
2662
+ const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
2663
+ return Math.round(netPercent * 1e12) / 1e12;
2664
+ }
2665
+ function riskAdjustedApyForDays(option, days) {
2666
+ if (days === "7D") return option.riskAdjustedApy7dAverage;
2667
+ if (days === "30D") return option.riskAdjustedApy30dAverage;
2668
+ return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
2669
+ }
2670
+ function assetAddressValue(record, address) {
2671
+ const entry = Object.entries(record).find(
2672
+ ([key2]) => key2.toLowerCase() === address.toLowerCase()
2673
+ );
2674
+ return entry?.[1] ?? "0";
2675
+ }
2676
+ function position(value, asset) {
2677
+ const option = value?.yieldOption;
2678
+ if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
2679
+ return invalid("yield positions", "missing vault metadata");
2680
+ }
2681
+ const decimals = YIELDSEEKER_ASSET_METADATA[asset].decimals;
2682
+ return {
2683
+ chain: "BASE",
2684
+ protocol: option.provider,
2685
+ protocolId: option.address,
2686
+ pool: option.name,
2687
+ asset,
2688
+ amount: decimal(value.assetsRaw, decimals, "yield positions"),
2689
+ amountRaw: String(value.assetsRaw),
2690
+ apy: percent(option.riskAdjustedApy),
2691
+ tvl: Number(option.totalDepositsUsd),
2692
+ liquidity: Number(option.withdrawableDepositsUsd)
2693
+ };
2694
+ }
2695
+ function mapYieldseekerBalances(contexts) {
2696
+ const tokens = [];
2697
+ const positions = [];
2698
+ let totalUsd = 0;
2699
+ for (const context of contexts) {
2700
+ const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
2701
+ const idle = assetAddressValue(
2702
+ context.snapshot.tokenBalances,
2703
+ metadata.address
2704
+ );
2705
+ tokens.push({
2706
+ chain: "BASE",
2707
+ chainId: 8453,
2708
+ asset: context.asset,
2709
+ amount: decimal(idle, metadata.decimals, "snapshot")
2710
+ });
2711
+ positions.push(
2712
+ ...context.positions.map((entry) => position(entry, context.asset))
2713
+ );
2714
+ totalUsd += usd(
2715
+ raw(context.snapshot.totalValueBase, "snapshot"),
2716
+ context.snapshot.baseAssetDecimals,
2717
+ context.snapshot.baseAssetPriceUsd
2718
+ );
2719
+ }
2720
+ return {
2721
+ ...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
2722
+ totalBalance: String(totalUsd),
2723
+ totalBalanceAsset: "usdc",
2724
+ tokens,
2725
+ positions
2726
+ };
2727
+ }
2728
+ function mapYieldseekerEarnings(contexts) {
2729
+ const tokens = [];
2730
+ let lifetimeEarnings = 0;
2731
+ for (const context of contexts) {
2732
+ const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
2733
+ tokens.push({
2734
+ chain: "BASE",
2735
+ chainId: 8453,
2736
+ asset: context.asset,
2737
+ amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
2738
+ });
2739
+ lifetimeEarnings += usd(
2740
+ amount,
2741
+ context.snapshot.baseAssetDecimals,
2742
+ context.snapshot.baseAssetPriceUsd
2743
+ );
2744
+ }
2745
+ return {
2746
+ smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2747
+ lifetimeEarnings,
2748
+ tokens
2749
+ };
2750
+ }
2751
+ function apyForDays(snapshot, days) {
2752
+ if (days === "7D") return percent(snapshot.apy7d);
2753
+ if (days === "30D") return percent(snapshot.apy30d);
2754
+ return percent((snapshot.apy7d + snapshot.apy30d) / 2);
2755
+ }
2756
+ function dailyApy(point) {
2757
+ const total = raw(point.totalValueBase, "historic position");
2758
+ const earned = raw(point.dailyYieldBase, "historic position");
2759
+ const principal = total - earned;
2760
+ if (principal <= 0n || earned === 0n) return 0;
2761
+ return Number(earned) / Number(principal) * 365 * 100;
2762
+ }
2763
+ function aggregateHistory(contexts, dayCount) {
2764
+ const byDate = /* @__PURE__ */ new Map();
2765
+ for (const context of contexts) {
2766
+ const points = context.historic?.dailyYieldSnapshots ?? [];
2767
+ for (const point of points.slice(-dayCount)) {
2768
+ const valueUsd = usd(
2769
+ raw(point.totalValueBase, "historic position"),
2770
+ point.baseAssetDecimals,
2771
+ point.baseAssetPriceUsd
2772
+ );
2773
+ const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
2774
+ current.weighted += dailyApy(point) * valueUsd;
2775
+ current.valueUsd += valueUsd;
2776
+ byDate.set(point.date, current);
2777
+ }
2778
+ }
2779
+ return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
2780
+ date,
2781
+ apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
2782
+ }));
2783
+ }
2784
+ function mapYieldseekerApy(walletAddress, contexts, days) {
2785
+ let weighted = 0;
2786
+ let totalUsd = 0;
2787
+ const byAsset = {};
2788
+ for (const context of contexts) {
2789
+ const valueUsd = usd(
2790
+ raw(context.snapshot.totalValueBase, "snapshot"),
2791
+ context.snapshot.baseAssetDecimals,
2792
+ context.snapshot.baseAssetPriceUsd
2793
+ );
2794
+ const apy = apyForDays(context.snapshot, days);
2795
+ weighted += apy * valueUsd;
2796
+ totalUsd += valueUsd;
2797
+ byAsset[context.asset] = apy;
2798
+ }
2799
+ const dayCount = Number(days.slice(0, -1));
2800
+ return {
2801
+ walletAddress,
2802
+ weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
2803
+ apyByChainAndAsset: { 8453: byAsset },
2804
+ history: aggregateHistory(contexts, dayCount)
2805
+ };
2806
+ }
2807
+ function actionType(value) {
2808
+ const normalized = value.toLowerCase();
2809
+ if (normalized.includes("deposit")) return "Deposit";
2810
+ if (normalized.includes("withdraw")) return "Withdraw";
2811
+ if (normalized.includes("yield") || normalized.includes("earn"))
2812
+ return "Earned";
2813
+ return "Rebalance";
2814
+ }
2815
+ function transactionHashes(details) {
2816
+ if (!details) return [];
2817
+ const values = [
2818
+ details.transactionHash,
2819
+ details.txHash,
2820
+ ...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
2821
+ ...Array.isArray(details.txHashes) ? details.txHashes : []
2822
+ ];
2823
+ return values.filter(
2824
+ (value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
2825
+ ).filter((value, index, all) => all.indexOf(value) === index);
2826
+ }
2827
+ function actionEntry(action) {
2828
+ return {
2829
+ agent: "yieldseeker",
2830
+ action: actionType(action.actionType),
2831
+ date: action.createdDate,
2832
+ oldApy: null,
2833
+ newApy: null,
2834
+ transactions: [
2835
+ {
2836
+ txHashes: transactionHashes(action.details),
2837
+ chainId: 8453
2838
+ }
2839
+ ],
2840
+ rebalanceLog: []
2841
+ };
2842
+ }
2843
+ function movementEntry(movement, wallet, agent, asset) {
2844
+ const isDeposit = movement.toAddress.toLowerCase() === wallet.walletAddress.toLowerCase();
2845
+ return {
2846
+ agent: "yieldseeker",
2847
+ action: isDeposit ? "Deposit" : "Withdraw",
2848
+ date: movement.blockDate,
2849
+ oldApy: null,
2850
+ newApy: null,
2851
+ transactions: [
2852
+ {
2853
+ txHashes: [movement.transactionHash],
2854
+ chainId: agent.chainId,
2855
+ tokenSymbol: asset,
2856
+ amount: decimal(
2857
+ movement.assetAmount,
2858
+ YIELDSEEKER_ASSET_METADATA[asset].decimals,
2859
+ "historic position"
2860
+ )
2861
+ }
2862
+ ],
2863
+ rebalanceLog: []
2864
+ };
2865
+ }
2866
+ function mapYieldseekerHistory(contexts, options) {
2867
+ const entries = contexts.flatMap((context) => [
2868
+ ...(context.actions ?? []).map(actionEntry),
2869
+ ...(context.historic?.movements ?? []).map(
2870
+ (movement) => movementEntry(movement, context.wallet, context.agent, context.asset)
2871
+ )
2872
+ ]);
2873
+ const filtered = entries.filter(
2874
+ (entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
2875
+ ).filter(
2876
+ (entry, index, all) => all.findIndex(
2877
+ (candidate) => candidate.action === entry.action && candidate.date === entry.date && candidate.transactions[0]?.txHashes[0] === entry.transactions[0]?.txHashes[0]
2878
+ ) === index
2879
+ ).sort((left, right) => right.date.localeCompare(left.date));
2880
+ return {
2881
+ data: filtered.slice(0, options.limit),
2882
+ // v1 returns the whole action/movement collection and defines no cursor.
2883
+ // Report a terminal page so callers never loop over the same prefix.
2884
+ hasMore: false
2885
+ };
2886
+ }
2887
+ function mapYieldseekerProfile(address, contexts) {
2888
+ const protocols = /* @__PURE__ */ new Set();
2889
+ for (const context of contexts) {
2890
+ for (const current of context.positions) {
2891
+ if (current.yieldOption?.provider) {
2892
+ protocols.add(String(current.yieldOption.provider));
2893
+ }
2894
+ }
2895
+ }
2896
+ return {
2897
+ address,
2898
+ smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2899
+ chains: contexts.length > 0 ? [8453] : [],
2900
+ hasActiveSessionKey: contexts.some(
2901
+ (context) => context.wallet.initializedDate != null
2902
+ ),
2903
+ protocols: [...protocols]
2904
+ };
2905
+ }
2906
+ function mapYieldseekerAgentApy(options, days) {
2907
+ const perAsset = {};
2908
+ const all = [];
2909
+ for (const entry of options) {
2910
+ const apys = entry.yieldOptions.map(
2911
+ (option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
2912
+ ).filter(Number.isFinite);
2913
+ if (apys.length === 0) continue;
2914
+ const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
2915
+ perAsset[entry.asset] = average;
2916
+ all.push(average);
2917
+ }
2918
+ return {
2919
+ averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
2920
+ detailedApys: { apyPerAsset: { 8453: perAsset } }
2921
+ };
2922
+ }
2923
+
2924
+ // src/agents/yieldseeker/yieldseeker.agent.ts
2925
+ var OWNEY_AGENT_NAME = "owney";
2926
+ var YIELDSEEKER_AGENT_WALLET_ABI = [
2927
+ {
2928
+ type: "function",
2929
+ name: "withdrawAssetToUser",
2930
+ stateMutability: "nonpayable",
2931
+ inputs: [
2932
+ { name: "recipient", type: "address" },
2933
+ { name: "asset", type: "address" },
2934
+ { name: "amount", type: "uint256" }
2935
+ ],
2936
+ outputs: []
2937
+ },
2938
+ {
2939
+ type: "function",
2940
+ name: "withdrawAllAssetToUser",
2941
+ stateMutability: "nonpayable",
2942
+ inputs: [
2943
+ { name: "recipient", type: "address" },
2944
+ { name: "asset", type: "address" }
2945
+ ],
2946
+ outputs: []
2947
+ }
2948
+ ];
2949
+ function query(params) {
2950
+ const search = new URLSearchParams();
2951
+ for (const [key2, value] of Object.entries(params)) {
2952
+ if (value !== void 0) search.set(key2, String(value));
2953
+ }
2954
+ const encoded = search.toString();
2955
+ return encoded ? `?${encoded}` : "";
2956
+ }
2957
+ var YieldseekerAgent = class {
2958
+ id = "yieldseeker";
2959
+ balanceComposition = "tokens-plus-positions";
2960
+ supportedChainIds = [8453];
2961
+ supportedAssets = [
2962
+ {
2963
+ chainId: 8453,
2964
+ chain: "BASE",
2965
+ assets: [
2966
+ { symbol: "USDC", minDepositAmount: "10000000" },
2967
+ { symbol: "WETH", minDepositAmount: "1" }
2968
+ ]
2969
+ }
2970
+ ];
2971
+ api;
2972
+ auth;
2973
+ transactionExecutor;
2974
+ unwindReceiptWaiter;
2975
+ agentContexts = /* @__PURE__ */ new Map();
2976
+ users = /* @__PURE__ */ new Map();
2977
+ pendingAgents = /* @__PURE__ */ new Map();
2978
+ constructor(owneyApiKey, options = {}) {
2979
+ this.api = new YieldseekerApiClient(
2980
+ owneyApiKey,
2981
+ options.baseUrl ?? getYieldseekerProxyBaseUrl(),
2982
+ options.fetchFn
2983
+ );
2984
+ this.auth = new YieldseekerAuth(options.auth);
2985
+ this.transactionExecutor = options.transactionExecutor;
2986
+ this.unwindReceiptWaiter = options.unwindReceiptWaiter;
2987
+ }
2988
+ async disconnect() {
2989
+ this.auth.clear();
2990
+ for (const key2 of this.users.keys()) {
2991
+ const [walletAddress, chainId] = key2.split(":");
2992
+ clearYieldseekerIdentity(walletAddress, Number(chainId));
2993
+ }
2994
+ this.users.clear();
2995
+ this.agentContexts.clear();
2996
+ this.pendingAgents.clear();
2997
+ }
2998
+ async activateAgent(state, chainId, asset) {
2999
+ this.assertChain(chainId);
3000
+ const targetAsset = asset ?? "USDC";
3001
+ this.assertAsset(targetAsset);
3002
+ await this.ensureAgent(state, chainId, targetAsset);
3003
+ }
3004
+ async deposit(state, chainId, amount, asset, depositCallback) {
3005
+ this.assertChain(chainId);
3006
+ this.assertAsset(asset);
3007
+ if (BigInt(amount) <= 0n) {
3008
+ throw new OwneyError(
3009
+ "DEPOSIT_AMOUNT_BELOW_MINIMUM",
3010
+ "Yieldseeker deposits must be greater than zero.",
3011
+ { amount, minDepositAmount: "1" },
3012
+ this.id
3013
+ );
3014
+ }
3015
+ const context = await this.ensureAgent(state, chainId, asset);
3016
+ let txHash;
3017
+ try {
3018
+ if (depositCallback) {
3019
+ provideDepositVerificationContext(depositCallback, {
3020
+ agentId: "yieldseeker",
3021
+ signature: await this.auth.getToken(state, chainId),
3022
+ userId: context.user.userId,
3023
+ yieldseekerAgentId: context.agent.agentId
3024
+ });
3025
+ txHash = await depositCallback(
3026
+ context.wallet.walletAddress,
3027
+ chainId,
3028
+ amount
3029
+ );
3030
+ await this.waitForReceipt(state, chainId, txHash);
3031
+ } else {
3032
+ txHash = await this.submitTransaction(state, chainId, {
3033
+ from: (0, import_viem6.getAddress)(state.walletAddress),
3034
+ to: YIELDSEEKER_ASSET_METADATA[asset].address,
3035
+ data: (0, import_viem6.encodeFunctionData)({
3036
+ abi: import_viem6.erc20Abi,
3037
+ functionName: "transfer",
3038
+ args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
3039
+ }),
3040
+ value: "0",
3041
+ chainId
3042
+ });
3043
+ }
3044
+ await this.deployAfterFunding(state, chainId, context);
3045
+ } finally {
3046
+ await this.refreshSnapshotAfterMovement(
3047
+ state,
3048
+ chainId,
3049
+ context,
3050
+ "deposit"
3051
+ );
3052
+ }
3053
+ return {
3054
+ txHash,
3055
+ smartWallet: context.wallet.walletAddress,
3056
+ amount
3057
+ };
3058
+ }
3059
+ async withdraw(state, chainId, asset, amount) {
3060
+ this.assertChain(chainId);
3061
+ this.assertAsset(asset);
3062
+ if (amount !== void 0 && BigInt(amount) <= 0n) {
3063
+ throw new OwneyError(
3064
+ "WITHDRAW_FAILED",
3065
+ "Yieldseeker withdrawals must be greater than zero.",
3066
+ { amount },
3067
+ this.id
3068
+ );
3069
+ }
3070
+ const context = await this.findAgent(state, chainId, asset);
3071
+ if (!context) {
3072
+ throw new OwneyError(
3073
+ "WITHDRAW_INSUFFICIENT_BALANCE",
3074
+ `No Yieldseeker ${asset} agent exists for this wallet.`,
3075
+ { asset, available: "0" },
3076
+ this.id
3077
+ );
3078
+ }
3079
+ try {
3080
+ const portfolio = await this.loadPortfolioContext(
3081
+ state,
3082
+ chainId,
3083
+ context
3084
+ );
3085
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3086
+ const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
3087
+ ([address]) => address.toLowerCase() === metadata.address.toLowerCase()
3088
+ );
3089
+ const idle = BigInt(idleEntry?.[1] ?? "0");
3090
+ const deployed = portfolio.positions.reduce(
3091
+ (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
3092
+ 0n
3093
+ );
3094
+ const totalAvailable = idle + deployed;
3095
+ const requested = amount === void 0 ? totalAvailable : BigInt(amount);
3096
+ if (requested > totalAvailable) {
3097
+ throw new OwneyError(
3098
+ "WITHDRAW_INSUFFICIENT_BALANCE",
3099
+ `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
3100
+ {
3101
+ asset,
3102
+ requested: requested.toString(),
3103
+ available: totalAvailable.toString()
3104
+ },
3105
+ this.id
3106
+ );
3107
+ }
3108
+ let remaining = requested > idle ? requested - idle : 0n;
3109
+ for (const position2 of portfolio.positions) {
3110
+ if (remaining === 0n) break;
3111
+ const available = BigInt(position2.withdrawableAssetsRaw);
3112
+ if (available <= 0n) continue;
3113
+ const assetsRaw = available < remaining ? available : remaining;
3114
+ const response = await this.walletRequest(
3115
+ state,
3116
+ chainId,
3117
+ this.agentPath(context, "withdraw-from-position"),
3118
+ {
3119
+ method: "POST",
3120
+ body: {
3121
+ chainId,
3122
+ vaultAddress: position2.yieldOption.address,
3123
+ assetsRaw: assetsRaw.toString()
3124
+ }
3125
+ }
3126
+ );
3127
+ if (!this.isTransactionHash(response?.transactionHash)) {
3128
+ throw this.invalidResponse("position withdrawal");
3129
+ }
3130
+ await this.waitForReceipt(state, chainId, response.transactionHash);
3131
+ remaining -= assetsRaw;
3132
+ }
3133
+ if (remaining > 0n) {
3134
+ throw this.invalidResponse("yield positions", {
3135
+ reason: "Withdrawable positions could not cover the request.",
3136
+ remaining: remaining.toString()
3137
+ });
3138
+ }
3139
+ const account = (0, import_viem6.getAddress)(state.walletAddress);
3140
+ const txHash = await this.submitTransaction(state, chainId, {
3141
+ from: account,
3142
+ to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
3143
+ data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
3144
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3145
+ functionName: "withdrawAllAssetToUser",
3146
+ args: [account, metadata.address]
3147
+ }) : (0, import_viem6.encodeFunctionData)({
3148
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3149
+ functionName: "withdrawAssetToUser",
3150
+ args: [account, metadata.address, requested]
3151
+ }),
3152
+ value: "0",
3153
+ chainId
3154
+ });
3155
+ return {
3156
+ txHash,
3157
+ type: amount === void 0 ? "full" : "partial",
3158
+ amount: requested.toString()
3159
+ };
3160
+ } finally {
3161
+ await this.refreshSnapshotAfterMovement(
3162
+ state,
3163
+ chainId,
3164
+ context,
3165
+ "withdrawal"
3166
+ );
3167
+ }
3168
+ }
3169
+ async getBalances(state, chainId) {
3170
+ this.assertChain(chainId);
3171
+ return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
3172
+ }
3173
+ async getEarnings(state, chainId) {
3174
+ this.assertChain(chainId);
3175
+ return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
3176
+ }
3177
+ async getAccountApy(state, chainId, days, tokenSymbol) {
3178
+ this.assertChain(chainId);
3179
+ const asset = tokenSymbol?.toUpperCase();
3180
+ if (asset !== void 0) this.assertAsset(asset);
3181
+ const contexts = await this.loadPortfolio(state, chainId, {
3182
+ ...asset ? { asset } : {},
3183
+ historic: true
3184
+ });
3185
+ return mapYieldseekerApy(state.walletAddress, contexts, days);
3186
+ }
3187
+ async getHistory(state, chainId, options) {
3188
+ this.assertChain(chainId);
3189
+ const asset = options?.tokenSymbol?.toUpperCase();
3190
+ if (asset !== void 0) this.assertAsset(asset);
3191
+ const contexts = await this.loadPortfolio(state, chainId, {
3192
+ ...asset ? { asset } : {},
3193
+ historic: true,
3194
+ actions: true
3195
+ });
3196
+ return mapYieldseekerHistory(contexts, {
3197
+ limit: options?.limit ?? 10,
3198
+ ...options?.fromDate ? { fromDate: options.fromDate } : {},
3199
+ ...options?.toDate ? { toDate: options.toDate } : {}
3200
+ });
3201
+ }
3202
+ async getUserProfile(state, chainId) {
3203
+ this.assertChain(chainId);
3204
+ return mapYieldseekerProfile(
3205
+ state.walletAddress,
3206
+ await this.loadPortfolio(state, chainId, {})
3207
+ );
3208
+ }
3209
+ async getAgentApy(days, options) {
3210
+ this.assertOptionalChain(options?.chainId);
3211
+ const requested = options?.tokenSymbol?.toUpperCase();
3212
+ if (requested !== void 0) this.assertAsset(requested);
3213
+ const assets = requested ? [requested] : ["USDC", "WETH"];
3214
+ const values = await Promise.all(
3215
+ assets.map(async (asset) => {
3216
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3217
+ const response = await this.api.request(
3218
+ `/chains/8453/assets/${metadata.address}/yield-options`
3219
+ );
3220
+ if (!Array.isArray(response?.yieldOptions)) {
3221
+ throw this.invalidResponse("yield options");
3222
+ }
3223
+ return { asset, yieldOptions: response.yieldOptions };
3224
+ })
3225
+ );
3226
+ return mapYieldseekerAgentApy(values, days);
3227
+ }
3228
+ userKey(state, chainId) {
3229
+ return `${state.walletAddress.toLowerCase()}:${chainId}`;
3230
+ }
3231
+ contextKey(state, chainId, asset) {
3232
+ return `${this.userKey(state, chainId)}:${asset}`;
3233
+ }
3234
+ async resolveUser(state, chainId) {
3235
+ const key2 = this.userKey(state, chainId);
3236
+ const inMemory = this.users.get(key2);
3237
+ if (inMemory) return inMemory;
3238
+ const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
3239
+ if (persisted) {
3240
+ this.users.set(key2, persisted);
3241
+ return persisted;
3242
+ }
3243
+ const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
3244
+ let user = null;
3245
+ try {
3246
+ const login = await this.providerRequest(
3247
+ state,
3248
+ chainId,
3249
+ "/users/login-with-wallet",
3250
+ { method: "POST", body: { walletAddress } }
3251
+ );
3252
+ user = login?.user ?? null;
3253
+ if (!user) {
3254
+ throw this.invalidResponse("wallet login", {
3255
+ reason: "A successful login returned no user."
3256
+ });
3257
+ }
3258
+ } catch (error) {
3259
+ if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
3260
+ if (error instanceof OwneyError) throw error;
3261
+ throw this.mapApiError(error);
3262
+ }
3263
+ let created;
3264
+ try {
3265
+ created = await this.providerRequest(
3266
+ state,
3267
+ chainId,
3268
+ "/users",
3269
+ {
3270
+ method: "POST",
3271
+ body: {
3272
+ walletAddress,
3273
+ username: `owney-${walletAddress.slice(2).toLowerCase()}`
3274
+ }
3275
+ }
3276
+ );
3277
+ } catch (createError) {
3278
+ throw this.mapApiError(createError);
3279
+ }
3280
+ user = created?.user ?? null;
3281
+ }
3282
+ if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
3283
+ throw this.invalidResponse("wallet identity");
3284
+ }
3285
+ const resolved = { userId: user.userId };
3286
+ this.users.set(key2, resolved);
3287
+ writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
3288
+ return resolved;
3289
+ }
3290
+ forgetUser(state, chainId) {
3291
+ this.users.delete(this.userKey(state, chainId));
3292
+ clearYieldseekerIdentity(state.walletAddress, chainId);
3293
+ }
3294
+ async ensureAgent(state, chainId, asset) {
3295
+ const key2 = this.contextKey(state, chainId, asset);
3296
+ const cached = this.agentContexts.get(key2);
3297
+ if (cached) return cached;
3298
+ const pending = this.pendingAgents.get(key2);
3299
+ if (pending) return pending;
3300
+ const request = this.resolveAgent(state, chainId, asset, true).then(
3301
+ (context) => {
3302
+ if (!context) throw this.invalidResponse("agent creation");
3303
+ this.agentContexts.set(key2, context);
3304
+ return context;
3305
+ }
3306
+ );
3307
+ this.pendingAgents.set(key2, request);
3308
+ try {
3309
+ return await request;
3310
+ } finally {
3311
+ this.pendingAgents.delete(key2);
3312
+ }
3313
+ }
3314
+ async findAgent(state, chainId, asset) {
3315
+ const key2 = this.contextKey(state, chainId, asset);
3316
+ const cached = this.agentContexts.get(key2);
3317
+ if (cached) return cached;
3318
+ const context = await this.resolveAgent(state, chainId, asset, false);
3319
+ if (context) this.agentContexts.set(key2, context);
3320
+ return context;
3321
+ }
3322
+ async resolveAgent(state, chainId, asset, createIfMissing) {
3323
+ const user = await this.resolveUser(state, chainId);
3324
+ const response = await this.walletRequest(
3325
+ state,
3326
+ chainId,
3327
+ `/users/${user.userId}/agents`
3328
+ );
3329
+ if (!Array.isArray(response?.agents)) {
3330
+ throw this.invalidResponse("agent list");
3331
+ }
3332
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3333
+ let agent = response.agents.find(
3334
+ (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3335
+ );
3336
+ if (!agent && createIfMissing) {
3337
+ const created = await this.walletRequest(
3338
+ state,
3339
+ chainId,
3340
+ `/users/${user.userId}/agents`,
3341
+ {
3342
+ method: "POST",
3343
+ body: {
3344
+ name: OWNEY_AGENT_NAME,
3345
+ emoji: "\u{1F989}",
3346
+ chainId,
3347
+ assetAddress: metadata.address,
3348
+ type: "vault",
3349
+ rulePreset: null
3350
+ }
3351
+ }
3352
+ );
3353
+ agent = created?.agent;
3354
+ }
3355
+ if (!agent) return null;
3356
+ this.assertAgent(agent);
3357
+ const walletResponse = await this.walletRequest(
3358
+ state,
3359
+ chainId,
3360
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
3361
+ );
3362
+ if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3363
+ throw this.invalidResponse("agent wallet");
3364
+ }
3365
+ return { user, agent, wallet: walletResponse.agentWallet, asset };
3366
+ }
3367
+ async loadPortfolio(state, chainId, options) {
3368
+ const user = await this.resolveUser(state, chainId);
3369
+ const response = await this.walletRequest(
3370
+ state,
3371
+ chainId,
3372
+ `/users/${user.userId}/agents`
3373
+ );
3374
+ if (!Array.isArray(response?.agents)) {
3375
+ throw this.invalidResponse("agent list");
3376
+ }
3377
+ const contexts = [];
3378
+ for (const agent of response.agents) {
3379
+ const asset = this.assetForAgent(agent);
3380
+ if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3381
+ continue;
3382
+ }
3383
+ this.assertAgent(agent);
3384
+ const walletResponse = await this.walletRequest(
3385
+ state,
3386
+ chainId,
3387
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
3388
+ );
3389
+ if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3390
+ throw this.invalidResponse("agent wallet");
3391
+ }
3392
+ const context = {
3393
+ user,
3394
+ agent,
3395
+ wallet: walletResponse.agentWallet,
3396
+ asset
3397
+ };
3398
+ this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3399
+ contexts.push(context);
3400
+ }
3401
+ return Promise.all(
3402
+ contexts.map(
3403
+ (context) => this.loadPortfolioContext(state, chainId, context, options)
3404
+ )
3405
+ );
3406
+ }
3407
+ async loadPortfolioContext(state, chainId, context, options = {}) {
3408
+ const [snapshot, positions, historic, actions] = await Promise.all([
3409
+ this.walletRequest(
3410
+ state,
3411
+ chainId,
3412
+ `${this.agentPath(context, "snapshot")}${query({
3413
+ shouldOnlyUseRecentValue: true,
3414
+ shouldAllowStaleOnError: true
3415
+ })}`
3416
+ ),
3417
+ this.walletRequest(
3418
+ state,
3419
+ chainId,
3420
+ this.agentPath(context, "yield-positions")
3421
+ ),
3422
+ options.historic ? this.walletRequest(
3423
+ state,
3424
+ chainId,
3425
+ this.agentPath(context, "wallet/historic-position")
3426
+ ) : Promise.resolve(void 0),
3427
+ options.actions ? this.walletRequest(
3428
+ state,
3429
+ chainId,
3430
+ this.agentPath(context, "actions")
3431
+ ) : Promise.resolve(void 0)
3432
+ ]);
3433
+ if (!snapshot?.agentSnapshot) {
3434
+ throw this.invalidResponse("agent snapshot");
3435
+ }
3436
+ if (!Array.isArray(positions?.yieldPositions)) {
3437
+ throw this.invalidResponse("yield positions");
3438
+ }
3439
+ return {
3440
+ ...context,
3441
+ snapshot: snapshot.agentSnapshot,
3442
+ positions: positions.yieldPositions,
3443
+ ...historic?.position ? { historic: historic.position } : {},
3444
+ ...actions?.actions ? { actions: actions.actions } : {}
3445
+ };
3446
+ }
3447
+ async deployAfterFunding(state, chainId, context) {
3448
+ if (context.wallet.initializedDate != null) return;
3449
+ try {
3450
+ const deployed = await this.walletRequest(
3451
+ state,
3452
+ chainId,
3453
+ this.agentPath(context, "deploy"),
3454
+ { method: "POST", body: {} }
3455
+ );
3456
+ if (deployed?.agentWallet) {
3457
+ context.wallet = deployed.agentWallet;
3458
+ }
3459
+ } catch (error) {
3460
+ console.warn(
3461
+ "[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
3462
+ error
3463
+ );
3464
+ }
3465
+ }
3466
+ async refreshSnapshotAfterMovement(state, chainId, context, movement) {
3467
+ try {
3468
+ const response = await this.walletRequest(
3469
+ state,
3470
+ chainId,
3471
+ `${this.agentPath(context, "snapshot")}${query({
3472
+ shouldForceRefresh: true
3473
+ })}`
3474
+ );
3475
+ if (!response?.agentSnapshot) {
3476
+ throw this.invalidResponse("agent snapshot refresh");
3477
+ }
3478
+ } catch (error) {
3479
+ console.warn(
3480
+ `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3481
+ error
3482
+ );
3483
+ }
3484
+ }
3485
+ agentPath(context, suffix) {
3486
+ return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
3487
+ }
3488
+ async walletRequest(state, chainId, path, options = {}) {
3489
+ try {
3490
+ return await this.providerRequest(state, chainId, path, options);
3491
+ } catch (error) {
3492
+ throw this.mapApiError(error);
3493
+ }
3494
+ }
3495
+ async providerRequest(state, chainId, path, options = {}) {
3496
+ this.assertChain(chainId);
3497
+ const request = (signature2) => this.api.request(path, {
3498
+ ...options,
3499
+ signature: signature2
3500
+ });
3501
+ let signature = await this.auth.getToken(state, chainId);
3502
+ try {
3503
+ return await request(signature);
3504
+ } catch (error) {
3505
+ if (!(error instanceof YieldseekerApiError)) throw error;
3506
+ if (error.providerCode === "NO_USER") throw error;
3507
+ if (!error.isAuthenticationError) throw error;
3508
+ this.auth.clear(state, chainId);
3509
+ signature = await this.auth.getToken(state, chainId);
3510
+ try {
3511
+ return await request(signature);
3512
+ } catch (retryError) {
3513
+ if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
3514
+ this.forgetUser(state, chainId);
3515
+ }
3516
+ throw retryError;
3517
+ }
3518
+ }
3519
+ }
3520
+ mapApiError(error) {
3521
+ if (!(error instanceof YieldseekerApiError)) {
3522
+ return new OwneyError(
3523
+ "AGENT_API_ERROR",
3524
+ "Yieldseeker request failed.",
3525
+ { cause: error instanceof Error ? error.message : String(error) },
3526
+ this.id
3527
+ );
3528
+ }
3529
+ const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
3530
+ return new OwneyError(
3531
+ code,
3532
+ `Yieldseeker request failed: ${error.providerCode}.`,
3533
+ {
3534
+ statusCode: error.status,
3535
+ providerCode: error.providerCode,
3536
+ ...error.responseFields ? { fields: error.responseFields } : {}
3537
+ },
3538
+ this.id
3539
+ );
3540
+ }
3541
+ async submitTransaction(state, chainId, transaction) {
3542
+ if (this.transactionExecutor) {
3543
+ return this.transactionExecutor(state, chainId, transaction);
3544
+ }
3545
+ this.assertTransaction(transaction, state, chainId);
3546
+ const account = (0, import_viem6.getAddress)(state.walletAddress);
3547
+ const walletClient = (0, import_viem6.createWalletClient)({
3548
+ account,
3549
+ chain: import_chains3.base,
3550
+ transport: (0, import_viem6.custom)(state.provider)
3551
+ });
3552
+ const publicClient = (0, import_viem6.createPublicClient)({
3553
+ chain: import_chains3.base,
3554
+ transport: (0, import_viem6.custom)(state.provider)
3555
+ });
3556
+ await ensureWalletOnChain(
3557
+ publicClient,
3558
+ walletClient,
3559
+ 8453
3560
+ );
3561
+ const hash = await walletClient.sendTransaction({
3562
+ account,
3563
+ chain: import_chains3.base,
3564
+ to: (0, import_viem6.getAddress)(transaction.to),
3565
+ data: transaction.data,
3566
+ value: BigInt(transaction.value)
3567
+ });
3568
+ const receipt = await publicClient.waitForTransactionReceipt({
3569
+ hash,
3570
+ confirmations: 1
3571
+ });
3572
+ if (receipt.status !== "success") {
3573
+ throw new OwneyError(
3574
+ "AGENT_TRANSACTION_REVERTED",
3575
+ `Yieldseeker transaction reverted (${hash}).`,
3576
+ { transactionHash: hash },
3577
+ this.id
3578
+ );
3579
+ }
3580
+ return hash;
3581
+ }
3582
+ async waitForReceipt(state, chainId, transactionHash) {
3583
+ if (this.unwindReceiptWaiter) {
3584
+ await this.unwindReceiptWaiter(state, chainId, transactionHash);
3585
+ return;
3586
+ }
3587
+ const publicClient = (0, import_viem6.createPublicClient)({
3588
+ chain: import_chains3.base,
3589
+ transport: (0, import_viem6.custom)(state.provider)
3590
+ });
3591
+ const receipt = await publicClient.waitForTransactionReceipt({
3592
+ hash: transactionHash,
3593
+ confirmations: 1
3594
+ });
3595
+ if (receipt.status !== "success") {
3596
+ throw new OwneyError(
3597
+ "AGENT_TRANSACTION_REVERTED",
3598
+ `Yieldseeker transaction reverted (${transactionHash}).`,
3599
+ { transactionHash },
3600
+ this.id
3601
+ );
3602
+ }
3603
+ }
3604
+ assertTransaction(transaction, state, chainId) {
3605
+ if (!transaction || typeof transaction.from !== "string" || !(0, import_viem6.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem6.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem6.getAddress)(transaction.from) !== (0, import_viem6.getAddress)(state.walletAddress)) {
3606
+ throw this.invalidResponse("transaction");
3607
+ }
3608
+ }
3609
+ assertAgent(agent) {
3610
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
3611
+ throw this.invalidResponse("agent");
3612
+ }
3613
+ }
3614
+ isOwneyAgent(agent) {
3615
+ return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
3616
+ }
3617
+ assetForAgent(agent) {
3618
+ for (const asset of ["USDC", "WETH"]) {
3619
+ if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
3620
+ return asset;
3621
+ }
3622
+ }
3623
+ return null;
3624
+ }
3625
+ isTransactionHash(value) {
3626
+ return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
3627
+ }
3628
+ assertChain(chainId) {
3629
+ if (chainId !== 8453) {
3630
+ throw new OwneyError(
3631
+ "CHAIN_UNSUPPORTED",
3632
+ `Yieldseeker does not support chain ${chainId}.`,
3633
+ { chainId, supportedChainIds: [8453] },
3634
+ this.id
3635
+ );
3636
+ }
3637
+ }
3638
+ assertOptionalChain(chainId) {
3639
+ if (chainId !== void 0) this.assertChain(chainId);
3640
+ }
3641
+ assertAsset(asset) {
3642
+ if (asset !== "USDC" && asset !== "WETH") {
3643
+ throw new OwneyError(
3644
+ "ASSET_UNSUPPORTED",
3645
+ `Yieldseeker does not support asset ${asset} in the Owney rollout.`,
3646
+ {
3647
+ asset,
3648
+ supportedAssets: ["USDC", "WETH"],
3649
+ providerAlsoAdvertises: ["cbBTC"]
3650
+ },
3651
+ this.id
3652
+ );
3653
+ }
3654
+ }
3655
+ invalidResponse(operation, details = {}) {
3656
+ return new OwneyError(
3657
+ "AGENT_INVALID_RESPONSE",
3658
+ `Yieldseeker returned an invalid ${operation} response.`,
3659
+ details,
3660
+ this.id
3661
+ );
3662
+ }
3663
+ };
3664
+
3665
+ // src/lib/routing-api.ts
3666
+ var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3667
+ async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3668
+ const url = `${baseUrl}/api/v1/agent/org-config`;
3669
+ try {
3670
+ const res = await fetch(url, {
3671
+ method: "GET",
3672
+ headers: {
3673
+ "Content-Type": "application/json",
3674
+ "x-owney-api-key": `${apiKey}`
3675
+ }
3676
+ });
3677
+ if (!res.ok) {
3678
+ if (res.status !== 404) {
3679
+ console.warn(
3680
+ `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
3681
+ );
3682
+ }
3683
+ return null;
3684
+ }
3685
+ const json = await res.json();
3686
+ const policy = json.success ? json.data ?? null : null;
3687
+ debugLog(
3688
+ "owney-sdk",
3689
+ policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
3690
+ policy ?? void 0
3691
+ );
3692
+ return policy;
3693
+ } catch (error) {
3694
+ console.warn(
3695
+ "[owney-sdk] Could not read org agent config (non-fatal):",
3696
+ error instanceof Error ? error.message : String(error)
3697
+ );
3698
+ return null;
3699
+ }
3700
+ }
3701
+ async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3702
+ const url = `${baseUrl}/api/v1/agent/keys`;
3703
+ const res = await fetch(url, {
3704
+ method: "GET",
3705
+ headers: {
3706
+ "Content-Type": "application/json",
3707
+ "x-owney-api-key": `${apiKey}`
3708
+ }
3709
+ });
3710
+ if (!res.ok) {
3711
+ const text = await res.text().catch(() => "");
3712
+ throw new OwneyError(
3713
+ "API_ROUTING_ERROR",
3714
+ `Routing API error ${res.status}: ${text}`,
3715
+ { statusCode: res.status, responseBody: text }
3716
+ );
3717
+ }
3718
+ const json = await res.json();
3719
+ if (!json.success) {
3720
+ throw new OwneyError(
3721
+ "API_ROUTING_FAILED",
3722
+ `Routing API request failed: ${json.message}`,
3723
+ { message: json.message }
3724
+ );
3725
+ }
3726
+ return json.data;
3727
+ }
3728
+
3729
+ // src/lib/health-report.ts
3730
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3731
+ async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
3732
+ try {
3733
+ await fetch(`${baseUrl}/api/v1/agent/health-report`, {
3734
+ method: "POST",
3735
+ headers: {
3736
+ "Content-Type": "application/json",
3737
+ "x-owney-api-key": apiKey
3738
+ },
3739
+ body: JSON.stringify({
3740
+ agent_type: agentType,
3741
+ error_code: errorCode,
3742
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3743
+ })
3744
+ });
3745
+ } catch (err) {
3746
+ console.warn(
3747
+ `[owney-sdk] health-report failed for agent "${agentType}":`,
3748
+ err instanceof Error ? err.message : err
3749
+ );
3750
+ }
3751
+ }
3752
+ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
3753
+ try {
3754
+ return await fn();
3755
+ } catch (err) {
3756
+ const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3757
+ void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
3758
+ throw err;
3759
+ }
3760
+ }
3761
+
3762
+ // src/lib/helpers/withdraw-helper.ts
3763
+ var import_viem7 = require("viem");
3764
+ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
3765
+ const target = asset.toUpperCase();
3766
+ return agents.map((agent) => {
3767
+ const agentBalance = aggregated[agent.id];
3768
+ const tokenBalance = agentBalance?.tokens.find(
3769
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
3770
+ );
3771
+ let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
3772
+ if (agent.balanceComposition === "tokens-plus-positions") {
3773
+ const chainNameById = {
3774
+ 1: "ETHEREUM",
3775
+ 8453: "BASE",
3776
+ 42161: "ARBITRUM"
3777
+ };
3778
+ const targetChain = chainNameById[chainId];
3779
+ for (const position2 of agentBalance?.positions ?? []) {
3780
+ const positionChain = position2.chain.trim().toUpperCase();
3781
+ const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
3782
+ if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
3783
+ if (position2.amountRaw !== void 0) {
3784
+ try {
3785
+ balance += BigInt(position2.amountRaw);
3786
+ continue;
3787
+ } catch {
3788
+ }
3789
+ }
3790
+ balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
3791
+ }
3792
+ }
3793
+ return { agent, balance };
3794
+ });
3795
+ }
3796
+ function planProportionalShares(balances, requested, totalAvailable) {
3797
+ const plans = balances.map(({ agent, balance }) => ({
3798
+ agent,
3799
+ balance,
3800
+ planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
3801
+ }));
3802
+ const assigned = plans.reduce((s, p) => s + p.planned, 0n);
3803
+ let remainder = requested - assigned;
3804
+ const byHeadroom = [...plans].sort((a, b) => {
3805
+ const diff = b.balance - b.planned - (a.balance - a.planned);
3806
+ return diff > 0n ? 1 : diff < 0n ? -1 : 0;
3807
+ });
3808
+ for (const p of byHeadroom) {
3809
+ if (remainder === 0n) break;
3810
+ const headroom = p.balance - p.planned;
3811
+ if (headroom <= 0n) continue;
3812
+ const take = headroom < remainder ? headroom : remainder;
3813
+ p.planned += take;
3814
+ remainder -= take;
3815
+ }
3816
+ return plans;
3817
+ }
3818
+ function planDisabledDrain(disabled, requested) {
3819
+ const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
3820
+ (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
3821
+ );
3822
+ const plans = [];
3823
+ let remaining = requested;
3824
+ for (const { agent, balance } of sorted) {
3825
+ if (remaining === 0n) {
3826
+ plans.push({ agent, balance, planned: 0n });
3827
+ continue;
3828
+ }
3829
+ const take = balance < remaining ? balance : remaining;
3830
+ plans.push({ agent, balance, planned: take });
3831
+ remaining -= take;
3832
+ }
3833
+ return { plans, remaining };
2343
3834
  }
2344
- async function ensureWalletOnChain(pub, wallet, expected) {
2345
- const actual = await pub.getChainId();
2346
- if (actual === expected) return;
2347
- try {
2348
- await wallet.switchChain({ id: expected });
2349
- } catch (error) {
2350
- throw new OwneyError(
2351
- "CHAIN_MISMATCH",
2352
- `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2353
- {
2354
- expectedChainId: expected,
2355
- actualChainId: actual,
2356
- cause: error instanceof Error ? error.message : String(error)
2357
- }
2358
- );
3835
+ function redistributeShare(plans, fromIndex, amount, candidatePool) {
3836
+ const pool = candidatePool ?? plans.slice(fromIndex + 1);
3837
+ const candidates = pool.filter((c) => c.balance - c.planned > 0n);
3838
+ const totalHeadroom = candidates.reduce(
3839
+ (s, c) => s + (c.balance - c.planned),
3840
+ 0n
3841
+ );
3842
+ if (totalHeadroom === 0n) return;
3843
+ let distributed = 0n;
3844
+ for (const c of candidates) {
3845
+ const headroom = c.balance - c.planned;
3846
+ const proportional = headroom * amount / totalHeadroom;
3847
+ const give = proportional > headroom ? headroom : proportional;
3848
+ c.planned += give;
3849
+ distributed += give;
2359
3850
  }
2360
- const after = await pub.getChainId();
2361
- if (after !== expected) {
2362
- throw new OwneyError(
2363
- "CHAIN_MISMATCH",
2364
- `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2365
- { expectedChainId: expected, actualChainId: after }
2366
- );
3851
+ let leftover = amount - distributed;
3852
+ for (const c of candidates) {
3853
+ if (leftover === 0n) break;
3854
+ const headroom = c.balance - c.planned;
3855
+ if (headroom <= 0n) continue;
3856
+ const take = headroom < leftover ? headroom : leftover;
3857
+ c.planned += take;
3858
+ leftover -= take;
2367
3859
  }
2368
3860
  }
3861
+ function sumWithdrawnAmount(results) {
3862
+ return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
3863
+ }
2369
3864
 
2370
- // src/lib/sponsored-deposit.ts
2371
- var AUTH_WINDOW_SECONDS = 15 * 60;
2372
- function makeSponsoredDepositCallback(deps) {
2373
- const post = deps.httpPost ?? postSponsorTransferAuth;
2374
- return async (smartWallet, chainId, amount) => {
2375
- const cid = chainId;
2376
- const token = deps.tokenAddressByChain[cid];
2377
- if (!token) {
2378
- throw new OwneyError(
2379
- "CHAIN_UNSUPPORTED",
2380
- `No sponsored token configured for chain ${chainId}`
2381
- );
3865
+ // src/lib/helpers/account-apy-helper.ts
3866
+ function balanceForApyScope(balance, chainId, tokenSymbol) {
3867
+ if (!tokenSymbol) {
3868
+ const total = Number(balance.totalBalance);
3869
+ return Number.isFinite(total) && total > 0 ? total : 0;
3870
+ }
3871
+ const normalizedToken = tokenSymbol.toUpperCase();
3872
+ return balance.tokens.reduce((total, token) => {
3873
+ if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
3874
+ return total;
2382
3875
  }
2383
- const pub = deps.getPublicClient(cid);
2384
- const wallet = deps.getWalletClient(cid);
2385
- await ensureWalletOnChain(pub, wallet, cid);
2386
- try {
2387
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2388
- if (balance < BigInt(amount)) {
2389
- throw new OwneyError(
2390
- "DEPOSIT_INSUFFICIENT_BALANCE",
2391
- "Insufficient balance for this deposit.",
2392
- { token, chainId: cid, balance: balance.toString(), amount }
2393
- );
2394
- }
2395
- } catch (err) {
2396
- if (err instanceof OwneyError) throw err;
2397
- console.warn(
2398
- "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2399
- err instanceof Error ? err.message : String(err)
2400
- );
2401
- }
2402
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2403
- const validAfter = 0n;
2404
- const validBefore = BigInt(
2405
- Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2406
- );
2407
- const nonce = randomAuthNonce();
2408
- const typedData = buildTransferWithAuthorizationTypedData({
2409
- token,
2410
- chainId: cid,
2411
- tokenName,
2412
- tokenVersion,
2413
- message: {
2414
- from: deps.ownerAddress,
2415
- to: smartWallet,
2416
- value: BigInt(amount),
2417
- validAfter,
2418
- validBefore,
2419
- nonce
2420
- }
2421
- });
2422
- const authSignature = await wallet.signTypedData({
2423
- account: deps.ownerAddress,
2424
- ...typedData
2425
- });
2426
- deps.onApproved?.();
2427
- const result = await post({
2428
- baseUrl: deps.baseUrl,
2429
- apiKey: deps.apiKey,
2430
- body: {
2431
- chainId: cid,
2432
- token,
2433
- from: deps.ownerAddress,
2434
- to: smartWallet,
2435
- value: amount,
2436
- validAfter: validAfter.toString(),
2437
- validBefore: validBefore.toString(),
2438
- nonce,
2439
- authSignature,
2440
- tokenName,
2441
- tokenVersion
3876
+ const amount = Number(token.amount);
3877
+ return Number.isFinite(amount) && amount > 0 ? total + amount : total;
3878
+ }, 0);
3879
+ }
3880
+ function aggregateApyHistory(agentApys, agentBalances) {
3881
+ const byDate = /* @__PURE__ */ new Map();
3882
+ for (const [id, accountApy] of Object.entries(agentApys)) {
3883
+ const balance = agentBalances[id] ?? 0;
3884
+ if (!Number.isFinite(balance) || balance <= 0) continue;
3885
+ for (const point of accountApy.history ?? []) {
3886
+ const apy = Number(point.apy);
3887
+ if (!point.date || !Number.isFinite(apy)) continue;
3888
+ const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
3889
+ current.weightedSum += apy * balance;
3890
+ current.weight += balance;
3891
+ byDate.set(point.date, current);
3892
+ }
3893
+ }
3894
+ return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
3895
+ date,
3896
+ apy: value.weightedSum / value.weight
3897
+ }));
3898
+ }
3899
+ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
3900
+ const sums = {};
3901
+ const weights = {};
3902
+ for (const id of Object.keys(agentApys)) {
3903
+ const cells = agentApys[id].apyByChainAndAsset;
3904
+ const balance = agentBalances[id] ?? 0;
3905
+ if (!cells || balance <= 0) continue;
3906
+ for (const [chainKey, perAsset] of Object.entries(cells)) {
3907
+ if (!perAsset) continue;
3908
+ const chainId = Number(chainKey);
3909
+ for (const [asset, apyValue] of Object.entries(perAsset)) {
3910
+ const apy = Number(apyValue ?? 0);
3911
+ if (apy === 0) continue;
3912
+ sums[chainId] ??= {};
3913
+ weights[chainId] ??= {};
3914
+ sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
3915
+ weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2442
3916
  }
2443
- });
2444
- return result.txHash;
2445
- };
3917
+ }
3918
+ }
3919
+ const out = {};
3920
+ for (const chainKey of Object.keys(sums)) {
3921
+ const chainId = Number(chainKey);
3922
+ const perAssetOut = {};
3923
+ for (const asset of Object.keys(sums[chainId])) {
3924
+ const w = weights[chainId][asset];
3925
+ if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
3926
+ }
3927
+ if (Object.keys(perAssetOut).length > 0) {
3928
+ out[chainId] = perAssetOut;
3929
+ }
3930
+ }
3931
+ return out;
2446
3932
  }
2447
3933
 
3934
+ // src/client.ts
3935
+ var import_viem9 = require("viem");
3936
+ var import_chains4 = require("viem/chains");
3937
+
2448
3938
  // src/lib/sponsored-weth-deposit.ts
2449
3939
  var PERMIT_WINDOW_SECONDS = 15 * 60;
2450
3940
  function makeSponsoredWethCallback(deps) {
2451
3941
  const get = deps.httpGet ?? getSponsorRelayerAddress;
2452
3942
  const post = deps.httpPost ?? postSponsorPermit2Transfer;
2453
- return async (smartWallet, chainId, amount) => {
2454
- const cid = chainId;
2455
- const token = deps.tokenAddressByChain[cid];
2456
- if (!token) {
2457
- throw new OwneyError(
2458
- "CHAIN_UNSUPPORTED",
2459
- `No sponsored WETH configured for chain ${chainId}`
2460
- );
2461
- }
2462
- const amountWei = BigInt(amount);
2463
- const pub = deps.getPublicClient(cid);
2464
- const wallet = deps.getWalletClient(cid);
2465
- await ensureWalletOnChain(pub, wallet, cid);
2466
- try {
2467
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2468
- if (balance < amountWei) {
3943
+ return makeVerificationAwareDepositCallback(
3944
+ async (smartWallet, chainId, amount, verification) => {
3945
+ const cid = chainId;
3946
+ const token = deps.tokenAddressByChain[cid];
3947
+ if (!token) {
2469
3948
  throw new OwneyError(
2470
- "DEPOSIT_INSUFFICIENT_BALANCE",
2471
- "Insufficient WETH balance for this deposit.",
2472
- { token, chainId: cid, balance: balance.toString(), amount }
3949
+ "CHAIN_UNSUPPORTED",
3950
+ `No sponsored WETH configured for chain ${chainId}`
2473
3951
  );
2474
3952
  }
2475
- } catch (err) {
2476
- if (err instanceof OwneyError) throw err;
2477
- console.warn(
2478
- "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2479
- err instanceof Error ? err.message : String(err)
2480
- );
2481
- }
2482
- const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2483
- if (allowance < amountWei) {
2484
- throw new OwneyError(
2485
- "PERMIT2_APPROVAL_REQUIRED",
2486
- "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
2487
- { token, chainId: cid, allowance: allowance.toString(), amount }
2488
- );
2489
- }
2490
- const relayer = await get({
2491
- baseUrl: deps.baseUrl,
2492
- apiKey: deps.apiKey,
2493
- chainId: cid
2494
- });
2495
- const nonce = randomPermit2Nonce();
2496
- const deadline = BigInt(
2497
- Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
2498
- );
2499
- const typedData = buildPermitTransferFromTypedData({
2500
- chainId: cid,
2501
- message: {
2502
- permitted: { token, amount: amountWei },
2503
- spender: relayer,
2504
- nonce,
2505
- deadline
3953
+ const amountWei = BigInt(amount);
3954
+ const pub = deps.getPublicClient(cid);
3955
+ const wallet = deps.getWalletClient(cid);
3956
+ await ensureWalletOnChain(pub, wallet, cid);
3957
+ try {
3958
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3959
+ if (balance < amountWei) {
3960
+ throw new OwneyError(
3961
+ "DEPOSIT_INSUFFICIENT_BALANCE",
3962
+ "Insufficient WETH balance for this deposit.",
3963
+ { token, chainId: cid, balance: balance.toString(), amount }
3964
+ );
3965
+ }
3966
+ } catch (err) {
3967
+ if (err instanceof OwneyError) throw err;
3968
+ console.warn(
3969
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
3970
+ err instanceof Error ? err.message : String(err)
3971
+ );
2506
3972
  }
2507
- });
2508
- const signature = await wallet.signTypedData({
2509
- account: deps.ownerAddress,
2510
- ...typedData
2511
- });
2512
- deps.onApproved?.();
2513
- const result = await post({
2514
- baseUrl: deps.baseUrl,
2515
- apiKey: deps.apiKey,
2516
- body: {
2517
- chainId: cid,
3973
+ const allowance = await readPermit2Allowance(
3974
+ pub,
2518
3975
  token,
2519
- from: deps.ownerAddress,
2520
- to: smartWallet,
2521
- amount,
2522
- nonce: nonce.toString(),
2523
- deadline: deadline.toString(),
2524
- signature
3976
+ deps.ownerAddress
3977
+ );
3978
+ if (allowance < amountWei) {
3979
+ throw new OwneyError(
3980
+ "PERMIT2_APPROVAL_REQUIRED",
3981
+ "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3982
+ { token, chainId: cid, allowance: allowance.toString(), amount }
3983
+ );
2525
3984
  }
2526
- });
2527
- return result.txHash;
2528
- };
3985
+ const relayer = await get({
3986
+ baseUrl: deps.baseUrl,
3987
+ apiKey: deps.apiKey,
3988
+ chainId: cid
3989
+ });
3990
+ const nonce = randomPermit2Nonce();
3991
+ const deadline = BigInt(
3992
+ Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
3993
+ );
3994
+ const typedData = buildPermitTransferFromTypedData({
3995
+ chainId: cid,
3996
+ message: {
3997
+ permitted: { token, amount: amountWei },
3998
+ spender: relayer,
3999
+ nonce,
4000
+ deadline
4001
+ }
4002
+ });
4003
+ const signature = await wallet.signTypedData({
4004
+ account: deps.ownerAddress,
4005
+ ...typedData
4006
+ });
4007
+ deps.onApproved?.();
4008
+ const result = await post({
4009
+ baseUrl: deps.baseUrl,
4010
+ apiKey: deps.apiKey,
4011
+ ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
4012
+ body: {
4013
+ chainId: cid,
4014
+ token,
4015
+ from: deps.ownerAddress,
4016
+ to: smartWallet,
4017
+ amount,
4018
+ nonce: nonce.toString(),
4019
+ deadline: deadline.toString(),
4020
+ signature,
4021
+ ...verification?.agentId === "yieldseeker" ? {
4022
+ yieldseekerUserId: verification.userId,
4023
+ yieldseekerAgentId: verification.yieldseekerAgentId
4024
+ } : {}
4025
+ }
4026
+ });
4027
+ return result.txHash;
4028
+ }
4029
+ );
2529
4030
  }
2530
4031
 
2531
4032
  // src/lib/sponsored-calls-deposit.ts
2532
- var import_viem5 = require("viem");
4033
+ var import_viem8 = require("viem");
2533
4034
  var DEFAULT_POLL_INTERVAL_MS = 1500;
2534
4035
  var DEFAULT_MAX_POLLS = 30;
2535
4036
  async function paymasterSupported(provider, owner, chainId) {
@@ -2537,7 +4038,7 @@ async function paymasterSupported(provider, owner, chainId) {
2537
4038
  method: "wallet_getCapabilities",
2538
4039
  params: [owner]
2539
4040
  });
2540
- const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
4041
+ const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
2541
4042
  return Boolean(forChain?.paymasterService?.supported);
2542
4043
  }
2543
4044
  function makeSponsoredCallsCallback(deps) {
@@ -2555,7 +4056,7 @@ function makeSponsoredCallsCallback(deps) {
2555
4056
  }
2556
4057
  return new URL(configured, origin).toString();
2557
4058
  };
2558
- return async (smartWallet, chainId, amount) => {
4059
+ return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
2559
4060
  const cid = chainId;
2560
4061
  const token = deps.tokenAddressByChain[cid];
2561
4062
  if (!token) {
@@ -2571,22 +4072,48 @@ function makeSponsoredCallsCallback(deps) {
2571
4072
  { chainId }
2572
4073
  );
2573
4074
  }
2574
- const data = (0, import_viem5.encodeFunctionData)({
2575
- abi: import_viem5.erc20Abi,
4075
+ const data = (0, import_viem8.encodeFunctionData)({
4076
+ abi: import_viem8.erc20Abi,
2576
4077
  functionName: "transfer",
2577
4078
  args: [smartWallet, BigInt(amount)]
2578
4079
  });
4080
+ let paymasterUrl = absolutePaymasterUrl();
4081
+ if (verification?.agentId === "yieldseeker") {
4082
+ if (chainId !== 8453) {
4083
+ throw new OwneyError(
4084
+ "CHAIN_UNSUPPORTED",
4085
+ `Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
4086
+ );
4087
+ }
4088
+ const { intent } = await postPaymasterIntent({
4089
+ baseUrl: deps.routingApiBaseUrl,
4090
+ apiKey: deps.apiKey,
4091
+ yieldseekerSignature: verification.signature,
4092
+ body: {
4093
+ chainId,
4094
+ token,
4095
+ from: deps.ownerAddress,
4096
+ to: smartWallet,
4097
+ amount,
4098
+ yieldseekerUserId: verification.userId,
4099
+ yieldseekerAgentId: verification.yieldseekerAgentId
4100
+ }
4101
+ });
4102
+ const url = new URL(paymasterUrl);
4103
+ url.searchParams.set("owneyIntent", intent);
4104
+ paymasterUrl = url.toString();
4105
+ }
2579
4106
  const sendResult = await deps.provider.request({
2580
4107
  method: "wallet_sendCalls",
2581
4108
  params: [
2582
4109
  {
2583
4110
  version: "2.0.0",
2584
4111
  from: deps.ownerAddress,
2585
- chainId: (0, import_viem5.toHex)(chainId),
4112
+ chainId: (0, import_viem8.toHex)(chainId),
2586
4113
  atomicRequired: false,
2587
4114
  calls: [{ to: token, value: "0x0", data }],
2588
4115
  capabilities: {
2589
- paymasterService: { url: absolutePaymasterUrl() }
4116
+ paymasterService: { url: paymasterUrl }
2590
4117
  }
2591
4118
  }
2592
4119
  ]
@@ -2616,7 +4143,7 @@ function makeSponsoredCallsCallback(deps) {
2616
4143
  `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
2617
4144
  { chainId, callsId }
2618
4145
  );
2619
- };
4146
+ });
2620
4147
  }
2621
4148
 
2622
4149
  // src/client.ts
@@ -2646,9 +4173,9 @@ var SPONSORED_USDC_BY_CHAIN = {
2646
4173
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
2647
4174
  };
2648
4175
  var VIEM_CHAIN2 = {
2649
- 8453: import_chains2.base,
2650
- 42161: import_chains2.arbitrum,
2651
- 1: import_chains2.mainnet
4176
+ 8453: import_chains4.base,
4177
+ 42161: import_chains4.arbitrum,
4178
+ 1: import_chains4.mainnet
2652
4179
  };
2653
4180
  var SPONSORED_WETH_BY_CHAIN = {
2654
4181
  8453: "0x4200000000000000000000000000000000000006",
@@ -2676,6 +4203,7 @@ var OwneySDK = class {
2676
4203
  orgAgentConfig;
2677
4204
  orgAgentConfigPromise = null;
2678
4205
  zyfaiRpcUrls;
4206
+ yieldseekerApiBaseUrl;
2679
4207
  routingApiBaseUrl;
2680
4208
  referralSource;
2681
4209
  cachedSponsoredCallback = null;
@@ -2687,6 +4215,7 @@ var OwneySDK = class {
2687
4215
  this.apiKey = config.apiKey;
2688
4216
  if (config.debug) setOwneyDebug(true);
2689
4217
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
4218
+ this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
2690
4219
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2691
4220
  this.paymasterServiceUrl = config.paymasterServiceUrl;
2692
4221
  this.referralSource = config.referralSource;
@@ -2789,14 +4318,14 @@ var OwneySDK = class {
2789
4318
  // Casts work around viem's chain-narrowed Client vs the generic
2790
4319
  // PublicClient/WalletClient param types — structurally identical at
2791
4320
  // runtime, but the two share a name TS treats as unrelated.
2792
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
4321
+ getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
2793
4322
  chain: VIEM_CHAIN2[cid],
2794
- transport: (0, import_viem6.custom)(provider)
4323
+ transport: (0, import_viem9.custom)(provider)
2795
4324
  }),
2796
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
4325
+ getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
2797
4326
  account: owner,
2798
4327
  chain: VIEM_CHAIN2[cid],
2799
- transport: (0, import_viem6.custom)(provider)
4328
+ transport: (0, import_viem9.custom)(provider)
2800
4329
  })
2801
4330
  });
2802
4331
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -2813,6 +4342,8 @@ var OwneySDK = class {
2813
4342
  if (!onApproved && cached) return cached;
2814
4343
  const provider = this.requireConnectedProvider();
2815
4344
  const callback = makeSponsoredCallsCallback({
4345
+ apiKey: this.apiKey,
4346
+ routingApiBaseUrl: this.routingApiBaseUrl,
2816
4347
  provider,
2817
4348
  ownerAddress: this.state.walletAddress,
2818
4349
  paymasterServiceUrl: this.paymasterServiceUrl,
@@ -2842,14 +4373,14 @@ var OwneySDK = class {
2842
4373
  // Casts work around viem's chain-narrowed Client vs the generic
2843
4374
  // PublicClient/WalletClient param types — structurally identical at
2844
4375
  // runtime, but the two share a name TS treats as unrelated.
2845
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
4376
+ getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
2846
4377
  chain: VIEM_CHAIN2[cid],
2847
- transport: (0, import_viem6.custom)(provider)
4378
+ transport: (0, import_viem9.custom)(provider)
2848
4379
  }),
2849
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
4380
+ getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
2850
4381
  account: owner,
2851
4382
  chain: VIEM_CHAIN2[cid],
2852
- transport: (0, import_viem6.custom)(provider)
4383
+ transport: (0, import_viem9.custom)(provider)
2853
4384
  })
2854
4385
  });
2855
4386
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -2877,12 +4408,13 @@ var OwneySDK = class {
2877
4408
  async getOrgAgentConfig() {
2878
4409
  if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
2879
4410
  if (!this.orgAgentConfigPromise) {
2880
- this.orgAgentConfigPromise = fetchOrgAgentConfig(this.apiKey).then(
2881
- (config) => {
2882
- this.orgAgentConfig = config;
2883
- return config;
2884
- }
2885
- );
4411
+ this.orgAgentConfigPromise = fetchOrgAgentConfig(
4412
+ this.apiKey,
4413
+ this.routingApiBaseUrl
4414
+ ).then((config) => {
4415
+ this.orgAgentConfig = config;
4416
+ return config;
4417
+ });
2886
4418
  }
2887
4419
  return this.orgAgentConfigPromise;
2888
4420
  }
@@ -2917,9 +4449,19 @@ var OwneySDK = class {
2917
4449
  if (this.agents.size > 0) return;
2918
4450
  if (!this.initializingAgentsPromise) {
2919
4451
  this.initializingAgentsPromise = (async () => {
2920
- const agentKeys = await fetchAgentKeys(this.apiKey);
4452
+ const agentKeys = await fetchAgentKeys(
4453
+ this.apiKey,
4454
+ this.routingApiBaseUrl
4455
+ );
2921
4456
  this.disabledAgents.clear();
2922
- for (const { key: key2, agent_type, is_enabled } of agentKeys) {
4457
+ for (const {
4458
+ key: key2,
4459
+ agent_type,
4460
+ is_enabled,
4461
+ is_configured
4462
+ } of agentKeys) {
4463
+ const configured = is_configured ?? Boolean(key2);
4464
+ if (!configured) continue;
2923
4465
  const agent = this.createAgent(agent_type, key2);
2924
4466
  if (!agent) continue;
2925
4467
  this.agents.set(agent_type, agent);
@@ -2943,10 +4485,47 @@ var OwneySDK = class {
2943
4485
  }
2944
4486
  createAgent(agentId, key2) {
2945
4487
  if (agentId === "zyfai") {
4488
+ if (!key2) return null;
2946
4489
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2947
4490
  }
4491
+ if (agentId === "yieldseeker") {
4492
+ return new YieldseekerAgent(this.apiKey, {
4493
+ baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
4494
+ });
4495
+ }
2948
4496
  return null;
2949
4497
  }
4498
+ /**
4499
+ * Discover the agents actually returned by routing for this organization.
4500
+ * Consumers should use this instead of hardcoding a global agent roster.
4501
+ */
4502
+ async getAvailableAgents(options = {}) {
4503
+ await this.ensureAgentsInitialized();
4504
+ const { chainId, asset, includeDisabled = false } = options;
4505
+ const available = [];
4506
+ for (const [id, agent] of this.agents) {
4507
+ const isEnabled = !this.isAgentDisabled(id);
4508
+ if (!includeDisabled && !isEnabled) continue;
4509
+ if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
4510
+ continue;
4511
+ }
4512
+ if (asset !== void 0) {
4513
+ const supportsAsset = agent.supportedAssets.some(
4514
+ (entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
4515
+ );
4516
+ if (!supportsAsset) {
4517
+ continue;
4518
+ }
4519
+ }
4520
+ available.push({
4521
+ id,
4522
+ isEnabled,
4523
+ supportedChainIds: agent.supportedChainIds,
4524
+ supportedAssets: agent.supportedAssets
4525
+ });
4526
+ }
4527
+ return available;
4528
+ }
2950
4529
  // --- Account lifecycle ---
2951
4530
  /**
2952
4531
  * Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
@@ -2958,7 +4537,7 @@ var OwneySDK = class {
2958
4537
  * If provided, ALL specified agents must support the chainId or the call
2959
4538
  * throws before activating any agent.
2960
4539
  */
2961
- async activateAgent(chainId, agentId) {
4540
+ async activateAgent(chainId, agentId, asset) {
2962
4541
  const state = this.requireState();
2963
4542
  await this.ensureAgentsInitialized();
2964
4543
  if (agentId !== void 0) {
@@ -2994,9 +4573,7 @@ var OwneySDK = class {
2994
4573
  this.activeAgents.add(id);
2995
4574
  }
2996
4575
  state.chainId = chainId;
2997
- this.activateAgentsInTurn(agents, state, chainId).catch((error) => {
2998
- console.error("activateAgent background init failed:", error);
2999
- });
4576
+ await this.activateAgentsInTurn(agents, state, chainId, asset);
3000
4577
  return;
3001
4578
  }
3002
4579
  const compatible = [...this.agents.values()].filter(
@@ -3017,11 +4594,7 @@ var OwneySDK = class {
3017
4594
  const enabledCompatible = compatible.filter(
3018
4595
  (agent) => !this.isAgentDisabled(agent.id)
3019
4596
  );
3020
- this.activateAgentsInTurn(enabledCompatible, state, chainId).catch(
3021
- (error) => {
3022
- console.error("activateAgent background init failed:", error);
3023
- }
3024
- );
4597
+ await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
3025
4598
  }
3026
4599
  /**
3027
4600
  * Activate agents ONE AT A TIME, each followed by its org policy.
@@ -3041,11 +4614,11 @@ var OwneySDK = class {
3041
4614
  * rethrown (matching the previous `Promise.all` rejection) once all agents
3042
4615
  * have had a chance to activate.
3043
4616
  */
3044
- async activateAgentsInTurn(agents, state, chainId) {
4617
+ async activateAgentsInTurn(agents, state, chainId, asset) {
3045
4618
  let firstError = null;
3046
4619
  for (const agent of agents) {
3047
4620
  try {
3048
- await agent.activateAgent(state, chainId);
4621
+ await agent.activateAgent(state, chainId, asset);
3049
4622
  await this.applyOrgPolicyTo(agent, state, chainId);
3050
4623
  } catch (error) {
3051
4624
  if (firstError === null) {
@@ -3173,7 +4746,8 @@ var OwneySDK = class {
3173
4746
  amount,
3174
4747
  asset,
3175
4748
  callback
3176
- )
4749
+ ),
4750
+ this.routingApiBaseUrl
3177
4751
  );
3178
4752
  let approvalAttempted = false;
3179
4753
  for (; ; ) {
@@ -3212,15 +4786,15 @@ var OwneySDK = class {
3212
4786
  agent,
3213
4787
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
3214
4788
  }));
3215
- const valid = splits.filter(
4789
+ const valid2 = splits.filter(
3216
4790
  (s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
3217
4791
  );
3218
- if (valid.length === agents.length) {
4792
+ if (valid2.length === agents.length) {
3219
4793
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
3220
4794
  }
3221
4795
  return this.splitDepositAmount(
3222
4796
  totalAmount,
3223
- valid.map((s) => s.agent),
4797
+ valid2.map((s) => s.agent),
3224
4798
  chainId,
3225
4799
  asset,
3226
4800
  exempt
@@ -3323,7 +4897,8 @@ var OwneySDK = class {
3323
4897
  return withFailureReporting(
3324
4898
  this.apiKey,
3325
4899
  agent.id,
3326
- () => agent.withdraw(state, chainId, token, amount)
4900
+ () => agent.withdraw(state, chainId, token, amount),
4901
+ this.routingApiBaseUrl
3327
4902
  );
3328
4903
  }
3329
4904
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
@@ -3337,7 +4912,12 @@ var OwneySDK = class {
3337
4912
  console.error(`withdraw failed for agent "${agent.id}":`, err);
3338
4913
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
3339
4914
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3340
- void reportAgentFailure(this.apiKey, agent.id, code);
4915
+ void reportAgentFailure(
4916
+ this.apiKey,
4917
+ agent.id,
4918
+ code,
4919
+ this.routingApiBaseUrl
4920
+ );
3341
4921
  }
3342
4922
  }
3343
4923
  if (Object.keys(results2).length === 0) {
@@ -3354,6 +4934,10 @@ var OwneySDK = class {
3354
4934
  }
3355
4935
  const requested = BigInt(amount);
3356
4936
  const aggregated = await this.getBalances();
4937
+ const unavailableAgents = eligibleAgents.filter(
4938
+ (agent) => !(agent.id in aggregated.agentBalances)
4939
+ );
4940
+ const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
3357
4941
  const balances = projectAgentBalancesForAsset(
3358
4942
  eligibleAgents,
3359
4943
  aggregated.agentBalances,
@@ -3362,7 +4946,18 @@ var OwneySDK = class {
3362
4946
  assetInfo.decimals
3363
4947
  );
3364
4948
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
3365
- if (totalAvailable < requested) {
4949
+ if (totalAvailable === 0n && unavailableAgents.length > 0) {
4950
+ throw new OwneyError(
4951
+ "WITHDRAW_BALANCE_UNAVAILABLE",
4952
+ `Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
4953
+ {
4954
+ asset,
4955
+ unavailableAgents: unavailableAgentIds,
4956
+ agentErrors: aggregated.agentErrors
4957
+ }
4958
+ );
4959
+ }
4960
+ if (totalAvailable < requested && unavailableAgents.length === 0) {
3366
4961
  throw new OwneyError(
3367
4962
  "WITHDRAW_INSUFFICIENT_BALANCE",
3368
4963
  `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
@@ -3373,6 +4968,7 @@ var OwneySDK = class {
3373
4968
  }
3374
4969
  );
3375
4970
  }
4971
+ const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
3376
4972
  const disabledBalances = balances.filter(
3377
4973
  (b) => this.isAgentDisabled(b.agent.id)
3378
4974
  );
@@ -3381,7 +4977,7 @@ var OwneySDK = class {
3381
4977
  );
3382
4978
  const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
3383
4979
  disabledBalances,
3384
- requested
4980
+ plannedTarget
3385
4981
  );
3386
4982
  const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
3387
4983
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
@@ -3391,7 +4987,9 @@ var OwneySDK = class {
3391
4987
  }));
3392
4988
  const plans = [...disabledPlans, ...enabledPlans];
3393
4989
  const results = {};
3394
- const agentErrors = {};
4990
+ const agentErrors = {
4991
+ ...aggregated.agentErrors ?? {}
4992
+ };
3395
4993
  for (let i = 0; i < plans.length; i++) {
3396
4994
  const p = plans[i];
3397
4995
  if (p.planned === 0n) continue;
@@ -3406,7 +5004,12 @@ var OwneySDK = class {
3406
5004
  console.error(`withdraw failed for agent "${p.agent.id}":`, err);
3407
5005
  agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
3408
5006
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3409
- void reportAgentFailure(this.apiKey, p.agent.id, code);
5007
+ void reportAgentFailure(
5008
+ this.apiKey,
5009
+ p.agent.id,
5010
+ code,
5011
+ this.routingApiBaseUrl
5012
+ );
3410
5013
  const failedAmount = p.planned;
3411
5014
  p.planned = 0n;
3412
5015
  redistributeShare(plans, i, failedAmount);
@@ -3433,7 +5036,8 @@ var OwneySDK = class {
3433
5036
  requested: amount,
3434
5037
  withdrawn: withdrawn.toString(),
3435
5038
  partialResults: results,
3436
- agentErrors
5039
+ agentErrors,
5040
+ ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
3437
5041
  }
3438
5042
  );
3439
5043
  }
@@ -3474,7 +5078,12 @@ var OwneySDK = class {
3474
5078
  continue;
3475
5079
  }
3476
5080
  const reason = settledResult.reason;
3477
- agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
5081
+ const message = reason instanceof Error ? reason.message : String(reason);
5082
+ agentErrors[agentId2] = message;
5083
+ console.error(
5084
+ `[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
5085
+ reason
5086
+ );
3478
5087
  }
3479
5088
  if (successCount === 0) {
3480
5089
  throw new OwneyError(
@@ -3486,7 +5095,8 @@ var OwneySDK = class {
3486
5095
  return {
3487
5096
  totalBalance: String(totalBalance),
3488
5097
  totalBalanceAsset: "usdc",
3489
- agentBalances: results
5098
+ agentBalances: results,
5099
+ ...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
3490
5100
  };
3491
5101
  }
3492
5102
  /**
@@ -3580,7 +5190,10 @@ var OwneySDK = class {
3580
5190
  Promise.all(
3581
5191
  entries.map(async ([id, agent]) => {
3582
5192
  const b = await agent.getBalances(state, chainId);
3583
- return [id, Number(b.totalBalance)];
5193
+ return [
5194
+ id,
5195
+ balanceForApyScope(b, chainId, tokenSymbol)
5196
+ ];
3584
5197
  })
3585
5198
  )
3586
5199
  ]);
@@ -3608,10 +5221,12 @@ var OwneySDK = class {
3608
5221
  }
3609
5222
  }
3610
5223
  const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
5224
+ const history = aggregateApyHistory(results, balances);
3611
5225
  return {
3612
5226
  totalApy: String(totalApy),
3613
5227
  agentApy: results,
3614
- apyByChainAndAsset
5228
+ apyByChainAndAsset,
5229
+ history
3615
5230
  };
3616
5231
  }
3617
5232
  /**
@@ -3741,10 +5356,10 @@ var OwneySDK = class {
3741
5356
  );
3742
5357
  }
3743
5358
  const provider = this.requireConnectedProvider();
3744
- const wallet = (0, import_viem6.createWalletClient)({
5359
+ const wallet = (0, import_viem9.createWalletClient)({
3745
5360
  account: state.walletAddress,
3746
5361
  chain: VIEM_CHAIN2[chainId],
3747
- transport: (0, import_viem6.custom)(provider)
5362
+ transport: (0, import_viem9.custom)(provider)
3748
5363
  });
3749
5364
  const hash = await wallet.writeContract({
3750
5365
  address: token,
@@ -3754,9 +5369,9 @@ var OwneySDK = class {
3754
5369
  account: state.walletAddress,
3755
5370
  chain: VIEM_CHAIN2[chainId]
3756
5371
  });
3757
- const publicClient = (0, import_viem6.createPublicClient)({
5372
+ const publicClient = (0, import_viem9.createPublicClient)({
3758
5373
  chain: VIEM_CHAIN2[chainId],
3759
- transport: (0, import_viem6.custom)(provider)
5374
+ transport: (0, import_viem9.custom)(provider)
3760
5375
  });
3761
5376
  const receipt = await publicClient.waitForTransactionReceipt({
3762
5377
  hash,
@@ -3792,7 +5407,9 @@ var OwneySDK = class {
3792
5407
  return this.getAgent(agentId).getAgentApy(days, agentOptions);
3793
5408
  }
3794
5409
  const results = {};
3795
- const agentEntries = [...this.agents.entries()];
5410
+ const agentEntries = [...this.agents.entries()].filter(
5411
+ ([id]) => !this.isAgentDisabled(id)
5412
+ );
3796
5413
  const apyResults = await Promise.all(
3797
5414
  agentEntries.map(async ([id, agent]) => {
3798
5415
  const apy = await agent.getAgentApy(days, agentOptions);
@@ -3865,13 +5482,13 @@ var OwneySDK = class {
3865
5482
  };
3866
5483
 
3867
5484
  // src/agents/zyfai/zyfai.siwx.ts
3868
- var import_viem7 = require("viem");
3869
- var import_siwe = require("siwe");
5485
+ var import_viem10 = require("viem");
5486
+ var import_siwe2 = require("siwe");
3870
5487
  var import_sdk2 = require("@zyfai/sdk");
3871
5488
 
3872
5489
  // src/agents/zyfai/zyfai.siwx-cache.ts
3873
- var KEY_PREFIX2 = "owney.siwx.session";
3874
- var storage2 = () => {
5490
+ var KEY_PREFIX4 = "owney.siwx.session";
5491
+ var storage4 = () => {
3875
5492
  if (typeof window === "undefined") return null;
3876
5493
  try {
3877
5494
  return window.localStorage;
@@ -3879,8 +5496,8 @@ var storage2 = () => {
3879
5496
  return null;
3880
5497
  }
3881
5498
  };
3882
- var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
3883
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
5499
+ var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
5500
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
3884
5501
  var memorySiwxSessions = /* @__PURE__ */ new Map();
3885
5502
  var readLegacySiwxSession = (store, address) => {
3886
5503
  if (!store) return null;
@@ -3911,17 +5528,17 @@ var readLegacySiwxSession = (store, address) => {
3911
5528
  };
3912
5529
  var readSiwxSession = (address, chainId) => {
3913
5530
  if (typeof window === "undefined") return null;
3914
- const key2 = buildKey2(address);
3915
- const store = storage2();
3916
- let raw = null;
5531
+ const key2 = buildKey3(address);
5532
+ const store = storage4();
5533
+ let raw2 = null;
3917
5534
  try {
3918
- raw = store?.getItem(key2) ?? null;
5535
+ raw2 = store?.getItem(key2) ?? null;
3919
5536
  } catch {
3920
- raw = null;
5537
+ raw2 = null;
3921
5538
  }
3922
- if (raw) {
5539
+ if (raw2) {
3923
5540
  try {
3924
- return JSON.parse(raw);
5541
+ return JSON.parse(raw2);
3925
5542
  } catch {
3926
5543
  memorySiwxSessions.delete(key2);
3927
5544
  try {
@@ -3940,18 +5557,18 @@ var readSiwxSession = (address, chainId) => {
3940
5557
  };
3941
5558
  var writeSiwxSession = (address, _chainId, session) => {
3942
5559
  if (typeof window === "undefined") return;
3943
- const key2 = buildKey2(address);
5560
+ const key2 = buildKey3(address);
3944
5561
  memorySiwxSessions.set(key2, session);
3945
- const store = storage2();
5562
+ const store = storage4();
3946
5563
  try {
3947
5564
  store?.setItem(key2, JSON.stringify(session));
3948
5565
  } catch {
3949
5566
  }
3950
5567
  };
3951
5568
  var clearSiwxSession = (address, _chainId) => {
3952
- const key2 = buildKey2(address);
5569
+ const key2 = buildKey3(address);
3953
5570
  memorySiwxSessions.delete(key2);
3954
- const store = storage2();
5571
+ const store = storage4();
3955
5572
  try {
3956
5573
  store?.removeItem(key2);
3957
5574
  } catch {
@@ -3991,8 +5608,8 @@ function buildSIWXConfig(deps) {
3991
5608
  statement: STATEMENT,
3992
5609
  issuedAt,
3993
5610
  toString() {
3994
- return new import_siwe.SiweMessage({
3995
- address: (0, import_viem7.getAddress)(accountAddress),
5611
+ return new import_siwe2.SiweMessage({
5612
+ address: (0, import_viem10.getAddress)(accountAddress),
3996
5613
  chainId: numericChainId(chainId),
3997
5614
  domain,
3998
5615
  uri,
@@ -4034,7 +5651,7 @@ function buildSIWXConfig(deps) {
4034
5651
  const persistSession = async (session) => {
4035
5652
  const address = session.data.accountAddress;
4036
5653
  const id = numericChainId(session.data.chainId);
4037
- const message = new import_siwe.SiweMessage(session.message);
5654
+ const message = new import_siwe2.SiweMessage(session.message);
4038
5655
  const login = await post("/auth/login", {
4039
5656
  message,
4040
5657
  signature: session.signature,
@@ -4083,6 +5700,7 @@ function createOwneySIWX(config) {
4083
5700
  NotConnectedError,
4084
5701
  OwneyError,
4085
5702
  OwneySDK,
5703
+ YieldseekerAgent,
4086
5704
  createOwneySIWX,
4087
5705
  setOwneyDebug
4088
5706
  });