@owney/sdk 0.7.17-beta.3 → 0.7.18

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,7 +26,6 @@ __export(index_exports, {
26
26
  NotConnectedError: () => NotConnectedError,
27
27
  OwneyError: () => OwneyError,
28
28
  OwneySDK: () => OwneySDK,
29
- YieldseekerAgent: () => YieldseekerAgent,
30
29
  createOwneySIWX: () => createOwneySIWX,
31
30
  setOwneyDebug: () => setOwneyDebug
32
31
  });
@@ -311,18 +310,18 @@ function tokenDecimals(symbol, explicit) {
311
310
  return explicit;
312
311
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
313
312
  }
314
- function mapDeposit(raw2) {
313
+ function mapDeposit(raw) {
315
314
  return {
316
- txHash: raw2.txHash,
317
- smartWallet: raw2.smartWallet,
318
- amount: raw2.amount
315
+ txHash: raw.txHash,
316
+ smartWallet: raw.smartWallet,
317
+ amount: raw.amount
319
318
  };
320
319
  }
321
- function mapWithdraw(raw2) {
320
+ function mapWithdraw(raw) {
322
321
  return {
323
- txHash: raw2.txHash,
324
- type: raw2.type,
325
- amount: raw2.amount
322
+ txHash: raw.txHash,
323
+ type: raw.type,
324
+ amount: raw.amount
326
325
  };
327
326
  }
328
327
  var CHAIN_ID_TO_NAME = {
@@ -341,10 +340,10 @@ function resolveChainId(chain) {
341
340
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
342
341
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
343
342
  }
344
- function mapPendingAllocations(raw2) {
345
- if (!Array.isArray(raw2)) return void 0;
343
+ function mapPendingAllocations(raw) {
344
+ if (!Array.isArray(raw)) return void 0;
346
345
  const pending = [];
347
- for (const entry of raw2) {
346
+ for (const entry of raw) {
348
347
  if (typeof entry !== "object" || entry === null) continue;
349
348
  const e = entry;
350
349
  if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
@@ -367,8 +366,8 @@ function mapPendingAllocations(raw2) {
367
366
  }
368
367
  return pending.length > 0 ? pending : void 0;
369
368
  }
370
- function mapBalances(raw2, _chainId, smartWallet) {
371
- const portfolio = raw2.portfolio;
369
+ function mapBalances(raw, _chainId, smartWallet) {
370
+ const portfolio = raw.portfolio;
372
371
  const portfolioByChain = portfolio.portfolioByChain ?? {};
373
372
  let totalBalance = 0;
374
373
  const tokens = [];
@@ -437,8 +436,8 @@ function sumTokenValues(tokens) {
437
436
  function sumTokenEarnings(tokens) {
438
437
  return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
439
438
  }
440
- function mapEarnings(raw2, smartWallet) {
441
- const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
439
+ function mapEarnings(raw, smartWallet) {
440
+ const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
442
441
  const tokens = [];
443
442
  for (const [chainIdKey, tokensBySymbol] of Object.entries(
444
443
  totalEarningsByChain
@@ -457,15 +456,15 @@ function mapEarnings(raw2, smartWallet) {
457
456
  return {
458
457
  smartWallet,
459
458
  lifetimeEarnings: sumTokenEarnings(
460
- raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
459
+ raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
461
460
  ),
462
461
  tokens
463
462
  };
464
463
  }
465
- function mapWeightedApyByChain(raw2) {
466
- if (!raw2) return void 0;
464
+ function mapWeightedApyByChain(raw) {
465
+ if (!raw) return void 0;
467
466
  const out = {};
468
- for (const [chainKey, tokenApy] of Object.entries(raw2)) {
467
+ for (const [chainKey, tokenApy] of Object.entries(raw)) {
469
468
  const chainId = Number(chainKey);
470
469
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
471
470
  const perAsset = {};
@@ -505,15 +504,15 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
505
504
  }
506
505
  return totalBalance > 0 ? weightedSum / totalBalance : null;
507
506
  }
508
- function mapApyHistory(raw2, chainId, tokenSymbol) {
509
- const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
507
+ function mapApyHistory(raw, chainId, tokenSymbol) {
508
+ const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
510
509
  date,
511
510
  apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
512
511
  })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
513
512
  return {
514
- walletAddress: raw2.walletAddress,
515
- weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
516
- apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
513
+ walletAddress: raw.walletAddress,
514
+ weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
515
+ apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
517
516
  history
518
517
  };
519
518
  }
@@ -609,23 +608,23 @@ function mapEntries(rawEntries, chainId) {
609
608
  };
610
609
  });
611
610
  }
612
- function mapUserProfile(raw2, userAddress) {
611
+ function mapUserProfile(raw, userAddress) {
613
612
  return {
614
613
  address: userAddress,
615
- smartWallet: raw2.smartWallet || "",
616
- chains: raw2.chains || [],
617
- strategy: raw2.strategy,
618
- hasActiveSessionKey: raw2.hasActiveSessionKey || false,
619
- protocols: raw2.protocols || [],
620
- splitting: raw2.splitting,
621
- minSplits: raw2.minSplits
614
+ smartWallet: raw.smartWallet || "",
615
+ chains: raw.chains || [],
616
+ strategy: raw.strategy,
617
+ hasActiveSessionKey: raw.hasActiveSessionKey || false,
618
+ protocols: raw.protocols || [],
619
+ splitting: raw.splitting,
620
+ minSplits: raw.minSplits
622
621
  };
623
622
  }
624
- function mapApyByStrategy(raw2) {
623
+ function mapApyByStrategy(raw) {
625
624
  const apyPerAsset = {};
626
625
  let apySum = 0;
627
626
  let apyCount = 0;
628
- for (const entry of raw2.data) {
627
+ for (const entry of raw.data) {
629
628
  const supported = SupportedAssets.find(
630
629
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
631
630
  );
@@ -763,7 +762,7 @@ var isJwtExpired = (token) => {
763
762
  }
764
763
  };
765
764
  var memorySessions = /* @__PURE__ */ new Map();
766
- var isFreshSession = (session) => !!session && !!session.token && !isJwtExpired(session.token);
765
+ var isFreshSession = (session) => !!session && typeof session.isPredeployed === "boolean" && !!session.token && !isJwtExpired(session.token);
767
766
  var readLegacySession = (store, address) => {
768
767
  if (!store) return null;
769
768
  const prefix = legacyKeyPrefix(address);
@@ -796,15 +795,15 @@ var readSession = (address, _chainId) => {
796
795
  if (typeof window === "undefined") return null;
797
796
  const key2 = buildKey(address);
798
797
  const store = storage();
799
- let raw2 = null;
798
+ let raw = null;
800
799
  try {
801
- raw2 = store?.getItem(key2) ?? null;
800
+ raw = store?.getItem(key2) ?? null;
802
801
  } catch {
803
- raw2 = null;
802
+ raw = null;
804
803
  }
805
- if (raw2) {
804
+ if (raw) {
806
805
  try {
807
- const parsed = JSON.parse(raw2);
806
+ const parsed = JSON.parse(raw);
808
807
  if (isFreshSession(parsed)) return parsed;
809
808
  } catch {
810
809
  }
@@ -972,8 +971,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
972
971
  }
973
972
  return result;
974
973
  }
975
- function flattenAvailablePools(raw2) {
976
- const byChain = raw2 ?? {};
974
+ function flattenAvailablePools(raw) {
975
+ const byChain = raw ?? {};
977
976
  const names = [];
978
977
  for (const byToken of Object.values(byChain ?? {})) {
979
978
  for (const entry of Object.values(byToken ?? {})) {
@@ -1178,6 +1177,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1178
1177
  internals.httpClient.setAuthToken(cached.token);
1179
1178
  internals.authenticatedUserId = cached.userId;
1180
1179
  internals.hasActiveSessionKey = cached.hasActiveSessionKey;
1180
+ internals.isPredeployed = cached.isPredeployed;
1181
1181
  return;
1182
1182
  }
1183
1183
  }
@@ -1189,7 +1189,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1189
1189
  writeSession(newAddress, newChainId, {
1190
1190
  token: newToken,
1191
1191
  userId: internals.authenticatedUserId,
1192
- hasActiveSessionKey: internals.hasActiveSessionKey
1192
+ hasActiveSessionKey: internals.hasActiveSessionKey,
1193
+ isPredeployed: internals.isPredeployed
1193
1194
  });
1194
1195
  }
1195
1196
  };
@@ -1474,8 +1475,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1474
1475
  const poolResults = await Promise.all(
1475
1476
  universe.map(async (protocol) => {
1476
1477
  try {
1477
- const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
1478
- return [protocol.id, flattenAvailablePools(raw2)];
1478
+ const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1479
+ return [protocol.id, flattenAvailablePools(raw)];
1479
1480
  } catch (error) {
1480
1481
  console.warn(
1481
1482
  `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
@@ -1541,14 +1542,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1541
1542
  async readWalletState(ownerAddress) {
1542
1543
  try {
1543
1544
  const { portfolio } = await this.sdk.getPositions(ownerAddress);
1544
- const raw2 = portfolio;
1545
+ const raw = portfolio;
1545
1546
  debugLog("zyfai:onboard", "wallet state from getPositions", {
1546
- predeployed: raw2?.predeployed,
1547
- hasActiveSessionKey: raw2?.hasActiveSessionKey
1547
+ predeployed: raw?.predeployed,
1548
+ hasActiveSessionKey: raw?.hasActiveSessionKey
1548
1549
  });
1549
1550
  return {
1550
- predeployed: raw2?.predeployed,
1551
- hasActiveSessionKey: raw2?.hasActiveSessionKey
1551
+ predeployed: raw?.predeployed,
1552
+ hasActiveSessionKey: raw?.hasActiveSessionKey
1552
1553
  };
1553
1554
  } catch (error) {
1554
1555
  console.warn(
@@ -1640,12 +1641,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
1640
1641
  );
1641
1642
  return;
1642
1643
  }
1643
- const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
1644
1644
  await this.ensureSessionKey(
1645
1645
  ownerAddress,
1646
1646
  chainId,
1647
- wallet.address,
1648
- hasActiveSessionKey
1647
+ wallet.address
1649
1648
  );
1650
1649
  } catch (error) {
1651
1650
  console.warn(
@@ -1654,7 +1653,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
1654
1653
  );
1655
1654
  }
1656
1655
  }
1657
- async ensureSessionKey(ownerAddress, chainId, smartWallet, hasActiveSessionKey) {
1656
+ async ensureSessionKey(ownerAddress, chainId, smartWallet) {
1657
+ const walletState = await this.readWalletState(ownerAddress);
1658
+ const predeployed = walletState.predeployed;
1659
+ if (predeployed !== false) {
1660
+ debugLog("zyfai:onboard", "skipping manual session key", {
1661
+ chainId,
1662
+ predeployed
1663
+ });
1664
+ return;
1665
+ }
1666
+ const hasActiveSessionKey = walletState.hasActiveSessionKey;
1658
1667
  const attemptKey = `${ownerAddress.toLowerCase()}:${chainId}`;
1659
1668
  if (this.sessionKeyAttempts.has(attemptKey)) {
1660
1669
  debugLog(
@@ -1747,12 +1756,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
1747
1756
  "zyfai:onboard",
1748
1757
  "Safe already has code -> no deploy needed, checking the session key"
1749
1758
  );
1750
- const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
1751
1759
  await this.ensureSessionKey(
1752
1760
  ownerAddress,
1753
1761
  chainId,
1754
- wallet.address,
1755
- hasActiveSessionKey
1762
+ wallet.address
1756
1763
  );
1757
1764
  return wallet.address;
1758
1765
  }
@@ -1828,14 +1835,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1828
1835
  return { txHash, smartWallet, amount };
1829
1836
  }
1830
1837
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
1831
- const raw2 = await this.sdk.depositFunds(
1838
+ const raw = await this.sdk.depositFunds(
1832
1839
  this.getAddress(),
1833
1840
  validChainId,
1834
1841
  amount,
1835
1842
  asset,
1836
1843
  "aggressive"
1837
1844
  );
1838
- return mapDeposit(raw2);
1845
+ return mapDeposit(raw);
1839
1846
  } catch (error) {
1840
1847
  throw error;
1841
1848
  }
@@ -1844,27 +1851,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
1844
1851
  async withdraw(state, chainId, token, amount) {
1845
1852
  const validChainId = isValidChainId(chainId);
1846
1853
  await this.ensureConnected(state, validChainId);
1847
- const raw2 = await this.sdk.withdrawFunds(
1854
+ const raw = await this.sdk.withdrawFunds(
1848
1855
  this.getAddress(),
1849
1856
  validChainId,
1850
1857
  amount,
1851
1858
  token
1852
1859
  );
1853
- if (!raw2.success) {
1860
+ if (!raw.success) {
1854
1861
  throw new OwneyError(
1855
1862
  "WITHDRAW_FAILED",
1856
- raw2.message || "Zyfai withdraw failed.",
1857
- { chainId: validChainId, token, amount, response: raw2 },
1863
+ raw.message || "Zyfai withdraw failed.",
1864
+ { chainId: validChainId, token, amount, response: raw },
1858
1865
  this.id
1859
1866
  );
1860
1867
  }
1861
- return mapWithdraw(raw2);
1868
+ return mapWithdraw(raw);
1862
1869
  }
1863
1870
  // --- IAgent: Portfolio reads ---
1864
1871
  async getBalances(state, chainId) {
1865
1872
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
1866
- const raw2 = await this.sdk.getPortfolio(this.getAddress());
1867
- return mapBalances(raw2, validChainId, smartWallet);
1873
+ const raw = await this.sdk.getPortfolio(this.getAddress());
1874
+ return mapBalances(raw, validChainId, smartWallet);
1868
1875
  }
1869
1876
  earningsKey(state, chainId, smartWallet) {
1870
1877
  return JSON.stringify([
@@ -1877,11 +1884,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
1877
1884
  const existing = this.earningsReads.get(key2);
1878
1885
  if (existing) return existing;
1879
1886
  const generation = this.earningsGeneration;
1880
- const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw2) => {
1887
+ const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw) => {
1881
1888
  if (generation === this.earningsGeneration) {
1882
- this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
1889
+ this.earningsSnapshot = { key: key2, raw, at: Date.now() };
1883
1890
  }
1884
- return raw2;
1891
+ return raw;
1885
1892
  }).finally(() => {
1886
1893
  if (this.earningsReads.get(key2) === pending)
1887
1894
  this.earningsReads.delete(key2);
@@ -1891,11 +1898,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
1891
1898
  }
1892
1899
  async getEarnings(state, chainId) {
1893
1900
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1894
- const raw2 = await this.readEarnings(
1901
+ const raw = await this.readEarnings(
1895
1902
  this.earningsKey(state, chainId, smartWallet),
1896
1903
  smartWallet
1897
1904
  );
1898
- return mapEarnings(raw2, smartWallet);
1905
+ return mapEarnings(raw, smartWallet);
1899
1906
  }
1900
1907
  async refreshEarnings(state, chainId) {
1901
1908
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -1922,8 +1929,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1922
1929
  }
1923
1930
  async getAccountApy(state, chainId, days, tokenSymbol) {
1924
1931
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1925
- const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
1926
- return mapApyHistory(raw2, chainId, tokenSymbol);
1932
+ const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
1933
+ return mapApyHistory(raw, chainId, tokenSymbol);
1927
1934
  }
1928
1935
  /**
1929
1936
  * Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
@@ -1958,7 +1965,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1958
1965
  const matched = [];
1959
1966
  let backendExhausted = false;
1960
1967
  for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
1961
- const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
1968
+ const raw = await this.sdk.getHistory(smartWallet, validChainId, {
1962
1969
  limit: backendPageSize,
1963
1970
  offset,
1964
1971
  fromDate: options?.fromDate,
@@ -1969,13 +1976,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
1969
1976
  // asset's rows and handing back a page that filters to nothing.
1970
1977
  assetType
1971
1978
  });
1972
- raw2.data.forEach((entry, idx) => {
1979
+ raw.data.forEach((entry, idx) => {
1973
1980
  if (entry.chainId === validChainId) {
1974
1981
  matched.push({ entry, rawIdx: offset + idx });
1975
1982
  }
1976
1983
  });
1977
- offset += raw2.data.length;
1978
- if (raw2.data.length < backendPageSize) {
1984
+ offset += raw.data.length;
1985
+ if (raw.data.length < backendPageSize) {
1979
1986
  backendExhausted = true;
1980
1987
  break;
1981
1988
  }
@@ -1997,18 +2004,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
1997
2004
  }
1998
2005
  async getUserProfile(state, chainId) {
1999
2006
  await this.connectAuth(state, chainId);
2000
- const raw2 = await this.sdk.getUserDetails();
2007
+ const raw = await this.sdk.getUserDetails();
2001
2008
  debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
2002
2009
  asset: "USDC (default \u2014 no asset passed)",
2003
- splitting: raw2.splitting,
2004
- minSplits: raw2.minSplits,
2005
- strategy: raw2.strategy,
2006
- chains: raw2.chains,
2007
- protocolCount: raw2.protocols?.length,
2008
- hasActiveSessionKey: raw2.hasActiveSessionKey,
2009
- smartWallet: raw2.smartWallet
2010
+ splitting: raw.splitting,
2011
+ minSplits: raw.minSplits,
2012
+ strategy: raw.strategy,
2013
+ chains: raw.chains,
2014
+ protocolCount: raw.protocols?.length,
2015
+ hasActiveSessionKey: raw.hasActiveSessionKey,
2016
+ smartWallet: raw.smartWallet
2010
2017
  });
2011
- return mapUserProfile(raw2, this.connectedAddress);
2018
+ return mapUserProfile(raw, this.connectedAddress);
2012
2019
  }
2013
2020
  async ensureAutoSelectProtocols(state, chainId, asset) {
2014
2021
  await this.connectAuth(state, chainId);
@@ -2027,58 +2034,236 @@ var ZyfaiAgent = class _ZyfaiAgent {
2027
2034
  }
2028
2035
  // --- IAgent: Discovery (no wallet required) ---
2029
2036
  async getAgentApy(days, options) {
2030
- const raw2 = await this.sdk.getAPYPerStrategy(
2037
+ const raw = await this.sdk.getAPYPerStrategy(
2031
2038
  false,
2032
2039
  DayFilterMapping[days],
2033
2040
  "aggressive",
2034
2041
  options?.chainId,
2035
2042
  options?.tokenSymbol
2036
2043
  );
2037
- return mapApyByStrategy(raw2);
2044
+ return mapApyByStrategy(raw);
2038
2045
  }
2039
2046
  };
2040
2047
 
2041
- // src/agents/yieldseeker/yieldseeker.agent.ts
2042
- var import_viem6 = require("viem");
2043
- var import_chains3 = require("viem/chains");
2044
-
2045
- // src/lib/chain-guard.ts
2046
- var CHAIN_NAMES = {
2047
- 1: "Ethereum",
2048
- 8453: "Base",
2049
- 42161: "Arbitrum"
2050
- };
2051
- function chainName(chainId) {
2052
- return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
2053
- }
2054
- async function ensureWalletOnChain(pub, wallet, expected) {
2055
- const actual = await pub.getChainId();
2056
- if (actual === expected) return;
2048
+ // src/lib/routing-api.ts
2049
+ var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2050
+ async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL) {
2051
+ const url = `${baseUrl}/api/v1/agent/org-config`;
2057
2052
  try {
2058
- await wallet.switchChain({ id: expected });
2053
+ const res = await fetch(url, {
2054
+ method: "GET",
2055
+ headers: {
2056
+ "Content-Type": "application/json",
2057
+ "x-owney-api-key": `${apiKey}`
2058
+ }
2059
+ });
2060
+ if (!res.ok) {
2061
+ if (res.status !== 404) {
2062
+ console.warn(
2063
+ `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
2064
+ );
2065
+ }
2066
+ return null;
2067
+ }
2068
+ const json = await res.json();
2069
+ const policy = json.success ? json.data ?? null : null;
2070
+ debugLog(
2071
+ "owney-sdk",
2072
+ policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
2073
+ policy ?? void 0
2074
+ );
2075
+ return policy;
2059
2076
  } catch (error) {
2077
+ console.warn(
2078
+ "[owney-sdk] Could not read org agent config (non-fatal):",
2079
+ error instanceof Error ? error.message : String(error)
2080
+ );
2081
+ return null;
2082
+ }
2083
+ }
2084
+ async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
2085
+ const url = `${baseUrl}/api/v1/agent/keys`;
2086
+ const res = await fetch(url, {
2087
+ method: "GET",
2088
+ headers: {
2089
+ "Content-Type": "application/json",
2090
+ "x-owney-api-key": `${apiKey}`
2091
+ }
2092
+ });
2093
+ if (!res.ok) {
2094
+ const text = await res.text().catch(() => "");
2060
2095
  throw new OwneyError(
2061
- "CHAIN_MISMATCH",
2062
- `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2063
- {
2064
- expectedChainId: expected,
2065
- actualChainId: actual,
2066
- cause: error instanceof Error ? error.message : String(error)
2067
- }
2096
+ "API_ROUTING_ERROR",
2097
+ `Routing API error ${res.status}: ${text}`,
2098
+ { statusCode: res.status, responseBody: text }
2068
2099
  );
2069
2100
  }
2070
- const after = await pub.getChainId();
2071
- if (after !== expected) {
2101
+ const json = await res.json();
2102
+ if (!json.success) {
2072
2103
  throw new OwneyError(
2073
- "CHAIN_MISMATCH",
2074
- `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2075
- { expectedChainId: expected, actualChainId: after }
2104
+ "API_ROUTING_FAILED",
2105
+ `Routing API request failed: ${json.message}`,
2106
+ { message: json.message }
2076
2107
  );
2077
2108
  }
2109
+ return json.data;
2078
2110
  }
2079
2111
 
2080
- // src/lib/transfer-auth.ts
2112
+ // src/lib/health-report.ts
2113
+ var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2114
+ async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
2115
+ try {
2116
+ await fetch(`${baseUrl}/api/v1/agent/health-report`, {
2117
+ method: "POST",
2118
+ headers: {
2119
+ "Content-Type": "application/json",
2120
+ "x-owney-api-key": apiKey
2121
+ },
2122
+ body: JSON.stringify({
2123
+ agent_type: agentType,
2124
+ error_code: errorCode,
2125
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2126
+ })
2127
+ });
2128
+ } catch (err) {
2129
+ console.warn(
2130
+ `[owney-sdk] health-report failed for agent "${agentType}":`,
2131
+ err instanceof Error ? err.message : err
2132
+ );
2133
+ }
2134
+ }
2135
+ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
2136
+ try {
2137
+ return await fn();
2138
+ } catch (err) {
2139
+ const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
2140
+ void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
2141
+ throw err;
2142
+ }
2143
+ }
2144
+
2145
+ // src/lib/helpers/withdraw-helper.ts
2081
2146
  var import_viem2 = require("viem");
2147
+ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
2148
+ const target = asset.toUpperCase();
2149
+ return agents.map((agent) => {
2150
+ const agentBalance = aggregated[agent.id];
2151
+ const tokenBalance = agentBalance?.tokens.find(
2152
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
2153
+ );
2154
+ if (!tokenBalance) return { agent, balance: 0n };
2155
+ return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
2156
+ });
2157
+ }
2158
+ function planProportionalShares(balances, requested, totalAvailable) {
2159
+ const plans = balances.map(({ agent, balance }) => ({
2160
+ agent,
2161
+ balance,
2162
+ planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
2163
+ }));
2164
+ const assigned = plans.reduce((s, p) => s + p.planned, 0n);
2165
+ let remainder = requested - assigned;
2166
+ const byHeadroom = [...plans].sort((a, b) => {
2167
+ const diff = b.balance - b.planned - (a.balance - a.planned);
2168
+ return diff > 0n ? 1 : diff < 0n ? -1 : 0;
2169
+ });
2170
+ for (const p of byHeadroom) {
2171
+ if (remainder === 0n) break;
2172
+ const headroom = p.balance - p.planned;
2173
+ if (headroom <= 0n) continue;
2174
+ const take = headroom < remainder ? headroom : remainder;
2175
+ p.planned += take;
2176
+ remainder -= take;
2177
+ }
2178
+ return plans;
2179
+ }
2180
+ function planDisabledDrain(disabled, requested) {
2181
+ const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
2182
+ const plans = [];
2183
+ let remaining = requested;
2184
+ for (const { agent, balance } of sorted) {
2185
+ if (remaining === 0n) {
2186
+ plans.push({ agent, balance, planned: 0n });
2187
+ continue;
2188
+ }
2189
+ const take = balance < remaining ? balance : remaining;
2190
+ plans.push({ agent, balance, planned: take });
2191
+ remaining -= take;
2192
+ }
2193
+ return { plans, remaining };
2194
+ }
2195
+ function redistributeShare(plans, fromIndex, amount, candidatePool) {
2196
+ const pool = candidatePool ?? plans.slice(fromIndex + 1);
2197
+ const candidates = pool.filter((c) => c.balance - c.planned > 0n);
2198
+ const totalHeadroom = candidates.reduce(
2199
+ (s, c) => s + (c.balance - c.planned),
2200
+ 0n
2201
+ );
2202
+ if (totalHeadroom === 0n) return;
2203
+ let distributed = 0n;
2204
+ for (const c of candidates) {
2205
+ const headroom = c.balance - c.planned;
2206
+ const proportional = headroom * amount / totalHeadroom;
2207
+ const give = proportional > headroom ? headroom : proportional;
2208
+ c.planned += give;
2209
+ distributed += give;
2210
+ }
2211
+ let leftover = amount - distributed;
2212
+ for (const c of candidates) {
2213
+ if (leftover === 0n) break;
2214
+ const headroom = c.balance - c.planned;
2215
+ if (headroom <= 0n) continue;
2216
+ const take = headroom < leftover ? headroom : leftover;
2217
+ c.planned += take;
2218
+ leftover -= take;
2219
+ }
2220
+ }
2221
+ function sumWithdrawnAmount(results) {
2222
+ return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
2223
+ }
2224
+
2225
+ // src/lib/helpers/account-apy-helper.ts
2226
+ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2227
+ const sums = {};
2228
+ const weights = {};
2229
+ for (const id of Object.keys(agentApys)) {
2230
+ const cells = agentApys[id].apyByChainAndAsset;
2231
+ const balance = agentBalances[id] ?? 0;
2232
+ if (!cells || balance <= 0) continue;
2233
+ for (const [chainKey, perAsset] of Object.entries(cells)) {
2234
+ if (!perAsset) continue;
2235
+ const chainId = Number(chainKey);
2236
+ for (const [asset, apyValue] of Object.entries(perAsset)) {
2237
+ const apy = Number(apyValue ?? 0);
2238
+ if (apy === 0) continue;
2239
+ sums[chainId] ??= {};
2240
+ weights[chainId] ??= {};
2241
+ sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
2242
+ weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2243
+ }
2244
+ }
2245
+ }
2246
+ const out = {};
2247
+ for (const chainKey of Object.keys(sums)) {
2248
+ const chainId = Number(chainKey);
2249
+ const perAssetOut = {};
2250
+ for (const asset of Object.keys(sums[chainId])) {
2251
+ const w = weights[chainId][asset];
2252
+ if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
2253
+ }
2254
+ if (Object.keys(perAssetOut).length > 0) {
2255
+ out[chainId] = perAssetOut;
2256
+ }
2257
+ }
2258
+ return out;
2259
+ }
2260
+
2261
+ // src/client.ts
2262
+ var import_viem6 = require("viem");
2263
+ var import_chains2 = require("viem/chains");
2264
+
2265
+ // src/lib/transfer-auth.ts
2266
+ var import_viem3 = require("viem");
2082
2267
  var ERC20_META_ABI = [
2083
2268
  { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
2084
2269
  { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
@@ -2110,29 +2295,28 @@ async function readTokenMeta(publicClient, token) {
2110
2295
  function randomAuthNonce() {
2111
2296
  const bytes = new Uint8Array(32);
2112
2297
  globalThis.crypto.getRandomValues(bytes);
2113
- return (0, import_viem2.bytesToHex)(bytes);
2298
+ return (0, import_viem3.bytesToHex)(bytes);
2114
2299
  }
2115
2300
 
2116
2301
  // src/lib/sponsor-client.ts
2117
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2118
- async function postPaymasterIntent(input) {
2119
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2302
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2303
+ async function postSponsorTransferAuth(input) {
2304
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2120
2305
  let res;
2121
2306
  try {
2122
- res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
2307
+ res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
2123
2308
  method: "POST",
2124
2309
  headers: {
2125
2310
  "content-type": "application/json",
2126
- "x-owney-api-key": input.apiKey,
2127
- Authorization: `Signature ${input.yieldseekerSignature}`
2311
+ "x-owney-api-key": input.apiKey
2128
2312
  },
2129
2313
  body: JSON.stringify(input.body)
2130
2314
  });
2131
2315
  } catch (networkError) {
2132
2316
  throw new OwneyError(
2133
2317
  "SPONSOR_REQUEST_FAILED",
2134
- `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2135
- { cause: String(networkError), safeToFallback: true }
2318
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2319
+ { cause: String(networkError) }
2136
2320
  );
2137
2321
  }
2138
2322
  const text = await res.text();
@@ -2141,29 +2325,30 @@ async function postPaymasterIntent(input) {
2141
2325
  parsed = JSON.parse(text);
2142
2326
  } catch {
2143
2327
  }
2144
- if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
2328
+ if (!res.ok || !parsed?.success || !parsed.data) {
2145
2329
  throw new OwneyError(
2146
2330
  "SPONSOR_REQUEST_FAILED",
2147
- `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2331
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2148
2332
  {
2149
2333
  statusCode: res.status,
2150
2334
  responseBody: text.slice(0, 500),
2151
- safeToFallback: true
2335
+ // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
2336
+ // before broadcast, so it is safe to fall back to a user-paid deposit.
2337
+ safeToFallback: res.status === 503
2152
2338
  }
2153
2339
  );
2154
2340
  }
2155
2341
  return parsed.data;
2156
2342
  }
2157
- async function postSponsorTransferAuth(input) {
2158
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2343
+ async function postSponsorPermit2Transfer(input) {
2344
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2159
2345
  let res;
2160
2346
  try {
2161
- res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
2347
+ res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
2162
2348
  method: "POST",
2163
2349
  headers: {
2164
2350
  "content-type": "application/json",
2165
- "x-owney-api-key": input.apiKey,
2166
- ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
2351
+ "x-owney-api-key": input.apiKey
2167
2352
  },
2168
2353
  body: JSON.stringify(input.body)
2169
2354
  });
@@ -2171,7 +2356,7 @@ async function postSponsorTransferAuth(input) {
2171
2356
  throw new OwneyError(
2172
2357
  "SPONSOR_REQUEST_FAILED",
2173
2358
  `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2174
- { cause: String(networkError) }
2359
+ { cause: String(networkError), safeToFallback: false }
2175
2360
  );
2176
2361
  }
2177
2362
  const text = await res.text();
@@ -2187,59 +2372,18 @@ async function postSponsorTransferAuth(input) {
2187
2372
  {
2188
2373
  statusCode: res.status,
2189
2374
  responseBody: text.slice(0, 500),
2190
- // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
2191
- // before broadcast, so it is safe to fall back to a user-paid deposit.
2192
- safeToFallback: res.status === 503
2193
- }
2194
- );
2195
- }
2196
- return parsed.data;
2197
- }
2198
- async function postSponsorPermit2Transfer(input) {
2199
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2200
- let res;
2201
- try {
2202
- res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
2203
- method: "POST",
2204
- headers: {
2205
- "content-type": "application/json",
2206
- "x-owney-api-key": input.apiKey,
2207
- ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
2208
- },
2209
- body: JSON.stringify(input.body)
2210
- });
2211
- } catch (networkError) {
2212
- throw new OwneyError(
2213
- "SPONSOR_REQUEST_FAILED",
2214
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2215
- { cause: String(networkError), safeToFallback: false }
2216
- );
2217
- }
2218
- const text = await res.text();
2219
- let parsed = null;
2220
- try {
2221
- parsed = JSON.parse(text);
2222
- } catch {
2223
- }
2224
- if (!res.ok || !parsed?.success || !parsed.data) {
2225
- throw new OwneyError(
2226
- "SPONSOR_REQUEST_FAILED",
2227
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2228
- {
2229
- statusCode: res.status,
2230
- responseBody: text.slice(0, 500),
2231
- safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
2375
+ safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
2232
2376
  }
2233
2377
  );
2234
2378
  }
2235
2379
  return parsed.data;
2236
2380
  }
2237
2381
  async function getSponsorRelayerAddress(input) {
2238
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2382
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2239
2383
  let res;
2240
2384
  try {
2241
2385
  res = await fetch(
2242
- `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2386
+ `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2243
2387
  {
2244
2388
  headers: { "x-owney-api-key": input.apiKey }
2245
2389
  }
@@ -2272,7 +2416,7 @@ async function getSponsorRelayerAddress(input) {
2272
2416
  }
2273
2417
 
2274
2418
  // src/lib/permit2.ts
2275
- var import_viem3 = require("viem");
2419
+ var import_viem4 = require("viem");
2276
2420
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2277
2421
  var MAX_UINT256 = 2n ** 256n - 1n;
2278
2422
  var ERC20_ALLOWANCE_ABI = [
@@ -2330,7 +2474,7 @@ function buildPermitTransferFromTypedData(input) {
2330
2474
  function randomPermit2Nonce() {
2331
2475
  const bytes = new Uint8Array(32);
2332
2476
  globalThis.crypto.getRandomValues(bytes);
2333
- return BigInt((0, import_viem3.bytesToHex)(bytes));
2477
+ return BigInt((0, import_viem4.bytesToHex)(bytes));
2334
2478
  }
2335
2479
  async function readPermit2Allowance(publicClient, token, owner) {
2336
2480
  return publicClient.readContract({
@@ -2340,1972 +2484,213 @@ async function readPermit2Allowance(publicClient, token, owner) {
2340
2484
  args: [owner, PERMIT2_ADDRESS]
2341
2485
  });
2342
2486
  }
2343
- async function readErc20Balance(publicClient, token, owner) {
2344
- return publicClient.readContract({
2345
- address: token,
2346
- abi: ERC20_ALLOWANCE_ABI,
2347
- functionName: "balanceOf",
2348
- args: [owner]
2349
- });
2350
- }
2351
-
2352
- // src/lib/sponsored-deposit.ts
2353
- var AUTH_WINDOW_SECONDS = 15 * 60;
2354
- var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
2355
- function provideDepositVerificationContext(callback, context) {
2356
- callback[verificationSetter]?.(context);
2357
- }
2358
- function makeVerificationAwareDepositCallback(implementation) {
2359
- let nextVerification;
2360
- const callback = async (smartWallet, chainId, amount) => {
2361
- const verification = nextVerification;
2362
- nextVerification = void 0;
2363
- return implementation(smartWallet, chainId, amount, verification);
2364
- };
2365
- Object.defineProperty(callback, verificationSetter, {
2366
- value: (context) => {
2367
- nextVerification = context;
2368
- }
2369
- });
2370
- return callback;
2371
- }
2372
- function makeSponsoredDepositCallback(deps) {
2373
- const post = deps.httpPost ?? postSponsorTransferAuth;
2374
- return makeVerificationAwareDepositCallback(
2375
- async (smartWallet, chainId, amount, verification) => {
2376
- const cid = chainId;
2377
- const token = deps.tokenAddressByChain[cid];
2378
- if (!token) {
2379
- throw new OwneyError(
2380
- "CHAIN_UNSUPPORTED",
2381
- `No sponsored token configured for chain ${chainId}`
2382
- );
2383
- }
2384
- const pub = deps.getPublicClient(cid);
2385
- const wallet = deps.getWalletClient(cid);
2386
- await ensureWalletOnChain(pub, wallet, cid);
2387
- try {
2388
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2389
- if (balance < BigInt(amount)) {
2390
- throw new OwneyError(
2391
- "DEPOSIT_INSUFFICIENT_BALANCE",
2392
- "Insufficient balance for this deposit.",
2393
- { token, chainId: cid, balance: balance.toString(), amount }
2394
- );
2395
- }
2396
- } catch (err) {
2397
- if (err instanceof OwneyError) throw err;
2398
- console.warn(
2399
- "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2400
- err instanceof Error ? err.message : String(err)
2401
- );
2402
- }
2403
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2404
- const validAfter = 0n;
2405
- const validBefore = BigInt(
2406
- Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2407
- );
2408
- const nonce = randomAuthNonce();
2409
- const typedData = buildTransferWithAuthorizationTypedData({
2410
- token,
2411
- chainId: cid,
2412
- tokenName,
2413
- tokenVersion,
2414
- message: {
2415
- from: deps.ownerAddress,
2416
- to: smartWallet,
2417
- value: BigInt(amount),
2418
- validAfter,
2419
- validBefore,
2420
- nonce
2421
- }
2422
- });
2423
- const authSignature = await wallet.signTypedData({
2424
- account: deps.ownerAddress,
2425
- ...typedData
2426
- });
2427
- deps.onApproved?.();
2428
- const result = await post({
2429
- baseUrl: deps.baseUrl,
2430
- apiKey: deps.apiKey,
2431
- ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
2432
- body: {
2433
- chainId: cid,
2434
- token,
2435
- from: deps.ownerAddress,
2436
- to: smartWallet,
2437
- value: amount,
2438
- validAfter: validAfter.toString(),
2439
- validBefore: validBefore.toString(),
2440
- nonce,
2441
- authSignature,
2442
- tokenName,
2443
- tokenVersion,
2444
- ...verification?.agentId === "yieldseeker" ? {
2445
- yieldseekerUserId: verification.userId,
2446
- yieldseekerAgentId: verification.yieldseekerAgentId
2447
- } : {}
2448
- }
2449
- });
2450
- return result.txHash;
2451
- }
2452
- );
2453
- }
2454
-
2455
- // src/agents/yieldseeker/yieldseeker.auth.ts
2456
- var import_siwe = require("siwe");
2457
- var import_viem4 = require("viem");
2458
- var import_chains2 = require("viem/chains");
2459
-
2460
- // src/agents/yieldseeker/yieldseeker.auth-cache.ts
2461
- var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
2462
- var INVALIDATED_KEY_PREFIXES = [
2463
- "owney.yieldseeker.session",
2464
- "owney.yieldseeker.session.v3",
2465
- "owney.yieldseeker.session.v4"
2466
- ];
2467
- var storage2 = () => {
2468
- if (typeof window === "undefined") return null;
2469
- try {
2470
- return window.localStorage;
2471
- } catch {
2472
- return null;
2473
- }
2474
- };
2475
- var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2476
- var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
2477
- (prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
2478
- );
2479
- var clearInvalidatedSessions = (store, address, chainId) => {
2480
- for (const key2 of invalidatedKeys(address, chainId)) {
2481
- memorySessions2.delete(key2);
2482
- try {
2483
- store?.removeItem(key2);
2484
- } catch {
2485
- }
2486
- }
2487
- };
2488
- var memorySessions2 = /* @__PURE__ */ new Map();
2489
- var isValidSession = (session) => {
2490
- if (!session?.token) return false;
2491
- try {
2492
- const parsed = JSON.parse(atob(session.token));
2493
- return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
2494
- } catch {
2495
- return false;
2496
- }
2497
- };
2498
- var readYieldseekerSession = (address, chainId) => {
2499
- if (typeof window === "undefined") return null;
2500
- const key2 = buildKey2(address, chainId);
2501
- const store = storage2();
2502
- clearInvalidatedSessions(store, address, chainId);
2503
- let raw2 = null;
2504
- try {
2505
- raw2 = store?.getItem(key2) ?? null;
2506
- } catch {
2507
- raw2 = null;
2508
- }
2509
- if (raw2) {
2510
- try {
2511
- const parsed = JSON.parse(raw2);
2512
- if (isValidSession(parsed)) return parsed.token;
2513
- } catch {
2514
- }
2515
- memorySessions2.delete(key2);
2516
- try {
2517
- store?.removeItem(key2);
2518
- } catch {
2519
- }
2520
- return null;
2521
- }
2522
- const cached = memorySessions2.get(key2);
2523
- if (isValidSession(cached)) return cached.token;
2524
- if (cached) memorySessions2.delete(key2);
2525
- return null;
2526
- };
2527
- var writeYieldseekerSession = (address, chainId, token) => {
2528
- if (typeof window === "undefined") return;
2529
- const session = { token };
2530
- if (!isValidSession(session)) return;
2531
- const key2 = buildKey2(address, chainId);
2532
- memorySessions2.set(key2, session);
2533
- const store = storage2();
2534
- try {
2535
- store?.setItem(key2, JSON.stringify(session));
2536
- } catch {
2537
- }
2538
- };
2539
- var clearYieldseekerSession = (address, chainId) => {
2540
- const key2 = buildKey2(address, chainId);
2541
- memorySessions2.delete(key2);
2542
- const store = storage2();
2543
- clearInvalidatedSessions(store, address, chainId);
2544
- try {
2545
- store?.removeItem(key2);
2546
- } catch {
2547
- }
2548
- };
2549
-
2550
- // src/agents/yieldseeker/yieldseeker.auth.ts
2551
- function resolveSiweOrigin(override) {
2552
- const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
2553
- if (!origin || origin === "null") {
2554
- throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
2555
- }
2556
- const url = new URL(origin);
2557
- if (url.protocol !== "https:" && url.protocol !== "http:") {
2558
- throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
2559
- }
2560
- if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
2561
- throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
2562
- }
2563
- return url;
2564
- }
2565
- function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2566
- const url = resolveSiweOrigin(dependencies.origin);
2567
- return new import_siwe.SiweMessage({
2568
- scheme: url.protocol.slice(0, -1),
2569
- domain: url.host,
2570
- address: (0, import_viem4.getAddress)(address),
2571
- uri: url.origin,
2572
- version: "1",
2573
- chainId,
2574
- nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
2575
- issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
2576
- }).prepareMessage();
2577
- }
2578
- function encodeYieldseekerAuthToken(token) {
2579
- const bytes = new TextEncoder().encode(JSON.stringify(token));
2580
- let binary = "";
2581
- for (const byte of bytes) binary += String.fromCharCode(byte);
2582
- return btoa(binary);
2583
- }
2584
- var YieldseekerAuth = class {
2585
- constructor(dependencies = {}) {
2586
- this.dependencies = dependencies;
2587
- }
2588
- dependencies;
2589
- tokens = /* @__PURE__ */ new Map();
2590
- pending = /* @__PURE__ */ new Map();
2591
- scopes = /* @__PURE__ */ new Map();
2592
- key(state, chainId) {
2593
- return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
2594
- }
2595
- async getToken(state, chainId) {
2596
- const key2 = this.key(state, chainId);
2597
- const scope = { address: state.walletAddress, chainId };
2598
- this.scopes.set(key2, scope);
2599
- const cached = this.tokens.get(key2);
2600
- if (cached) return cached;
2601
- const persisted = readYieldseekerSession(scope.address, scope.chainId);
2602
- if (persisted && this.matchesOrigin(persisted)) {
2603
- this.tokens.set(key2, persisted);
2604
- return persisted;
2605
- }
2606
- if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
2607
- const inFlight = this.pending.get(key2);
2608
- if (inFlight) return inFlight;
2609
- const request = this.sign(state, chainId).then((token) => {
2610
- this.tokens.set(key2, token);
2611
- writeYieldseekerSession(scope.address, scope.chainId, token);
2612
- return token;
2613
- });
2614
- this.pending.set(key2, request);
2615
- try {
2616
- return await request;
2617
- } finally {
2618
- this.pending.delete(key2);
2619
- }
2620
- }
2621
- matchesOrigin(token) {
2622
- try {
2623
- const message = new import_siwe.SiweMessage(JSON.parse(atob(token)).message);
2624
- const url = resolveSiweOrigin(this.dependencies.origin);
2625
- return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
2626
- } catch {
2627
- return false;
2628
- }
2629
- }
2630
- clear(state, chainId) {
2631
- if (!state || chainId === void 0) {
2632
- for (const scope of this.scopes.values()) {
2633
- clearYieldseekerSession(scope.address, scope.chainId);
2634
- }
2635
- this.tokens.clear();
2636
- this.pending.clear();
2637
- this.scopes.clear();
2638
- return;
2639
- }
2640
- const key2 = this.key(state, chainId);
2641
- this.tokens.delete(key2);
2642
- this.pending.delete(key2);
2643
- this.scopes.delete(key2);
2644
- clearYieldseekerSession(state.walletAddress, chainId);
2645
- }
2646
- async sign(state, chainId) {
2647
- const account = (0, import_viem4.getAddress)(state.walletAddress);
2648
- const publicClient = (0, import_viem4.createPublicClient)({
2649
- chain: import_chains2.base,
2650
- transport: (0, import_viem4.custom)(state.provider)
2651
- });
2652
- const walletClient = (0, import_viem4.createWalletClient)({
2653
- account,
2654
- chain: import_chains2.base,
2655
- transport: (0, import_viem4.custom)(state.provider)
2656
- });
2657
- await ensureWalletOnChain(
2658
- publicClient,
2659
- walletClient,
2660
- 8453
2661
- );
2662
- const message = createYieldseekerSiweMessage(
2663
- account,
2664
- chainId,
2665
- this.dependencies
2666
- );
2667
- const signature = await walletClient.signMessage({ account, message });
2668
- return encodeYieldseekerAuthToken({ message, signature });
2669
- }
2670
- };
2671
-
2672
- // src/agents/yieldseeker/yieldseeker.identity-cache.ts
2673
- var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
2674
- var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
2675
- var memoryIdentities = /* @__PURE__ */ new Map();
2676
- var storage3 = () => {
2677
- if (typeof window === "undefined") return null;
2678
- try {
2679
- return window.localStorage;
2680
- } catch {
2681
- return null;
2682
- }
2683
- };
2684
- var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
2685
- function valid(value, walletAddress, chainId, now) {
2686
- return Boolean(
2687
- 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
2688
- );
2689
- }
2690
- function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
2691
- if (typeof window === "undefined") return null;
2692
- const key2 = keyFor(walletAddress, chainId);
2693
- const store = storage3();
2694
- let parsed = null;
2695
- try {
2696
- const raw2 = store?.getItem(key2);
2697
- parsed = raw2 ? JSON.parse(raw2) : null;
2698
- } catch {
2699
- parsed = null;
2700
- }
2701
- const candidate = parsed ?? memoryIdentities.get(key2);
2702
- if (valid(candidate, walletAddress, chainId, now)) {
2703
- memoryIdentities.set(key2, candidate);
2704
- return { userId: candidate.userId };
2705
- }
2706
- memoryIdentities.delete(key2);
2707
- try {
2708
- store?.removeItem(key2);
2709
- } catch {
2710
- }
2711
- return null;
2712
- }
2713
- function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
2714
- if (typeof window === "undefined") return;
2715
- const identity = {
2716
- userId,
2717
- walletAddress,
2718
- chainId,
2719
- expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
2720
- };
2721
- if (!valid(identity, walletAddress, chainId, now)) return;
2722
- const key2 = keyFor(walletAddress, chainId);
2723
- memoryIdentities.set(key2, identity);
2724
- try {
2725
- storage3()?.setItem(key2, JSON.stringify(identity));
2726
- } catch {
2727
- }
2728
- }
2729
- function clearYieldseekerIdentity(walletAddress, chainId) {
2730
- const key2 = keyFor(walletAddress, chainId);
2731
- memoryIdentities.delete(key2);
2732
- try {
2733
- storage3()?.removeItem(key2);
2734
- } catch {
2735
- }
2736
- }
2737
-
2738
- // src/agents/yieldseeker/yieldseeker.client.ts
2739
- var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2740
- function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
2741
- return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
2742
- }
2743
- var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
2744
- var YieldseekerApiError = class extends Error {
2745
- constructor(status, providerCode, responseFields) {
2746
- super(`Yieldseeker request failed (${status}): ${providerCode}`);
2747
- this.status = status;
2748
- this.providerCode = providerCode;
2749
- this.responseFields = responseFields;
2750
- this.name = "YieldseekerApiError";
2751
- }
2752
- status;
2753
- providerCode;
2754
- responseFields;
2755
- get isAuthenticationError() {
2756
- return this.status === 401 || this.status === 403;
2757
- }
2758
- };
2759
- function providerError(body, fallback) {
2760
- if (!body || typeof body !== "object") return { code: fallback };
2761
- const record = body;
2762
- return {
2763
- code: typeof record.message === "string" ? record.message : fallback,
2764
- fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
2765
- };
2766
- }
2767
- var YieldseekerApiClient = class {
2768
- constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
2769
- this.owneyApiKey = owneyApiKey;
2770
- this.baseUrl = baseUrl;
2771
- this.fetchFn = fetchFn;
2772
- }
2773
- owneyApiKey;
2774
- baseUrl;
2775
- fetchFn;
2776
- async request(path, options = {}) {
2777
- const controller = new AbortController();
2778
- const timer = setTimeout(
2779
- () => controller.abort(),
2780
- options.timeoutMs ?? 15e3
2781
- );
2782
- try {
2783
- const response = await this.fetchFn(`${this.baseUrl}${path}`, {
2784
- method: options.method ?? "GET",
2785
- headers: {
2786
- "Content-Type": "application/json",
2787
- "x-owney-api-key": this.owneyApiKey,
2788
- ...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
2789
- },
2790
- body: options.body ? JSON.stringify(options.body) : void 0,
2791
- signal: controller.signal
2792
- });
2793
- const payload = await response.json().catch(() => null);
2794
- if (!response.ok) {
2795
- const error = providerError(payload, `HTTP_${response.status}`);
2796
- throw new YieldseekerApiError(
2797
- response.status,
2798
- error.code,
2799
- error.fields
2800
- );
2801
- }
2802
- if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
2803
- return payload.data;
2804
- }
2805
- return payload;
2806
- } catch (error) {
2807
- if (error instanceof YieldseekerApiError) throw error;
2808
- if (error instanceof DOMException && error.name === "AbortError") {
2809
- throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
2810
- }
2811
- throw new YieldseekerApiError(0, "NETWORK_ERROR", {
2812
- cause: error instanceof Error ? error.message : String(error)
2813
- });
2814
- } finally {
2815
- clearTimeout(timer);
2816
- }
2817
- }
2818
- };
2819
-
2820
- // src/agents/yieldseeker/yieldseeker.mapper.ts
2821
- var import_viem5 = require("viem");
2822
-
2823
- // src/agents/yieldseeker/yieldseeker.types.ts
2824
- var YIELDSEEKER_ASSET_METADATA = {
2825
- USDC: {
2826
- address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2827
- decimals: 6
2828
- },
2829
- WETH: {
2830
- address: "0x4200000000000000000000000000000000000006",
2831
- decimals: 18
2832
- }
2833
- };
2834
-
2835
- // src/agents/yieldseeker/yieldseeker.mapper.ts
2836
- function invalid(endpoint, detail) {
2837
- throw new OwneyError(
2838
- "AGENT_INVALID_RESPONSE",
2839
- `Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
2840
- { endpoint, detail },
2841
- "yieldseeker"
2842
- );
2843
- }
2844
- function raw(value, endpoint) {
2845
- if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
2846
- return invalid(endpoint, "expected a base-10 integer string");
2847
- }
2848
- return BigInt(value);
2849
- }
2850
- function decimal(value, decimals, endpoint) {
2851
- return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
2852
- }
2853
- function usd(rawAmount, decimals, price) {
2854
- return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
2855
- }
2856
- function percent(value) {
2857
- const result = Number(value);
2858
- return Number.isFinite(result) ? result * 100 : 0;
2859
- }
2860
- var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
2861
- function publicApyAfterYieldseekerFee(value) {
2862
- const grossPercent = percent(value);
2863
- if (grossPercent <= 0) return grossPercent;
2864
- const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
2865
- return Math.round(netPercent * 1e12) / 1e12;
2866
- }
2867
- function riskAdjustedApyForDays(option, days) {
2868
- if (days === "7D") return option.riskAdjustedApy7dAverage;
2869
- if (days === "30D") return option.riskAdjustedApy30dAverage;
2870
- return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
2871
- }
2872
- function assetAddressValue(record, address) {
2873
- const entry = Object.entries(record).find(
2874
- ([key2]) => key2.toLowerCase() === address.toLowerCase()
2875
- );
2876
- return entry?.[1] ?? "0";
2877
- }
2878
- function position(value, asset, baseAssetDecimals) {
2879
- const option = value?.yieldOption;
2880
- if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
2881
- return invalid("yield positions", "missing vault metadata");
2882
- }
2883
- return {
2884
- chain: "BASE",
2885
- protocol: option.provider,
2886
- protocolId: option.address,
2887
- pool: option.name,
2888
- asset,
2889
- // `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
2890
- // differ from the underlying asset. Yieldseeker already converts it to
2891
- // underlying base-asset units in `assetsBase`; pair that value with the
2892
- // snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
2893
- // share quantity separately because withdraw-from-position expects it.
2894
- amount: decimal(
2895
- value.assetsBase,
2896
- baseAssetDecimals,
2897
- "yield positions"
2898
- ),
2899
- amountRaw: String(value.assetsRaw),
2900
- apy: percent(option.riskAdjustedApy),
2901
- tvl: Number(option.totalDepositsUsd),
2902
- liquidity: Number(option.withdrawableDepositsUsd)
2903
- };
2904
- }
2905
- function mapYieldseekerBalances(contexts) {
2906
- const tokens = [];
2907
- const assetBalances = [];
2908
- const positions = [];
2909
- let totalUsd = 0;
2910
- for (const context of contexts) {
2911
- const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
2912
- assetBalances.push({
2913
- chain: "BASE",
2914
- chainId: 8453,
2915
- asset: context.asset,
2916
- amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
2917
- });
2918
- const idle = assetAddressValue(
2919
- context.snapshot.tokenBalances,
2920
- metadata.address
2921
- );
2922
- tokens.push({
2923
- chain: "BASE",
2924
- chainId: 8453,
2925
- asset: context.asset,
2926
- amount: decimal(idle, metadata.decimals, "snapshot")
2927
- });
2928
- positions.push(
2929
- ...context.positions.map(
2930
- (entry) => position(
2931
- entry,
2932
- context.asset,
2933
- context.snapshot.baseAssetDecimals
2934
- )
2935
- )
2936
- );
2937
- totalUsd += usd(
2938
- raw(context.snapshot.totalValueBase, "snapshot"),
2939
- context.snapshot.baseAssetDecimals,
2940
- context.snapshot.baseAssetPriceUsd
2941
- );
2942
- }
2943
- return {
2944
- ...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
2945
- totalBalance: String(totalUsd),
2946
- totalBalanceAsset: "usdc",
2947
- assetBalances,
2948
- tokens,
2949
- positions
2950
- };
2951
- }
2952
- function mapYieldseekerEarnings(contexts) {
2953
- const tokens = [];
2954
- let lifetimeEarnings = 0;
2955
- for (const context of contexts) {
2956
- const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
2957
- tokens.push({
2958
- chain: "BASE",
2959
- chainId: 8453,
2960
- asset: context.asset,
2961
- amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
2962
- });
2963
- lifetimeEarnings += usd(
2964
- amount,
2965
- context.snapshot.baseAssetDecimals,
2966
- context.snapshot.baseAssetPriceUsd
2967
- );
2968
- }
2969
- return {
2970
- smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2971
- lifetimeEarnings,
2972
- tokens
2973
- };
2974
- }
2975
- function apyForDays(snapshot, days) {
2976
- if (days === "7D") return percent(snapshot.apy7d);
2977
- if (days === "30D") return percent(snapshot.apy30d);
2978
- return percent((snapshot.apy7d + snapshot.apy30d) / 2);
2979
- }
2980
- function dailyApy(point) {
2981
- const total = raw(point.totalValueBase, "historic position");
2982
- const earned = raw(point.dailyYieldBase, "historic position");
2983
- const principal = total - earned;
2984
- if (principal <= 0n || earned === 0n) return 0;
2985
- return Number(earned) / Number(principal) * 365 * 100;
2986
- }
2987
- function aggregateHistory(contexts, dayCount) {
2988
- const byDate = /* @__PURE__ */ new Map();
2989
- for (const context of contexts) {
2990
- const points = context.historic?.dailyYieldSnapshots ?? [];
2991
- for (const point of points.slice(-dayCount)) {
2992
- const valueUsd = usd(
2993
- raw(point.totalValueBase, "historic position"),
2994
- point.baseAssetDecimals,
2995
- point.baseAssetPriceUsd
2996
- );
2997
- const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
2998
- current.weighted += dailyApy(point) * valueUsd;
2999
- current.valueUsd += valueUsd;
3000
- byDate.set(point.date, current);
3001
- }
3002
- }
3003
- return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
3004
- date,
3005
- apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
3006
- }));
3007
- }
3008
- function mapYieldseekerApy(walletAddress, contexts, days) {
3009
- let weighted = 0;
3010
- let totalUsd = 0;
3011
- const byAsset = {};
3012
- for (const context of contexts) {
3013
- const valueUsd = usd(
3014
- raw(context.snapshot.totalValueBase, "snapshot"),
3015
- context.snapshot.baseAssetDecimals,
3016
- context.snapshot.baseAssetPriceUsd
3017
- );
3018
- const apy = apyForDays(context.snapshot, days);
3019
- weighted += apy * valueUsd;
3020
- totalUsd += valueUsd;
3021
- byAsset[context.asset] = apy;
3022
- }
3023
- const dayCount = Number(days.slice(0, -1));
3024
- return {
3025
- walletAddress,
3026
- weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
3027
- apyByChainAndAsset: { 8453: byAsset },
3028
- history: aggregateHistory(contexts, dayCount)
3029
- };
3030
- }
3031
- function actionType(value) {
3032
- const normalized = value.toLowerCase();
3033
- if (normalized.includes("deposit")) return "Deposit";
3034
- if (normalized.includes("withdraw")) return "Withdraw";
3035
- if (normalized.includes("yield") || normalized.includes("earn"))
3036
- return "Earned";
3037
- return "Rebalance";
3038
- }
3039
- function transactionHashes(details) {
3040
- if (!details) return [];
3041
- const values = [
3042
- details.transactionHash,
3043
- details.txHash,
3044
- ...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
3045
- ...Array.isArray(details.txHashes) ? details.txHashes : []
3046
- ];
3047
- return values.filter(
3048
- (value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
3049
- ).filter((value, index, all) => all.indexOf(value) === index);
3050
- }
3051
- function actionEntry(action) {
3052
- if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
3053
- return {
3054
- agent: "yieldseeker",
3055
- action: actionType(action.actionType),
3056
- date: action.createdDate,
3057
- oldApy: null,
3058
- newApy: null,
3059
- transactions: [
3060
- {
3061
- txHashes: transactionHashes(action.details),
3062
- chainId: 8453
3063
- }
3064
- ],
3065
- rebalanceLog: []
3066
- };
3067
- }
3068
- function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses) {
3069
- const from = movement.fromAddress.toLowerCase();
3070
- const to = movement.toAddress.toLowerCase();
3071
- const owner = ownerAddress.toLowerCase();
3072
- const agentWallet = wallet.walletAddress.toLowerCase();
3073
- const baseAsset = agent.assetAddress.toLowerCase();
3074
- if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
3075
- return void 0;
3076
- }
3077
- let action;
3078
- if (from === owner && to === agentWallet) {
3079
- action = "Top up";
3080
- } else if (from === agentWallet && to === owner) {
3081
- action = "Withdraw";
3082
- } else if (from === agentWallet && vaultAddresses.has(to)) {
3083
- action = "Deposit";
3084
- }
3085
- if (!action) return void 0;
3086
- return {
3087
- agent: "yieldseeker",
3088
- action,
3089
- date: movement.blockDate,
3090
- oldApy: null,
3091
- newApy: null,
3092
- transactions: [
3093
- {
3094
- txHashes: [movement.transactionHash],
3095
- chainId: agent.chainId,
3096
- tokenSymbol: asset,
3097
- amount: decimal(
3098
- movement.assetAmount,
3099
- YIELDSEEKER_ASSET_METADATA[asset].decimals,
3100
- "historic position"
3101
- )
3102
- }
3103
- ],
3104
- rebalanceLog: []
3105
- };
3106
- }
3107
- function mapYieldseekerHistory(contexts, options) {
3108
- const entries = contexts.flatMap((context) => {
3109
- const vaultAddresses = new Set(
3110
- context.positions.map(
3111
- (position2) => position2.yieldOption.address.toLowerCase()
3112
- )
3113
- );
3114
- return [
3115
- ...(context.historic?.movements ?? []).map(
3116
- (movement) => movementEntry(
3117
- movement,
3118
- context.wallet,
3119
- context.agent,
3120
- context.asset,
3121
- options.ownerAddress,
3122
- vaultAddresses
3123
- )
3124
- ),
3125
- ...(context.actions ?? []).map(actionEntry)
3126
- ].filter((entry) => entry !== void 0);
3127
- });
3128
- const filtered = entries.filter(
3129
- (entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
3130
- ).filter((entry, index, all) => {
3131
- const transactionHash = entry.transactions[0]?.txHashes[0];
3132
- if (!transactionHash) return true;
3133
- return all.findIndex(
3134
- (candidate) => candidate.action === entry.action && candidate.transactions[0]?.txHashes[0] === transactionHash
3135
- ) === index;
3136
- }).sort((left, right) => right.date.localeCompare(left.date));
3137
- return {
3138
- data: filtered.slice(0, options.limit),
3139
- // v1 returns the whole action/movement collection and defines no cursor.
3140
- // Report a terminal page so callers never loop over the same prefix.
3141
- hasMore: false
3142
- };
3143
- }
3144
- function mapYieldseekerProfile(address, contexts) {
3145
- const protocols = /* @__PURE__ */ new Set();
3146
- for (const context of contexts) {
3147
- for (const current of context.positions) {
3148
- if (current.yieldOption?.provider) {
3149
- protocols.add(String(current.yieldOption.provider));
3150
- }
3151
- }
3152
- }
3153
- return {
3154
- address,
3155
- smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
3156
- chains: contexts.length > 0 ? [8453] : [],
3157
- hasActiveSessionKey: contexts.some(
3158
- (context) => context.wallet.initializedDate != null
3159
- ),
3160
- protocols: [...protocols]
3161
- };
3162
- }
3163
- function mapYieldseekerAgentApy(options, days) {
3164
- const perAsset = {};
3165
- const all = [];
3166
- for (const entry of options) {
3167
- const apys = entry.yieldOptions.map(
3168
- (option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
3169
- ).filter(Number.isFinite);
3170
- if (apys.length === 0) continue;
3171
- const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
3172
- perAsset[entry.asset] = average;
3173
- all.push(average);
3174
- }
3175
- return {
3176
- averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
3177
- detailedApys: { apyPerAsset: { 8453: perAsset } }
3178
- };
3179
- }
3180
-
3181
- // src/agents/yieldseeker/yieldseeker.agent.ts
3182
- var OWNEY_AGENT_NAME = "owney";
3183
- var YIELDSEEKER_USERNAME_PREFIX = "owney_";
3184
- var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
3185
- var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
3186
- function generateYieldseekerUsername() {
3187
- const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
3188
- return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
3189
- }
3190
- function isUsernameConflict(error) {
3191
- if (!(error instanceof YieldseekerApiError)) return false;
3192
- const code = error.providerCode.toUpperCase();
3193
- return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
3194
- }
3195
- var YIELDSEEKER_AGENT_WALLET_ABI = [
3196
- {
3197
- type: "function",
3198
- name: "withdrawAssetToUser",
3199
- stateMutability: "nonpayable",
3200
- inputs: [
3201
- { name: "recipient", type: "address" },
3202
- { name: "asset", type: "address" },
3203
- { name: "amount", type: "uint256" }
3204
- ],
3205
- outputs: []
3206
- },
3207
- {
3208
- type: "function",
3209
- name: "withdrawAllAssetToUser",
3210
- stateMutability: "nonpayable",
3211
- inputs: [
3212
- { name: "recipient", type: "address" },
3213
- { name: "asset", type: "address" }
3214
- ],
3215
- outputs: []
3216
- }
3217
- ];
3218
- function query(params) {
3219
- const search = new URLSearchParams();
3220
- for (const [key2, value] of Object.entries(params)) {
3221
- if (value !== void 0) search.set(key2, String(value));
3222
- }
3223
- const encoded = search.toString();
3224
- return encoded ? `?${encoded}` : "";
3225
- }
3226
- var YieldseekerAgent = class {
3227
- id = "yieldseeker";
3228
- balanceComposition = "tokens-plus-positions";
3229
- supportedChainIds = [8453];
3230
- supportedAssets = [
3231
- {
3232
- chainId: 8453,
3233
- chain: "BASE",
3234
- assets: [
3235
- { symbol: "USDC", minDepositAmount: "10000000" },
3236
- { symbol: "WETH", minDepositAmount: "1" }
3237
- ]
3238
- }
3239
- ];
3240
- api;
3241
- auth;
3242
- transactionExecutor;
3243
- unwindReceiptWaiter;
3244
- agentContexts = /* @__PURE__ */ new Map();
3245
- users = /* @__PURE__ */ new Map();
3246
- pendingAgents = /* @__PURE__ */ new Map();
3247
- constructor(owneyApiKey, options = {}) {
3248
- this.api = new YieldseekerApiClient(
3249
- owneyApiKey,
3250
- options.baseUrl ?? getYieldseekerProxyBaseUrl(),
3251
- options.fetchFn
3252
- );
3253
- this.auth = new YieldseekerAuth(options.auth);
3254
- this.transactionExecutor = options.transactionExecutor;
3255
- this.unwindReceiptWaiter = options.unwindReceiptWaiter;
3256
- }
3257
- async disconnect() {
3258
- this.auth.clear();
3259
- for (const key2 of this.users.keys()) {
3260
- const [walletAddress, chainId] = key2.split(":");
3261
- clearYieldseekerIdentity(walletAddress, Number(chainId));
3262
- }
3263
- this.users.clear();
3264
- this.agentContexts.clear();
3265
- this.pendingAgents.clear();
3266
- }
3267
- async activateAgent(state, chainId, asset) {
3268
- this.assertChain(chainId);
3269
- const targetAsset = asset ?? "USDC";
3270
- this.assertAsset(targetAsset);
3271
- await this.ensureAgent(state, chainId, targetAsset);
3272
- }
3273
- async deposit(state, chainId, amount, asset, depositCallback) {
3274
- this.assertChain(chainId);
3275
- this.assertAsset(asset);
3276
- if (BigInt(amount) <= 0n) {
3277
- throw new OwneyError(
3278
- "DEPOSIT_AMOUNT_BELOW_MINIMUM",
3279
- "Yieldseeker deposits must be greater than zero.",
3280
- { amount, minDepositAmount: "1" },
3281
- this.id
3282
- );
3283
- }
3284
- const context = await this.ensureAgent(state, chainId, asset);
3285
- let txHash;
3286
- try {
3287
- if (depositCallback) {
3288
- provideDepositVerificationContext(depositCallback, {
3289
- agentId: "yieldseeker",
3290
- signature: await this.auth.getToken(state, chainId),
3291
- userId: context.user.userId,
3292
- yieldseekerAgentId: context.agent.agentId
3293
- });
3294
- txHash = await depositCallback(
3295
- context.wallet.walletAddress,
3296
- chainId,
3297
- amount
3298
- );
3299
- await this.waitForReceipt(state, chainId, txHash);
3300
- } else {
3301
- txHash = await this.submitTransaction(state, chainId, {
3302
- from: (0, import_viem6.getAddress)(state.walletAddress),
3303
- to: YIELDSEEKER_ASSET_METADATA[asset].address,
3304
- data: (0, import_viem6.encodeFunctionData)({
3305
- abi: import_viem6.erc20Abi,
3306
- functionName: "transfer",
3307
- args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
3308
- }),
3309
- value: "0",
3310
- chainId
3311
- });
3312
- }
3313
- await this.deployAfterFunding(state, chainId, context);
3314
- } finally {
3315
- await this.refreshSnapshotAfterMovement(
3316
- state,
3317
- chainId,
3318
- context,
3319
- "deposit"
3320
- );
3321
- }
3322
- return {
3323
- txHash,
3324
- smartWallet: context.wallet.walletAddress,
3325
- amount
3326
- };
3327
- }
3328
- async withdraw(state, chainId, asset, amount) {
3329
- this.assertChain(chainId);
3330
- this.assertAsset(asset);
3331
- if (amount !== void 0 && BigInt(amount) <= 0n) {
3332
- throw new OwneyError(
3333
- "WITHDRAW_FAILED",
3334
- "Yieldseeker withdrawals must be greater than zero.",
3335
- { amount },
3336
- this.id
3337
- );
3338
- }
3339
- const context = await this.findAgent(state, chainId, asset);
3340
- if (!context) {
3341
- throw new OwneyError(
3342
- "WITHDRAW_INSUFFICIENT_BALANCE",
3343
- `No Yieldseeker ${asset} agent exists for this wallet.`,
3344
- { asset, available: "0" },
3345
- this.id
3346
- );
3347
- }
3348
- try {
3349
- const portfolio = await this.loadPortfolioContext(
3350
- state,
3351
- chainId,
3352
- context
3353
- );
3354
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3355
- const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
3356
- ([address]) => address.toLowerCase() === metadata.address.toLowerCase()
3357
- );
3358
- const idle = BigInt(idleEntry?.[1] ?? "0");
3359
- const deployed = portfolio.positions.reduce(
3360
- (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
3361
- 0n
3362
- );
3363
- const totalAvailable = idle + deployed;
3364
- const requested = amount === void 0 ? totalAvailable : BigInt(amount);
3365
- if (requested > totalAvailable) {
3366
- throw new OwneyError(
3367
- "WITHDRAW_INSUFFICIENT_BALANCE",
3368
- `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
3369
- {
3370
- asset,
3371
- requested: requested.toString(),
3372
- available: totalAvailable.toString()
3373
- },
3374
- this.id
3375
- );
3376
- }
3377
- let remaining = requested > idle ? requested - idle : 0n;
3378
- for (const position2 of portfolio.positions) {
3379
- if (remaining === 0n) break;
3380
- const available = BigInt(position2.withdrawableAssetsRaw);
3381
- if (available <= 0n) continue;
3382
- const assetsRaw = available < remaining ? available : remaining;
3383
- const response = await this.walletRequest(
3384
- state,
3385
- chainId,
3386
- this.agentPath(context, "withdraw-from-position"),
3387
- {
3388
- method: "POST",
3389
- body: {
3390
- chainId,
3391
- vaultAddress: position2.yieldOption.address,
3392
- assetsRaw: assetsRaw.toString()
3393
- }
3394
- }
3395
- );
3396
- if (!this.isTransactionHash(response?.transactionHash)) {
3397
- throw this.invalidResponse("position withdrawal");
3398
- }
3399
- await this.waitForReceipt(state, chainId, response.transactionHash);
3400
- remaining -= assetsRaw;
3401
- }
3402
- if (remaining > 0n) {
3403
- throw this.invalidResponse("yield positions", {
3404
- reason: "Withdrawable positions could not cover the request.",
3405
- remaining: remaining.toString()
3406
- });
3407
- }
3408
- const account = (0, import_viem6.getAddress)(state.walletAddress);
3409
- const txHash = await this.submitTransaction(state, chainId, {
3410
- from: account,
3411
- to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
3412
- data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
3413
- abi: YIELDSEEKER_AGENT_WALLET_ABI,
3414
- functionName: "withdrawAllAssetToUser",
3415
- args: [account, metadata.address]
3416
- }) : (0, import_viem6.encodeFunctionData)({
3417
- abi: YIELDSEEKER_AGENT_WALLET_ABI,
3418
- functionName: "withdrawAssetToUser",
3419
- args: [account, metadata.address, requested]
3420
- }),
3421
- value: "0",
3422
- chainId
3423
- });
3424
- return {
3425
- txHash,
3426
- type: amount === void 0 ? "full" : "partial",
3427
- amount: requested.toString()
3428
- };
3429
- } finally {
3430
- await this.refreshSnapshotAfterMovement(
3431
- state,
3432
- chainId,
3433
- context,
3434
- "withdrawal"
3435
- );
3436
- }
3437
- }
3438
- async getBalances(state, chainId) {
3439
- this.assertChain(chainId);
3440
- return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
3441
- }
3442
- async getEarnings(state, chainId) {
3443
- this.assertChain(chainId);
3444
- return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
3445
- }
3446
- async getAccountApy(state, chainId, days, tokenSymbol) {
3447
- this.assertChain(chainId);
3448
- const asset = tokenSymbol?.toUpperCase();
3449
- if (asset !== void 0) this.assertAsset(asset);
3450
- const contexts = await this.loadPortfolio(state, chainId, {
3451
- ...asset ? { asset } : {},
3452
- historic: true
3453
- });
3454
- return mapYieldseekerApy(state.walletAddress, contexts, days);
3455
- }
3456
- async getHistory(state, chainId, options) {
3457
- this.assertChain(chainId);
3458
- const asset = options?.tokenSymbol?.toUpperCase();
3459
- if (asset !== void 0) this.assertAsset(asset);
3460
- const contexts = await this.loadPortfolio(state, chainId, {
3461
- ...asset ? { asset } : {},
3462
- historic: true,
3463
- actions: true
3464
- });
3465
- return mapYieldseekerHistory(contexts, {
3466
- limit: options?.limit ?? 10,
3467
- ownerAddress: state.walletAddress,
3468
- ...options?.fromDate ? { fromDate: options.fromDate } : {},
3469
- ...options?.toDate ? { toDate: options.toDate } : {}
3470
- });
3471
- }
3472
- async getUserProfile(state, chainId) {
3473
- this.assertChain(chainId);
3474
- return mapYieldseekerProfile(
3475
- state.walletAddress,
3476
- await this.loadPortfolio(state, chainId, {})
3477
- );
3478
- }
3479
- async getAgentApy(days, options) {
3480
- this.assertOptionalChain(options?.chainId);
3481
- const requested = options?.tokenSymbol?.toUpperCase();
3482
- if (requested !== void 0) this.assertAsset(requested);
3483
- const assets = requested ? [requested] : ["USDC", "WETH"];
3484
- const values = await Promise.all(
3485
- assets.map(async (asset) => {
3486
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3487
- const response = await this.api.request(
3488
- `/chains/8453/assets/${metadata.address}/yield-options`
3489
- );
3490
- if (!Array.isArray(response?.yieldOptions)) {
3491
- throw this.invalidResponse("yield options");
3492
- }
3493
- return { asset, yieldOptions: response.yieldOptions };
3494
- })
3495
- );
3496
- return mapYieldseekerAgentApy(values, days);
3497
- }
3498
- userKey(state, chainId) {
3499
- return `${state.walletAddress.toLowerCase()}:${chainId}`;
3500
- }
3501
- contextKey(state, chainId, asset) {
3502
- return `${this.userKey(state, chainId)}:${asset}`;
3503
- }
3504
- async resolveUser(state, chainId) {
3505
- const key2 = this.userKey(state, chainId);
3506
- const inMemory = this.users.get(key2);
3507
- if (inMemory) return inMemory;
3508
- const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
3509
- if (persisted) {
3510
- this.users.set(key2, persisted);
3511
- return persisted;
3512
- }
3513
- const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
3514
- let user = null;
3515
- try {
3516
- const login = await this.providerRequest(
3517
- state,
3518
- chainId,
3519
- "/users/login-with-wallet",
3520
- { method: "POST", body: { walletAddress } }
3521
- );
3522
- user = login?.user ?? null;
3523
- if (!user) {
3524
- throw this.invalidResponse("wallet login", {
3525
- reason: "A successful login returned no user."
3526
- });
3527
- }
3528
- } catch (error) {
3529
- if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
3530
- if (error instanceof OwneyError) throw error;
3531
- throw this.mapApiError(error);
3532
- }
3533
- let created;
3534
- for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
3535
- try {
3536
- created = await this.providerRequest(
3537
- state,
3538
- chainId,
3539
- "/users",
3540
- {
3541
- method: "POST",
3542
- body: {
3543
- walletAddress,
3544
- username: generateYieldseekerUsername()
3545
- }
3546
- }
3547
- );
3548
- break;
3549
- } catch (createError) {
3550
- const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
3551
- if (canRetry) continue;
3552
- throw this.mapApiError(createError);
3553
- }
3554
- }
3555
- user = created?.user ?? null;
3556
- }
3557
- if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
3558
- throw this.invalidResponse("wallet identity");
3559
- }
3560
- const resolved = { userId: user.userId };
3561
- this.users.set(key2, resolved);
3562
- writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
3563
- return resolved;
3564
- }
3565
- forgetUser(state, chainId) {
3566
- this.users.delete(this.userKey(state, chainId));
3567
- clearYieldseekerIdentity(state.walletAddress, chainId);
3568
- }
3569
- async ensureAgent(state, chainId, asset) {
3570
- const key2 = this.contextKey(state, chainId, asset);
3571
- const cached = this.agentContexts.get(key2);
3572
- if (cached) return cached;
3573
- const pending = this.pendingAgents.get(key2);
3574
- if (pending) return pending;
3575
- const request = this.resolveAgent(state, chainId, asset, true).then(
3576
- (context) => {
3577
- if (!context) throw this.invalidResponse("agent creation");
3578
- this.agentContexts.set(key2, context);
3579
- return context;
3580
- }
3581
- );
3582
- this.pendingAgents.set(key2, request);
3583
- try {
3584
- return await request;
3585
- } finally {
3586
- this.pendingAgents.delete(key2);
3587
- }
3588
- }
3589
- async findAgent(state, chainId, asset) {
3590
- const key2 = this.contextKey(state, chainId, asset);
3591
- const cached = this.agentContexts.get(key2);
3592
- if (cached) return cached;
3593
- const context = await this.resolveAgent(state, chainId, asset, false);
3594
- if (context) this.agentContexts.set(key2, context);
3595
- return context;
3596
- }
3597
- async resolveAgent(state, chainId, asset, createIfMissing) {
3598
- const user = await this.resolveUser(state, chainId);
3599
- const response = await this.walletRequest(
3600
- state,
3601
- chainId,
3602
- `/users/${user.userId}/agents`
3603
- );
3604
- if (!Array.isArray(response?.agents)) {
3605
- throw this.invalidResponse("agent list");
3606
- }
3607
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3608
- let agent = response.agents.find(
3609
- (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3610
- );
3611
- if (!agent && createIfMissing) {
3612
- const created = await this.walletRequest(
3613
- state,
3614
- chainId,
3615
- `/users/${user.userId}/agents`,
3616
- {
3617
- method: "POST",
3618
- body: {
3619
- name: OWNEY_AGENT_NAME,
3620
- emoji: "\u{1F989}",
3621
- chainId,
3622
- assetAddress: metadata.address,
3623
- type: "vault",
3624
- rulePreset: null
3625
- }
3626
- }
3627
- );
3628
- agent = created?.agent;
3629
- }
3630
- if (!agent) return null;
3631
- this.assertAgent(agent);
3632
- const walletResponse = await this.walletRequest(
3633
- state,
3634
- chainId,
3635
- `/users/${user.userId}/agents/${agent.agentId}/wallet`
3636
- );
3637
- if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3638
- throw this.invalidResponse("agent wallet");
3639
- }
3640
- return { user, agent, wallet: walletResponse.agentWallet, asset };
3641
- }
3642
- async loadPortfolio(state, chainId, options) {
3643
- const user = await this.resolveUser(state, chainId);
3644
- const response = await this.walletRequest(
3645
- state,
3646
- chainId,
3647
- `/users/${user.userId}/agents`
3648
- );
3649
- if (!Array.isArray(response?.agents)) {
3650
- throw this.invalidResponse("agent list");
3651
- }
3652
- const contexts = [];
3653
- for (const agent of response.agents) {
3654
- const asset = this.assetForAgent(agent);
3655
- if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3656
- continue;
3657
- }
3658
- this.assertAgent(agent);
3659
- const walletResponse = await this.walletRequest(
3660
- state,
3661
- chainId,
3662
- `/users/${user.userId}/agents/${agent.agentId}/wallet`
3663
- );
3664
- if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3665
- throw this.invalidResponse("agent wallet");
3666
- }
3667
- const context = {
3668
- user,
3669
- agent,
3670
- wallet: walletResponse.agentWallet,
3671
- asset
3672
- };
3673
- this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3674
- contexts.push(context);
3675
- }
3676
- return Promise.all(
3677
- contexts.map(
3678
- (context) => this.loadPortfolioContext(state, chainId, context, options)
3679
- )
3680
- );
3681
- }
3682
- async loadPortfolioContext(state, chainId, context, options = {}) {
3683
- const [snapshot, positions, historic, actions] = await Promise.all([
3684
- this.walletRequest(
3685
- state,
3686
- chainId,
3687
- `${this.agentPath(context, "snapshot")}${query({
3688
- shouldOnlyUseRecentValue: true,
3689
- shouldAllowStaleOnError: true
3690
- })}`
3691
- ),
3692
- this.walletRequest(
3693
- state,
3694
- chainId,
3695
- this.agentPath(context, "yield-positions")
3696
- ),
3697
- options.historic ? this.walletRequest(
3698
- state,
3699
- chainId,
3700
- this.agentPath(context, "wallet/historic-position")
3701
- ) : Promise.resolve(void 0),
3702
- options.actions ? this.walletRequest(
3703
- state,
3704
- chainId,
3705
- this.agentPath(context, "actions")
3706
- ) : Promise.resolve(void 0)
3707
- ]);
3708
- if (!snapshot?.agentSnapshot) {
3709
- throw this.invalidResponse("agent snapshot");
3710
- }
3711
- if (!Array.isArray(positions?.yieldPositions)) {
3712
- throw this.invalidResponse("yield positions");
3713
- }
3714
- return {
3715
- ...context,
3716
- snapshot: snapshot.agentSnapshot,
3717
- positions: positions.yieldPositions,
3718
- ...historic?.position ? { historic: historic.position } : {},
3719
- ...actions?.actions ? { actions: actions.actions } : {}
3720
- };
3721
- }
3722
- async deployAfterFunding(state, chainId, context) {
3723
- if (context.wallet.initializedDate != null) return;
3724
- try {
3725
- const deployed = await this.walletRequest(
3726
- state,
3727
- chainId,
3728
- this.agentPath(context, "deploy"),
3729
- { method: "POST", body: {} }
3730
- );
3731
- if (deployed?.agentWallet) {
3732
- context.wallet = deployed.agentWallet;
3733
- }
3734
- } catch (error) {
3735
- console.warn(
3736
- "[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
3737
- error
3738
- );
3739
- }
3740
- }
3741
- async refreshSnapshotAfterMovement(state, chainId, context, movement) {
3742
- try {
3743
- const response = await this.walletRequest(
3744
- state,
3745
- chainId,
3746
- `${this.agentPath(context, "snapshot")}${query({
3747
- shouldForceRefresh: true
3748
- })}`
3749
- );
3750
- if (!response?.agentSnapshot) {
3751
- throw this.invalidResponse("agent snapshot refresh");
3752
- }
3753
- } catch (error) {
3754
- console.warn(
3755
- `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3756
- error
3757
- );
3758
- }
3759
- }
3760
- agentPath(context, suffix) {
3761
- return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
3762
- }
3763
- async walletRequest(state, chainId, path, options = {}) {
3764
- try {
3765
- return await this.providerRequest(state, chainId, path, options);
3766
- } catch (error) {
3767
- throw this.mapApiError(error);
3768
- }
3769
- }
3770
- async providerRequest(state, chainId, path, options = {}) {
3771
- this.assertChain(chainId);
3772
- const request = (signature2) => this.api.request(path, {
3773
- ...options,
3774
- signature: signature2
3775
- });
3776
- let signature = await this.auth.getToken(state, chainId);
3777
- try {
3778
- return await request(signature);
3779
- } catch (error) {
3780
- if (!(error instanceof YieldseekerApiError)) throw error;
3781
- if (error.providerCode === "NO_USER") throw error;
3782
- if (!error.isAuthenticationError) throw error;
3783
- this.auth.clear(state, chainId);
3784
- signature = await this.auth.getToken(state, chainId);
3785
- try {
3786
- return await request(signature);
3787
- } catch (retryError) {
3788
- if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
3789
- this.forgetUser(state, chainId);
3790
- }
3791
- throw retryError;
3792
- }
3793
- }
3794
- }
3795
- mapApiError(error) {
3796
- if (!(error instanceof YieldseekerApiError)) {
3797
- return new OwneyError(
3798
- "AGENT_API_ERROR",
3799
- "Yieldseeker request failed.",
3800
- { cause: error instanceof Error ? error.message : String(error) },
3801
- this.id
3802
- );
3803
- }
3804
- const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
3805
- return new OwneyError(
3806
- code,
3807
- `Yieldseeker request failed: ${error.providerCode}.`,
3808
- {
3809
- statusCode: error.status,
3810
- providerCode: error.providerCode,
3811
- ...error.responseFields ? { fields: error.responseFields } : {}
3812
- },
3813
- this.id
3814
- );
3815
- }
3816
- async submitTransaction(state, chainId, transaction) {
3817
- if (this.transactionExecutor) {
3818
- return this.transactionExecutor(state, chainId, transaction);
3819
- }
3820
- this.assertTransaction(transaction, state, chainId);
3821
- const account = (0, import_viem6.getAddress)(state.walletAddress);
3822
- const walletClient = (0, import_viem6.createWalletClient)({
3823
- account,
3824
- chain: import_chains3.base,
3825
- transport: (0, import_viem6.custom)(state.provider)
3826
- });
3827
- const publicClient = (0, import_viem6.createPublicClient)({
3828
- chain: import_chains3.base,
3829
- transport: (0, import_viem6.custom)(state.provider)
3830
- });
3831
- await ensureWalletOnChain(
3832
- publicClient,
3833
- walletClient,
3834
- 8453
3835
- );
3836
- const hash = await walletClient.sendTransaction({
3837
- account,
3838
- chain: import_chains3.base,
3839
- to: (0, import_viem6.getAddress)(transaction.to),
3840
- data: transaction.data,
3841
- value: BigInt(transaction.value)
3842
- });
3843
- const receipt = await publicClient.waitForTransactionReceipt({
3844
- hash,
3845
- confirmations: 1
3846
- });
3847
- if (receipt.status !== "success") {
3848
- throw new OwneyError(
3849
- "AGENT_TRANSACTION_REVERTED",
3850
- `Yieldseeker transaction reverted (${hash}).`,
3851
- { transactionHash: hash },
3852
- this.id
3853
- );
3854
- }
3855
- return hash;
3856
- }
3857
- async waitForReceipt(state, chainId, transactionHash) {
3858
- if (this.unwindReceiptWaiter) {
3859
- await this.unwindReceiptWaiter(state, chainId, transactionHash);
3860
- return;
3861
- }
3862
- const publicClient = (0, import_viem6.createPublicClient)({
3863
- chain: import_chains3.base,
3864
- transport: (0, import_viem6.custom)(state.provider)
3865
- });
3866
- const receipt = await publicClient.waitForTransactionReceipt({
3867
- hash: transactionHash,
3868
- confirmations: 1
3869
- });
3870
- if (receipt.status !== "success") {
3871
- throw new OwneyError(
3872
- "AGENT_TRANSACTION_REVERTED",
3873
- `Yieldseeker transaction reverted (${transactionHash}).`,
3874
- { transactionHash },
3875
- this.id
3876
- );
3877
- }
3878
- }
3879
- assertTransaction(transaction, state, chainId) {
3880
- 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)) {
3881
- throw this.invalidResponse("transaction");
3882
- }
3883
- }
3884
- assertAgent(agent) {
3885
- if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
3886
- throw this.invalidResponse("agent");
3887
- }
3888
- }
3889
- isOwneyAgent(agent) {
3890
- return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
3891
- }
3892
- assetForAgent(agent) {
3893
- for (const asset of ["USDC", "WETH"]) {
3894
- if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
3895
- return asset;
3896
- }
3897
- }
3898
- return null;
3899
- }
3900
- isTransactionHash(value) {
3901
- return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
3902
- }
3903
- assertChain(chainId) {
3904
- if (chainId !== 8453) {
3905
- throw new OwneyError(
3906
- "CHAIN_UNSUPPORTED",
3907
- `Yieldseeker does not support chain ${chainId}.`,
3908
- { chainId, supportedChainIds: [8453] },
3909
- this.id
3910
- );
3911
- }
3912
- }
3913
- assertOptionalChain(chainId) {
3914
- if (chainId !== void 0) this.assertChain(chainId);
3915
- }
3916
- assertAsset(asset) {
3917
- if (asset !== "USDC" && asset !== "WETH") {
3918
- throw new OwneyError(
3919
- "ASSET_UNSUPPORTED",
3920
- `Yieldseeker does not support asset ${asset} in the Owney rollout.`,
3921
- {
3922
- asset,
3923
- supportedAssets: ["USDC", "WETH"],
3924
- providerAlsoAdvertises: ["cbBTC"]
3925
- },
3926
- this.id
3927
- );
3928
- }
3929
- }
3930
- invalidResponse(operation, details = {}) {
3931
- return new OwneyError(
3932
- "AGENT_INVALID_RESPONSE",
3933
- `Yieldseeker returned an invalid ${operation} response.`,
3934
- details,
3935
- this.id
3936
- );
3937
- }
3938
- };
3939
-
3940
- // src/lib/routing-api.ts
3941
- var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3942
- async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3943
- const url = `${baseUrl}/api/v1/agent/org-config`;
3944
- try {
3945
- const res = await fetch(url, {
3946
- method: "GET",
3947
- headers: {
3948
- "Content-Type": "application/json",
3949
- "x-owney-api-key": `${apiKey}`
3950
- }
3951
- });
3952
- if (!res.ok) {
3953
- if (res.status !== 404) {
3954
- console.warn(
3955
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
3956
- );
3957
- }
3958
- return null;
3959
- }
3960
- const json = await res.json();
3961
- const policy = json.success ? json.data ?? null : null;
3962
- debugLog(
3963
- "owney-sdk",
3964
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
3965
- policy ?? void 0
3966
- );
3967
- return policy;
3968
- } catch (error) {
3969
- console.warn(
3970
- "[owney-sdk] Could not read org agent config (non-fatal):",
3971
- error instanceof Error ? error.message : String(error)
3972
- );
3973
- return null;
3974
- }
3975
- }
3976
- async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3977
- const url = `${baseUrl}/api/v1/agent/keys`;
3978
- const res = await fetch(url, {
3979
- method: "GET",
3980
- headers: {
3981
- "Content-Type": "application/json",
3982
- "x-owney-api-key": `${apiKey}`
3983
- }
3984
- });
3985
- if (!res.ok) {
3986
- const text = await res.text().catch(() => "");
3987
- throw new OwneyError(
3988
- "API_ROUTING_ERROR",
3989
- `Routing API error ${res.status}: ${text}`,
3990
- { statusCode: res.status, responseBody: text }
3991
- );
3992
- }
3993
- const json = await res.json();
3994
- if (!json.success) {
3995
- throw new OwneyError(
3996
- "API_ROUTING_FAILED",
3997
- `Routing API request failed: ${json.message}`,
3998
- { message: json.message }
3999
- );
4000
- }
4001
- return json.data;
4002
- }
4003
-
4004
- // src/lib/health-report.ts
4005
- var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
4006
- async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
4007
- try {
4008
- await fetch(`${baseUrl}/api/v1/agent/health-report`, {
4009
- method: "POST",
4010
- headers: {
4011
- "Content-Type": "application/json",
4012
- "x-owney-api-key": apiKey
4013
- },
4014
- body: JSON.stringify({
4015
- agent_type: agentType,
4016
- error_code: errorCode,
4017
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
4018
- })
4019
- });
4020
- } catch (err) {
4021
- console.warn(
4022
- `[owney-sdk] health-report failed for agent "${agentType}":`,
4023
- err instanceof Error ? err.message : err
4024
- );
4025
- }
4026
- }
4027
- async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
4028
- try {
4029
- return await fn();
4030
- } catch (err) {
4031
- const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
4032
- void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
4033
- throw err;
4034
- }
4035
- }
4036
-
4037
- // src/lib/helpers/withdraw-helper.ts
4038
- var import_viem7 = require("viem");
4039
- function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
4040
- const target = asset.toUpperCase();
4041
- return agents.map((agent) => {
4042
- const agentBalance = aggregated[agent.id];
4043
- const tokenBalance = agentBalance?.tokens.find(
4044
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
4045
- );
4046
- let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
4047
- if (agent.balanceComposition === "tokens-plus-positions") {
4048
- const chainNameById = {
4049
- 1: "ETHEREUM",
4050
- 8453: "BASE",
4051
- 42161: "ARBITRUM"
4052
- };
4053
- const targetChain = chainNameById[chainId];
4054
- for (const position2 of agentBalance?.positions ?? []) {
4055
- const positionChain = position2.chain.trim().toUpperCase();
4056
- const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
4057
- if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
4058
- if (position2.amountRaw !== void 0) {
4059
- try {
4060
- balance += BigInt(position2.amountRaw);
4061
- continue;
4062
- } catch {
4063
- }
4064
- }
4065
- balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
4066
- }
4067
- }
4068
- return { agent, balance };
4069
- });
4070
- }
4071
- function planProportionalShares(balances, requested, totalAvailable) {
4072
- const plans = balances.map(({ agent, balance }) => ({
4073
- agent,
4074
- balance,
4075
- planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
4076
- }));
4077
- const assigned = plans.reduce((s, p) => s + p.planned, 0n);
4078
- let remainder = requested - assigned;
4079
- const byHeadroom = [...plans].sort((a, b) => {
4080
- const diff = b.balance - b.planned - (a.balance - a.planned);
4081
- return diff > 0n ? 1 : diff < 0n ? -1 : 0;
2487
+ async function readErc20Balance(publicClient, token, owner) {
2488
+ return publicClient.readContract({
2489
+ address: token,
2490
+ abi: ERC20_ALLOWANCE_ABI,
2491
+ functionName: "balanceOf",
2492
+ args: [owner]
4082
2493
  });
4083
- for (const p of byHeadroom) {
4084
- if (remainder === 0n) break;
4085
- const headroom = p.balance - p.planned;
4086
- if (headroom <= 0n) continue;
4087
- const take = headroom < remainder ? headroom : remainder;
4088
- p.planned += take;
4089
- remainder -= take;
4090
- }
4091
- return plans;
4092
2494
  }
4093
- function planDisabledDrain(disabled, requested) {
4094
- const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
4095
- (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
4096
- );
4097
- const plans = [];
4098
- let remaining = requested;
4099
- for (const { agent, balance } of sorted) {
4100
- if (remaining === 0n) {
4101
- plans.push({ agent, balance, planned: 0n });
4102
- continue;
4103
- }
4104
- const take = balance < remaining ? balance : remaining;
4105
- plans.push({ agent, balance, planned: take });
4106
- remaining -= take;
4107
- }
4108
- return { plans, remaining };
2495
+
2496
+ // src/lib/chain-guard.ts
2497
+ var CHAIN_NAMES = {
2498
+ 1: "Ethereum",
2499
+ 8453: "Base",
2500
+ 42161: "Arbitrum"
2501
+ };
2502
+ function chainName(chainId) {
2503
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
4109
2504
  }
4110
- function redistributeShare(plans, fromIndex, amount, candidatePool) {
4111
- const pool = candidatePool ?? plans.slice(fromIndex + 1);
4112
- const candidates = pool.filter((c) => c.balance - c.planned > 0n);
4113
- const totalHeadroom = candidates.reduce(
4114
- (s, c) => s + (c.balance - c.planned),
4115
- 0n
4116
- );
4117
- if (totalHeadroom === 0n) return;
4118
- let distributed = 0n;
4119
- for (const c of candidates) {
4120
- const headroom = c.balance - c.planned;
4121
- const proportional = headroom * amount / totalHeadroom;
4122
- const give = proportional > headroom ? headroom : proportional;
4123
- c.planned += give;
4124
- distributed += give;
2505
+ async function ensureWalletOnChain(pub, wallet, expected) {
2506
+ const actual = await pub.getChainId();
2507
+ if (actual === expected) return;
2508
+ try {
2509
+ await wallet.switchChain({ id: expected });
2510
+ } catch (error) {
2511
+ throw new OwneyError(
2512
+ "CHAIN_MISMATCH",
2513
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2514
+ {
2515
+ expectedChainId: expected,
2516
+ actualChainId: actual,
2517
+ cause: error instanceof Error ? error.message : String(error)
2518
+ }
2519
+ );
4125
2520
  }
4126
- let leftover = amount - distributed;
4127
- for (const c of candidates) {
4128
- if (leftover === 0n) break;
4129
- const headroom = c.balance - c.planned;
4130
- if (headroom <= 0n) continue;
4131
- const take = headroom < leftover ? headroom : leftover;
4132
- c.planned += take;
4133
- leftover -= take;
2521
+ const after = await pub.getChainId();
2522
+ if (after !== expected) {
2523
+ throw new OwneyError(
2524
+ "CHAIN_MISMATCH",
2525
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2526
+ { expectedChainId: expected, actualChainId: after }
2527
+ );
4134
2528
  }
4135
2529
  }
4136
- function sumWithdrawnAmount(results) {
4137
- return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
4138
- }
4139
2530
 
4140
- // src/lib/helpers/account-apy-helper.ts
4141
- function balanceForApyScope(balance, chainId, tokenSymbol) {
4142
- if (!tokenSymbol) {
4143
- const total = Number(balance.totalBalance);
4144
- return Number.isFinite(total) && total > 0 ? total : 0;
4145
- }
4146
- const normalizedToken = tokenSymbol.toUpperCase();
4147
- return balance.tokens.reduce((total, token) => {
4148
- if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
4149
- return total;
4150
- }
4151
- const amount = Number(token.amount);
4152
- return Number.isFinite(amount) && amount > 0 ? total + amount : total;
4153
- }, 0);
4154
- }
4155
- function aggregateApyHistory(agentApys, agentBalances) {
4156
- const byDate = /* @__PURE__ */ new Map();
4157
- for (const [id, accountApy] of Object.entries(agentApys)) {
4158
- const balance = agentBalances[id] ?? 0;
4159
- if (!Number.isFinite(balance) || balance <= 0) continue;
4160
- for (const point of accountApy.history ?? []) {
4161
- const apy = Number(point.apy);
4162
- if (!point.date || !Number.isFinite(apy)) continue;
4163
- const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
4164
- current.weightedSum += apy * balance;
4165
- current.weight += balance;
4166
- byDate.set(point.date, current);
2531
+ // src/lib/sponsored-deposit.ts
2532
+ var AUTH_WINDOW_SECONDS = 15 * 60;
2533
+ function makeSponsoredDepositCallback(deps) {
2534
+ const post = deps.httpPost ?? postSponsorTransferAuth;
2535
+ return async (smartWallet, chainId, amount) => {
2536
+ const cid = chainId;
2537
+ const token = deps.tokenAddressByChain[cid];
2538
+ if (!token) {
2539
+ throw new OwneyError(
2540
+ "CHAIN_UNSUPPORTED",
2541
+ `No sponsored token configured for chain ${chainId}`
2542
+ );
4167
2543
  }
4168
- }
4169
- return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
4170
- date,
4171
- apy: value.weightedSum / value.weight
4172
- }));
4173
- }
4174
- function aggregateApyByChainAndAsset(agentApys, agentBalances) {
4175
- const sums = {};
4176
- const weights = {};
4177
- for (const id of Object.keys(agentApys)) {
4178
- const cells = agentApys[id].apyByChainAndAsset;
4179
- const balance = agentBalances[id] ?? 0;
4180
- if (!cells || balance <= 0) continue;
4181
- for (const [chainKey, perAsset] of Object.entries(cells)) {
4182
- if (!perAsset) continue;
4183
- const chainId = Number(chainKey);
4184
- for (const [asset, apyValue] of Object.entries(perAsset)) {
4185
- const apy = Number(apyValue ?? 0);
4186
- if (apy === 0) continue;
4187
- sums[chainId] ??= {};
4188
- weights[chainId] ??= {};
4189
- sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
4190
- weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2544
+ const pub = deps.getPublicClient(cid);
2545
+ const wallet = deps.getWalletClient(cid);
2546
+ await ensureWalletOnChain(pub, wallet, cid);
2547
+ try {
2548
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2549
+ if (balance < BigInt(amount)) {
2550
+ throw new OwneyError(
2551
+ "DEPOSIT_INSUFFICIENT_BALANCE",
2552
+ "Insufficient balance for this deposit.",
2553
+ { token, chainId: cid, balance: balance.toString(), amount }
2554
+ );
4191
2555
  }
2556
+ } catch (err) {
2557
+ if (err instanceof OwneyError) throw err;
2558
+ console.warn(
2559
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2560
+ err instanceof Error ? err.message : String(err)
2561
+ );
4192
2562
  }
4193
- }
4194
- const out = {};
4195
- for (const chainKey of Object.keys(sums)) {
4196
- const chainId = Number(chainKey);
4197
- const perAssetOut = {};
4198
- for (const asset of Object.keys(sums[chainId])) {
4199
- const w = weights[chainId][asset];
4200
- if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
4201
- }
4202
- if (Object.keys(perAssetOut).length > 0) {
4203
- out[chainId] = perAssetOut;
4204
- }
4205
- }
4206
- return out;
2563
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2564
+ const validAfter = 0n;
2565
+ const validBefore = BigInt(
2566
+ Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2567
+ );
2568
+ const nonce = randomAuthNonce();
2569
+ const typedData = buildTransferWithAuthorizationTypedData({
2570
+ token,
2571
+ chainId: cid,
2572
+ tokenName,
2573
+ tokenVersion,
2574
+ message: {
2575
+ from: deps.ownerAddress,
2576
+ to: smartWallet,
2577
+ value: BigInt(amount),
2578
+ validAfter,
2579
+ validBefore,
2580
+ nonce
2581
+ }
2582
+ });
2583
+ const authSignature = await wallet.signTypedData({
2584
+ account: deps.ownerAddress,
2585
+ ...typedData
2586
+ });
2587
+ deps.onApproved?.();
2588
+ const result = await post({
2589
+ baseUrl: deps.baseUrl,
2590
+ apiKey: deps.apiKey,
2591
+ body: {
2592
+ chainId: cid,
2593
+ token,
2594
+ from: deps.ownerAddress,
2595
+ to: smartWallet,
2596
+ value: amount,
2597
+ validAfter: validAfter.toString(),
2598
+ validBefore: validBefore.toString(),
2599
+ nonce,
2600
+ authSignature,
2601
+ tokenName,
2602
+ tokenVersion
2603
+ }
2604
+ });
2605
+ return result.txHash;
2606
+ };
4207
2607
  }
4208
2608
 
4209
- // src/client.ts
4210
- var import_viem9 = require("viem");
4211
- var import_chains4 = require("viem/chains");
4212
-
4213
2609
  // src/lib/sponsored-weth-deposit.ts
4214
2610
  var PERMIT_WINDOW_SECONDS = 15 * 60;
4215
2611
  function makeSponsoredWethCallback(deps) {
4216
2612
  const get = deps.httpGet ?? getSponsorRelayerAddress;
4217
2613
  const post = deps.httpPost ?? postSponsorPermit2Transfer;
4218
- return makeVerificationAwareDepositCallback(
4219
- async (smartWallet, chainId, amount, verification) => {
4220
- const cid = chainId;
4221
- const token = deps.tokenAddressByChain[cid];
4222
- if (!token) {
4223
- throw new OwneyError(
4224
- "CHAIN_UNSUPPORTED",
4225
- `No sponsored WETH configured for chain ${chainId}`
4226
- );
4227
- }
4228
- const amountWei = BigInt(amount);
4229
- const pub = deps.getPublicClient(cid);
4230
- const wallet = deps.getWalletClient(cid);
4231
- await ensureWalletOnChain(pub, wallet, cid);
4232
- try {
4233
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
4234
- if (balance < amountWei) {
4235
- throw new OwneyError(
4236
- "DEPOSIT_INSUFFICIENT_BALANCE",
4237
- "Insufficient WETH balance for this deposit.",
4238
- { token, chainId: cid, balance: balance.toString(), amount }
4239
- );
4240
- }
4241
- } catch (err) {
4242
- if (err instanceof OwneyError) throw err;
4243
- console.warn(
4244
- "[owney-sdk] WETH balance pre-check failed (non-fatal):",
4245
- err instanceof Error ? err.message : String(err)
4246
- );
4247
- }
4248
- const allowance = await readPermit2Allowance(
4249
- pub,
4250
- token,
4251
- deps.ownerAddress
2614
+ return async (smartWallet, chainId, amount) => {
2615
+ const cid = chainId;
2616
+ const token = deps.tokenAddressByChain[cid];
2617
+ if (!token) {
2618
+ throw new OwneyError(
2619
+ "CHAIN_UNSUPPORTED",
2620
+ `No sponsored WETH configured for chain ${chainId}`
4252
2621
  );
4253
- if (allowance < amountWei) {
2622
+ }
2623
+ const amountWei = BigInt(amount);
2624
+ const pub = deps.getPublicClient(cid);
2625
+ const wallet = deps.getWalletClient(cid);
2626
+ await ensureWalletOnChain(pub, wallet, cid);
2627
+ try {
2628
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2629
+ if (balance < amountWei) {
4254
2630
  throw new OwneyError(
4255
- "PERMIT2_APPROVAL_REQUIRED",
4256
- "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
4257
- { token, chainId: cid, allowance: allowance.toString(), amount }
2631
+ "DEPOSIT_INSUFFICIENT_BALANCE",
2632
+ "Insufficient WETH balance for this deposit.",
2633
+ { token, chainId: cid, balance: balance.toString(), amount }
4258
2634
  );
4259
2635
  }
4260
- const relayer = await get({
4261
- baseUrl: deps.baseUrl,
4262
- apiKey: deps.apiKey,
4263
- chainId: cid
4264
- });
4265
- const nonce = randomPermit2Nonce();
4266
- const deadline = BigInt(
4267
- Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
2636
+ } catch (err) {
2637
+ if (err instanceof OwneyError) throw err;
2638
+ console.warn(
2639
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2640
+ err instanceof Error ? err.message : String(err)
4268
2641
  );
4269
- const typedData = buildPermitTransferFromTypedData({
4270
- chainId: cid,
4271
- message: {
4272
- permitted: { token, amount: amountWei },
4273
- spender: relayer,
4274
- nonce,
4275
- deadline
4276
- }
4277
- });
4278
- const signature = await wallet.signTypedData({
4279
- account: deps.ownerAddress,
4280
- ...typedData
4281
- });
4282
- deps.onApproved?.();
4283
- const result = await post({
4284
- baseUrl: deps.baseUrl,
4285
- apiKey: deps.apiKey,
4286
- ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
4287
- body: {
4288
- chainId: cid,
4289
- token,
4290
- from: deps.ownerAddress,
4291
- to: smartWallet,
4292
- amount,
4293
- nonce: nonce.toString(),
4294
- deadline: deadline.toString(),
4295
- signature,
4296
- ...verification?.agentId === "yieldseeker" ? {
4297
- yieldseekerUserId: verification.userId,
4298
- yieldseekerAgentId: verification.yieldseekerAgentId
4299
- } : {}
4300
- }
4301
- });
4302
- return result.txHash;
4303
2642
  }
4304
- );
2643
+ const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2644
+ if (allowance < amountWei) {
2645
+ throw new OwneyError(
2646
+ "PERMIT2_APPROVAL_REQUIRED",
2647
+ "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
2648
+ { token, chainId: cid, allowance: allowance.toString(), amount }
2649
+ );
2650
+ }
2651
+ const relayer = await get({
2652
+ baseUrl: deps.baseUrl,
2653
+ apiKey: deps.apiKey,
2654
+ chainId: cid
2655
+ });
2656
+ const nonce = randomPermit2Nonce();
2657
+ const deadline = BigInt(
2658
+ Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
2659
+ );
2660
+ const typedData = buildPermitTransferFromTypedData({
2661
+ chainId: cid,
2662
+ message: {
2663
+ permitted: { token, amount: amountWei },
2664
+ spender: relayer,
2665
+ nonce,
2666
+ deadline
2667
+ }
2668
+ });
2669
+ const signature = await wallet.signTypedData({
2670
+ account: deps.ownerAddress,
2671
+ ...typedData
2672
+ });
2673
+ deps.onApproved?.();
2674
+ const result = await post({
2675
+ baseUrl: deps.baseUrl,
2676
+ apiKey: deps.apiKey,
2677
+ body: {
2678
+ chainId: cid,
2679
+ token,
2680
+ from: deps.ownerAddress,
2681
+ to: smartWallet,
2682
+ amount,
2683
+ nonce: nonce.toString(),
2684
+ deadline: deadline.toString(),
2685
+ signature
2686
+ }
2687
+ });
2688
+ return result.txHash;
2689
+ };
4305
2690
  }
4306
2691
 
4307
2692
  // src/lib/sponsored-calls-deposit.ts
4308
- var import_viem8 = require("viem");
2693
+ var import_viem5 = require("viem");
4309
2694
  var DEFAULT_POLL_INTERVAL_MS = 1500;
4310
2695
  var DEFAULT_MAX_POLLS = 30;
4311
2696
  async function paymasterSupported(provider, owner, chainId) {
@@ -4313,7 +2698,7 @@ async function paymasterSupported(provider, owner, chainId) {
4313
2698
  method: "wallet_getCapabilities",
4314
2699
  params: [owner]
4315
2700
  });
4316
- const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
2701
+ const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
4317
2702
  return Boolean(forChain?.paymasterService?.supported);
4318
2703
  }
4319
2704
  function makeSponsoredCallsCallback(deps) {
@@ -4331,7 +2716,7 @@ function makeSponsoredCallsCallback(deps) {
4331
2716
  }
4332
2717
  return new URL(configured, origin).toString();
4333
2718
  };
4334
- return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
2719
+ return async (smartWallet, chainId, amount) => {
4335
2720
  const cid = chainId;
4336
2721
  const token = deps.tokenAddressByChain[cid];
4337
2722
  if (!token) {
@@ -4347,48 +2732,22 @@ function makeSponsoredCallsCallback(deps) {
4347
2732
  { chainId }
4348
2733
  );
4349
2734
  }
4350
- const data = (0, import_viem8.encodeFunctionData)({
4351
- abi: import_viem8.erc20Abi,
2735
+ const data = (0, import_viem5.encodeFunctionData)({
2736
+ abi: import_viem5.erc20Abi,
4352
2737
  functionName: "transfer",
4353
2738
  args: [smartWallet, BigInt(amount)]
4354
2739
  });
4355
- let paymasterUrl = absolutePaymasterUrl();
4356
- if (verification?.agentId === "yieldseeker") {
4357
- if (chainId !== 8453) {
4358
- throw new OwneyError(
4359
- "CHAIN_UNSUPPORTED",
4360
- `Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
4361
- );
4362
- }
4363
- const { intent } = await postPaymasterIntent({
4364
- baseUrl: deps.routingApiBaseUrl,
4365
- apiKey: deps.apiKey,
4366
- yieldseekerSignature: verification.signature,
4367
- body: {
4368
- chainId,
4369
- token,
4370
- from: deps.ownerAddress,
4371
- to: smartWallet,
4372
- amount,
4373
- yieldseekerUserId: verification.userId,
4374
- yieldseekerAgentId: verification.yieldseekerAgentId
4375
- }
4376
- });
4377
- const url = new URL(paymasterUrl);
4378
- url.searchParams.set("owneyIntent", intent);
4379
- paymasterUrl = url.toString();
4380
- }
4381
2740
  const sendResult = await deps.provider.request({
4382
2741
  method: "wallet_sendCalls",
4383
2742
  params: [
4384
2743
  {
4385
2744
  version: "2.0.0",
4386
2745
  from: deps.ownerAddress,
4387
- chainId: (0, import_viem8.toHex)(chainId),
2746
+ chainId: (0, import_viem5.toHex)(chainId),
4388
2747
  atomicRequired: false,
4389
2748
  calls: [{ to: token, value: "0x0", data }],
4390
2749
  capabilities: {
4391
- paymasterService: { url: paymasterUrl }
2750
+ paymasterService: { url: absolutePaymasterUrl() }
4392
2751
  }
4393
2752
  }
4394
2753
  ]
@@ -4418,7 +2777,7 @@ function makeSponsoredCallsCallback(deps) {
4418
2777
  `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
4419
2778
  { chainId, callsId }
4420
2779
  );
4421
- });
2780
+ };
4422
2781
  }
4423
2782
 
4424
2783
  // src/client.ts
@@ -4448,9 +2807,9 @@ var SPONSORED_USDC_BY_CHAIN = {
4448
2807
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
4449
2808
  };
4450
2809
  var VIEM_CHAIN2 = {
4451
- 8453: import_chains4.base,
4452
- 42161: import_chains4.arbitrum,
4453
- 1: import_chains4.mainnet
2810
+ 8453: import_chains2.base,
2811
+ 42161: import_chains2.arbitrum,
2812
+ 1: import_chains2.mainnet
4454
2813
  };
4455
2814
  var SPONSORED_WETH_BY_CHAIN = {
4456
2815
  8453: "0x4200000000000000000000000000000000000006",
@@ -4478,8 +2837,6 @@ var OwneySDK = class {
4478
2837
  orgAgentConfig;
4479
2838
  orgAgentConfigPromise = null;
4480
2839
  zyfaiRpcUrls;
4481
- yieldseekerApiBaseUrl;
4482
- yieldseekerSiweOrigin;
4483
2840
  routingApiBaseUrl;
4484
2841
  referralSource;
4485
2842
  cachedSponsoredCallback = null;
@@ -4502,8 +2859,6 @@ var OwneySDK = class {
4502
2859
  this.apiKey = config.apiKey;
4503
2860
  if (config.debug) setOwneyDebug(true);
4504
2861
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
4505
- this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
4506
- this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
4507
2862
  this.routingApiBaseUrl = config.routingApiBaseUrl;
4508
2863
  this.paymasterServiceUrl = config.paymasterServiceUrl;
4509
2864
  this.referralSource = config.referralSource;
@@ -4610,14 +2965,14 @@ var OwneySDK = class {
4610
2965
  // Casts work around viem's chain-narrowed Client vs the generic
4611
2966
  // PublicClient/WalletClient param types — structurally identical at
4612
2967
  // runtime, but the two share a name TS treats as unrelated.
4613
- getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
2968
+ getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
4614
2969
  chain: VIEM_CHAIN2[cid],
4615
- transport: (0, import_viem9.custom)(provider)
2970
+ transport: (0, import_viem6.custom)(provider)
4616
2971
  }),
4617
- getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
2972
+ getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
4618
2973
  account: owner,
4619
2974
  chain: VIEM_CHAIN2[cid],
4620
- transport: (0, import_viem9.custom)(provider)
2975
+ transport: (0, import_viem6.custom)(provider)
4621
2976
  })
4622
2977
  });
4623
2978
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -4634,8 +2989,6 @@ var OwneySDK = class {
4634
2989
  if (!onApproved && cached) return cached;
4635
2990
  const provider = this.requireConnectedProvider();
4636
2991
  const callback = makeSponsoredCallsCallback({
4637
- apiKey: this.apiKey,
4638
- routingApiBaseUrl: this.routingApiBaseUrl,
4639
2992
  provider,
4640
2993
  ownerAddress: this.state.walletAddress,
4641
2994
  paymasterServiceUrl: this.paymasterServiceUrl,
@@ -4665,14 +3018,14 @@ var OwneySDK = class {
4665
3018
  // Casts work around viem's chain-narrowed Client vs the generic
4666
3019
  // PublicClient/WalletClient param types — structurally identical at
4667
3020
  // runtime, but the two share a name TS treats as unrelated.
4668
- getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
3021
+ getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
4669
3022
  chain: VIEM_CHAIN2[cid],
4670
- transport: (0, import_viem9.custom)(provider)
3023
+ transport: (0, import_viem6.custom)(provider)
4671
3024
  }),
4672
- getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
3025
+ getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
4673
3026
  account: owner,
4674
3027
  chain: VIEM_CHAIN2[cid],
4675
- transport: (0, import_viem9.custom)(provider)
3028
+ transport: (0, import_viem6.custom)(provider)
4676
3029
  })
4677
3030
  });
4678
3031
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -4703,10 +3056,12 @@ var OwneySDK = class {
4703
3056
  this.orgAgentConfigPromise = fetchOrgAgentConfig(
4704
3057
  this.apiKey,
4705
3058
  this.routingApiBaseUrl
4706
- ).then((config) => {
4707
- this.orgAgentConfig = config;
4708
- return config;
4709
- });
3059
+ ).then(
3060
+ (config) => {
3061
+ this.orgAgentConfig = config;
3062
+ return config;
3063
+ }
3064
+ );
4710
3065
  }
4711
3066
  return this.orgAgentConfigPromise;
4712
3067
  }
@@ -4746,14 +3101,7 @@ var OwneySDK = class {
4746
3101
  this.routingApiBaseUrl
4747
3102
  );
4748
3103
  this.disabledAgents.clear();
4749
- for (const {
4750
- key: key2,
4751
- agent_type,
4752
- is_enabled,
4753
- is_configured
4754
- } of agentKeys) {
4755
- const configured = is_configured ?? Boolean(key2);
4756
- if (!configured) continue;
3104
+ for (const { key: key2, agent_type, is_enabled } of agentKeys) {
4757
3105
  const agent = this.createAgent(agent_type, key2);
4758
3106
  if (!agent) continue;
4759
3107
  this.agents.set(agent_type, agent);
@@ -4777,15 +3125,8 @@ var OwneySDK = class {
4777
3125
  }
4778
3126
  createAgent(agentId, key2) {
4779
3127
  if (agentId === "zyfai") {
4780
- if (!key2) return null;
4781
3128
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
4782
3129
  }
4783
- if (agentId === "yieldseeker") {
4784
- return new YieldseekerAgent(this.apiKey, {
4785
- auth: { origin: this.yieldseekerSiweOrigin },
4786
- baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
4787
- });
4788
- }
4789
3130
  return null;
4790
3131
  }
4791
3132
  /**
@@ -4830,7 +3171,7 @@ var OwneySDK = class {
4830
3171
  * If provided, ALL specified agents must support the chainId or the call
4831
3172
  * throws before activating any agent.
4832
3173
  */
4833
- async activateAgent(chainId, agentId, asset) {
3174
+ async activateAgent(chainId, agentId) {
4834
3175
  const state = this.requireState();
4835
3176
  await this.ensureAgentsInitialized();
4836
3177
  if (agentId !== void 0) {
@@ -4866,7 +3207,7 @@ var OwneySDK = class {
4866
3207
  this.activeAgents.add(id);
4867
3208
  }
4868
3209
  state.chainId = chainId;
4869
- await this.activateAgentsInTurn(agents, state, chainId, asset);
3210
+ await this.activateAgentsInTurn(agents, state, chainId);
4870
3211
  return;
4871
3212
  }
4872
3213
  const compatible = [...this.agents.values()].filter(
@@ -4887,7 +3228,7 @@ var OwneySDK = class {
4887
3228
  const enabledCompatible = compatible.filter(
4888
3229
  (agent) => !this.isAgentDisabled(agent.id)
4889
3230
  );
4890
- await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
3231
+ await this.activateAgentsInTurn(enabledCompatible, state, chainId);
4891
3232
  }
4892
3233
  /**
4893
3234
  * Activate agents ONE AT A TIME, each followed by its org policy.
@@ -4903,25 +3244,17 @@ var OwneySDK = class {
4903
3244
  * at a time anyway.
4904
3245
  *
4905
3246
  * Every agent is attempted even if an earlier one fails, so one declined
4906
- * signature can't deny the remaining agents their turn. Once all agents have
4907
- * had a chance, a partial failure identifies the agents that still need a
4908
- * retry; if none activated, the original provider error is preserved.
3247
+ * signature can't deny the remaining agents their turn. The first failure is
3248
+ * rethrown (matching the previous `Promise.all` rejection) once all agents
3249
+ * have had a chance to activate.
4909
3250
  */
4910
- async activateAgentsInTurn(agents, state, chainId, asset) {
3251
+ async activateAgentsInTurn(agents, state, chainId) {
4911
3252
  let firstError = null;
4912
- const activatedAgentIds = [];
4913
- const failedAgents = [];
4914
3253
  for (const agent of agents) {
4915
3254
  try {
4916
- await agent.activateAgent(state, chainId, asset);
3255
+ await agent.activateAgent(state, chainId);
4917
3256
  await this.applyOrgPolicyTo(agent, state, chainId);
4918
- activatedAgentIds.push(agent.id);
4919
3257
  } catch (error) {
4920
- failedAgents.push({
4921
- agentId: agent.id,
4922
- code: error instanceof OwneyError ? error.code : void 0,
4923
- message: error instanceof Error ? error.message : String(error)
4924
- });
4925
3258
  if (firstError === null) {
4926
3259
  firstError = error;
4927
3260
  } else {
@@ -4929,16 +3262,7 @@ var OwneySDK = class {
4929
3262
  }
4930
3263
  }
4931
3264
  }
4932
- if (firstError === null) return;
4933
- if (activatedAgentIds.length === 0) throw firstError;
4934
- const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
4935
- const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
4936
- const remainingNames = failedAgentIds.map(this.formatAgentName).join(", ");
4937
- throw new OwneyError(
4938
- "AGENT_ACTIVATION_PARTIAL_FAILURE",
4939
- `${activeNames} activated, but ${remainingNames} still needs activation. Try again and approve the remaining wallet request.`,
4940
- { activatedAgentIds, failedAgentIds, failures: failedAgents }
4941
- );
3265
+ if (firstError !== null) throw firstError;
4942
3266
  }
4943
3267
  /**
4944
3268
  * Deposit into a specific agent, or distribute across all eligible agents.
@@ -5152,10 +3476,10 @@ var OwneySDK = class {
5152
3476
  agent,
5153
3477
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
5154
3478
  }));
5155
- const valid2 = splits.filter(
3479
+ const valid = splits.filter(
5156
3480
  (s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
5157
3481
  );
5158
- if (valid2.length === agents.length) {
3482
+ if (valid.length === agents.length) {
5159
3483
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
5160
3484
  }
5161
3485
  const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
@@ -5174,11 +3498,6 @@ var OwneySDK = class {
5174
3498
  )
5175
3499
  }));
5176
3500
  }
5177
- formatAgentName(agentId) {
5178
- if (agentId === "zyfai") return "Zyfai";
5179
- if (agentId === "yieldseeker") return "Yieldseeker";
5180
- return agentId;
5181
- }
5182
3501
  async validateMinDepositAmount(agent, state, chainId, asset, amount) {
5183
3502
  const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
5184
3503
  const parsedAmount = BigInt(amount);
@@ -5210,12 +3529,12 @@ var OwneySDK = class {
5210
3529
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
5211
3530
  );
5212
3531
  const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
5213
- const position2 = (balance.positions ?? []).find((p) => {
3532
+ const position = (balance.positions ?? []).find((p) => {
5214
3533
  const positionChain = p.chain.trim().toUpperCase();
5215
3534
  const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
5216
3535
  return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
5217
3536
  });
5218
- return !!token && Number(token.amount) > 0 || !!position2;
3537
+ return !!token && Number(token.amount) > 0 || !!position;
5219
3538
  } catch (error) {
5220
3539
  if (requireReliableRead) {
5221
3540
  throw new OwneyError(
@@ -5330,10 +3649,6 @@ var OwneySDK = class {
5330
3649
  }
5331
3650
  const requested = BigInt(amount);
5332
3651
  const aggregated = await this.getBalances();
5333
- const unavailableAgents = eligibleAgents.filter(
5334
- (agent) => !(agent.id in aggregated.agentBalances)
5335
- );
5336
- const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
5337
3652
  const balances = projectAgentBalancesForAsset(
5338
3653
  eligibleAgents,
5339
3654
  aggregated.agentBalances,
@@ -5342,18 +3657,7 @@ var OwneySDK = class {
5342
3657
  assetInfo.decimals
5343
3658
  );
5344
3659
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
5345
- if (totalAvailable === 0n && unavailableAgents.length > 0) {
5346
- throw new OwneyError(
5347
- "WITHDRAW_BALANCE_UNAVAILABLE",
5348
- `Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
5349
- {
5350
- asset,
5351
- unavailableAgents: unavailableAgentIds,
5352
- agentErrors: aggregated.agentErrors
5353
- }
5354
- );
5355
- }
5356
- if (totalAvailable < requested && unavailableAgents.length === 0) {
3660
+ if (totalAvailable < requested) {
5357
3661
  throw new OwneyError(
5358
3662
  "WITHDRAW_INSUFFICIENT_BALANCE",
5359
3663
  `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
@@ -5364,7 +3668,6 @@ var OwneySDK = class {
5364
3668
  }
5365
3669
  );
5366
3670
  }
5367
- const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
5368
3671
  const disabledBalances = balances.filter(
5369
3672
  (b) => this.isAgentDisabled(b.agent.id)
5370
3673
  );
@@ -5373,7 +3676,7 @@ var OwneySDK = class {
5373
3676
  );
5374
3677
  const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
5375
3678
  disabledBalances,
5376
- plannedTarget
3679
+ requested
5377
3680
  );
5378
3681
  const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
5379
3682
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
@@ -5383,9 +3686,7 @@ var OwneySDK = class {
5383
3686
  }));
5384
3687
  const plans = [...disabledPlans, ...enabledPlans];
5385
3688
  const results = {};
5386
- const agentErrors = {
5387
- ...aggregated.agentErrors ?? {}
5388
- };
3689
+ const agentErrors = {};
5389
3690
  for (let i = 0; i < plans.length; i++) {
5390
3691
  const p = plans[i];
5391
3692
  if (p.planned === 0n) continue;
@@ -5432,8 +3733,7 @@ var OwneySDK = class {
5432
3733
  requested: amount,
5433
3734
  withdrawn: withdrawn.toString(),
5434
3735
  partialResults: results,
5435
- agentErrors,
5436
- ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
3736
+ agentErrors
5437
3737
  }
5438
3738
  );
5439
3739
  }
@@ -5451,10 +3751,7 @@ var OwneySDK = class {
5451
3751
  if (agentId) {
5452
3752
  const agent = this.getAgent(agentId);
5453
3753
  const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5454
- return {
5455
- ...result,
5456
- balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
5457
- };
3754
+ return result;
5458
3755
  }
5459
3756
  let totalBalance = 0;
5460
3757
  const results = {};
@@ -5462,13 +3759,7 @@ var OwneySDK = class {
5462
3759
  const balanceResults = await Promise.allSettled(
5463
3760
  entries.map(async ([id, agent]) => {
5464
3761
  const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5465
- return [
5466
- id,
5467
- {
5468
- ...b,
5469
- balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
5470
- }
5471
- ];
3762
+ return [id, b];
5472
3763
  })
5473
3764
  );
5474
3765
  let successCount = 0;
@@ -5490,7 +3781,6 @@ var OwneySDK = class {
5490
3781
  const retryDelay = rateLimitDelay(reason);
5491
3782
  if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
5492
3783
  agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
5493
- console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
5494
3784
  }
5495
3785
  if (successCount === 0) {
5496
3786
  throw new OwneyError(
@@ -5603,10 +3893,7 @@ var OwneySDK = class {
5603
3893
  Promise.all(
5604
3894
  entries.map(async ([id, agent]) => {
5605
3895
  const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5606
- return [
5607
- id,
5608
- balanceForApyScope(b, chainId, tokenSymbol)
5609
- ];
3896
+ return [id, Number(b.totalBalance)];
5610
3897
  })
5611
3898
  )
5612
3899
  ]);
@@ -5634,12 +3921,10 @@ var OwneySDK = class {
5634
3921
  }
5635
3922
  }
5636
3923
  const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
5637
- const history = aggregateApyHistory(results, balances);
5638
3924
  return {
5639
3925
  totalApy: String(totalApy),
5640
3926
  agentApy: results,
5641
- apyByChainAndAsset,
5642
- history
3927
+ apyByChainAndAsset
5643
3928
  };
5644
3929
  }
5645
3930
  /**
@@ -5787,10 +4072,10 @@ var OwneySDK = class {
5787
4072
  );
5788
4073
  }
5789
4074
  const provider = this.requireConnectedProvider();
5790
- const wallet = (0, import_viem9.createWalletClient)({
4075
+ const wallet = (0, import_viem6.createWalletClient)({
5791
4076
  account: state.walletAddress,
5792
4077
  chain: VIEM_CHAIN2[chainId],
5793
- transport: (0, import_viem9.custom)(provider)
4078
+ transport: (0, import_viem6.custom)(provider)
5794
4079
  });
5795
4080
  const hash = await wallet.writeContract({
5796
4081
  address: token,
@@ -5800,9 +4085,9 @@ var OwneySDK = class {
5800
4085
  account: state.walletAddress,
5801
4086
  chain: VIEM_CHAIN2[chainId]
5802
4087
  });
5803
- const publicClient = (0, import_viem9.createPublicClient)({
4088
+ const publicClient = (0, import_viem6.createPublicClient)({
5804
4089
  chain: VIEM_CHAIN2[chainId],
5805
- transport: (0, import_viem9.custom)(provider)
4090
+ transport: (0, import_viem6.custom)(provider)
5806
4091
  });
5807
4092
  const receipt = await publicClient.waitForTransactionReceipt({
5808
4093
  hash,
@@ -5839,9 +4124,7 @@ var OwneySDK = class {
5839
4124
  return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
5840
4125
  }
5841
4126
  const results = {};
5842
- const agentEntries = [...this.agents.entries()].filter(
5843
- ([id]) => !this.isAgentDisabled(id)
5844
- );
4127
+ const agentEntries = [...this.agents.entries()];
5845
4128
  const apyResults = await Promise.all(
5846
4129
  agentEntries.map(async ([id, agent]) => {
5847
4130
  const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
@@ -5916,13 +4199,13 @@ var OwneySDK = class {
5916
4199
  };
5917
4200
 
5918
4201
  // src/agents/zyfai/zyfai.siwx.ts
5919
- var import_viem10 = require("viem");
5920
- var import_siwe2 = require("siwe");
4202
+ var import_viem7 = require("viem");
4203
+ var import_siwe = require("siwe");
5921
4204
  var import_sdk2 = require("@zyfai/sdk");
5922
4205
 
5923
4206
  // src/agents/zyfai/zyfai.siwx-cache.ts
5924
- var KEY_PREFIX4 = "owney.siwx.session";
5925
- var storage4 = () => {
4207
+ var KEY_PREFIX2 = "owney.siwx.session";
4208
+ var storage2 = () => {
5926
4209
  if (typeof window === "undefined") return null;
5927
4210
  try {
5928
4211
  return window.localStorage;
@@ -5930,8 +4213,8 @@ var storage4 = () => {
5930
4213
  return null;
5931
4214
  }
5932
4215
  };
5933
- var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
5934
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
4216
+ var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
4217
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
5935
4218
  var memorySiwxSessions = /* @__PURE__ */ new Map();
5936
4219
  var readLegacySiwxSession = (store, address) => {
5937
4220
  if (!store) return null;
@@ -5962,17 +4245,17 @@ var readLegacySiwxSession = (store, address) => {
5962
4245
  };
5963
4246
  var readSiwxSession = (address, chainId) => {
5964
4247
  if (typeof window === "undefined") return null;
5965
- const key2 = buildKey3(address);
5966
- const store = storage4();
5967
- let raw2 = null;
4248
+ const key2 = buildKey2(address);
4249
+ const store = storage2();
4250
+ let raw = null;
5968
4251
  try {
5969
- raw2 = store?.getItem(key2) ?? null;
4252
+ raw = store?.getItem(key2) ?? null;
5970
4253
  } catch {
5971
- raw2 = null;
4254
+ raw = null;
5972
4255
  }
5973
- if (raw2) {
4256
+ if (raw) {
5974
4257
  try {
5975
- return JSON.parse(raw2);
4258
+ return JSON.parse(raw);
5976
4259
  } catch {
5977
4260
  memorySiwxSessions.delete(key2);
5978
4261
  try {
@@ -5991,18 +4274,18 @@ var readSiwxSession = (address, chainId) => {
5991
4274
  };
5992
4275
  var writeSiwxSession = (address, _chainId, session) => {
5993
4276
  if (typeof window === "undefined") return;
5994
- const key2 = buildKey3(address);
4277
+ const key2 = buildKey2(address);
5995
4278
  memorySiwxSessions.set(key2, session);
5996
- const store = storage4();
4279
+ const store = storage2();
5997
4280
  try {
5998
4281
  store?.setItem(key2, JSON.stringify(session));
5999
4282
  } catch {
6000
4283
  }
6001
4284
  };
6002
4285
  var clearSiwxSession = (address, _chainId) => {
6003
- const key2 = buildKey3(address);
4286
+ const key2 = buildKey2(address);
6004
4287
  memorySiwxSessions.delete(key2);
6005
- const store = storage4();
4288
+ const store = storage2();
6006
4289
  try {
6007
4290
  store?.removeItem(key2);
6008
4291
  } catch {
@@ -6042,8 +4325,8 @@ function buildSIWXConfig(deps) {
6042
4325
  statement: STATEMENT,
6043
4326
  issuedAt,
6044
4327
  toString() {
6045
- return new import_siwe2.SiweMessage({
6046
- address: (0, import_viem10.getAddress)(accountAddress),
4328
+ return new import_siwe.SiweMessage({
4329
+ address: (0, import_viem7.getAddress)(accountAddress),
6047
4330
  chainId: numericChainId(chainId),
6048
4331
  domain,
6049
4332
  uri,
@@ -6085,7 +4368,7 @@ function buildSIWXConfig(deps) {
6085
4368
  const persistSession = async (session) => {
6086
4369
  const address = session.data.accountAddress;
6087
4370
  const id = numericChainId(session.data.chainId);
6088
- const message = new import_siwe2.SiweMessage(session.message);
4371
+ const message = new import_siwe.SiweMessage(session.message);
6089
4372
  const login = await post("/auth/login", {
6090
4373
  message,
6091
4374
  signature: session.signature,
@@ -6094,7 +4377,8 @@ function buildSIWXConfig(deps) {
6094
4377
  writeSession(address, id, {
6095
4378
  token: login.accessToken,
6096
4379
  userId: login.userId ?? null,
6097
- hasActiveSessionKey: Boolean(login.hasActiveSessionKey)
4380
+ hasActiveSessionKey: Boolean(login.hasActiveSessionKey),
4381
+ isPredeployed: login.predeployed
6098
4382
  });
6099
4383
  const stored = {
6100
4384
  data: session.data,
@@ -6134,7 +4418,6 @@ function createOwneySIWX(config) {
6134
4418
  NotConnectedError,
6135
4419
  OwneyError,
6136
4420
  OwneySDK,
6137
- YieldseekerAgent,
6138
4421
  createOwneySIWX,
6139
4422
  setOwneyDebug
6140
4423
  });