@owney/sdk 0.7.17-beta.1 → 0.7.17-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -26,6 +26,7 @@ __export(index_exports, {
26
26
  NotConnectedError: () => NotConnectedError,
27
27
  OwneyError: () => OwneyError,
28
28
  OwneySDK: () => OwneySDK,
29
+ YieldseekerAgent: () => YieldseekerAgent,
29
30
  createOwneySIWX: () => createOwneySIWX,
30
31
  setOwneyDebug: () => setOwneyDebug
31
32
  });
@@ -310,18 +311,18 @@ function tokenDecimals(symbol, explicit) {
310
311
  return explicit;
311
312
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
312
313
  }
313
- function mapDeposit(raw) {
314
+ function mapDeposit(raw2) {
314
315
  return {
315
- txHash: raw.txHash,
316
- smartWallet: raw.smartWallet,
317
- amount: raw.amount
316
+ txHash: raw2.txHash,
317
+ smartWallet: raw2.smartWallet,
318
+ amount: raw2.amount
318
319
  };
319
320
  }
320
- function mapWithdraw(raw) {
321
+ function mapWithdraw(raw2) {
321
322
  return {
322
- txHash: raw.txHash,
323
- type: raw.type,
324
- amount: raw.amount
323
+ txHash: raw2.txHash,
324
+ type: raw2.type,
325
+ amount: raw2.amount
325
326
  };
326
327
  }
327
328
  var CHAIN_ID_TO_NAME = {
@@ -340,10 +341,10 @@ function resolveChainId(chain) {
340
341
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
341
342
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
342
343
  }
343
- function mapPendingAllocations(raw) {
344
- if (!Array.isArray(raw)) return void 0;
344
+ function mapPendingAllocations(raw2) {
345
+ if (!Array.isArray(raw2)) return void 0;
345
346
  const pending = [];
346
- for (const entry of raw) {
347
+ for (const entry of raw2) {
347
348
  if (typeof entry !== "object" || entry === null) continue;
348
349
  const e = entry;
349
350
  if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
@@ -366,8 +367,8 @@ function mapPendingAllocations(raw) {
366
367
  }
367
368
  return pending.length > 0 ? pending : void 0;
368
369
  }
369
- function mapBalances(raw, _chainId, smartWallet) {
370
- const portfolio = raw.portfolio;
370
+ function mapBalances(raw2, _chainId, smartWallet) {
371
+ const portfolio = raw2.portfolio;
371
372
  const portfolioByChain = portfolio.portfolioByChain ?? {};
372
373
  let totalBalance = 0;
373
374
  const tokens = [];
@@ -436,8 +437,8 @@ function sumTokenValues(tokens) {
436
437
  function sumTokenEarnings(tokens) {
437
438
  return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
438
439
  }
439
- function mapEarnings(raw, smartWallet) {
440
- const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
440
+ function mapEarnings(raw2, smartWallet) {
441
+ const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
441
442
  const tokens = [];
442
443
  for (const [chainIdKey, tokensBySymbol] of Object.entries(
443
444
  totalEarningsByChain
@@ -456,15 +457,15 @@ function mapEarnings(raw, smartWallet) {
456
457
  return {
457
458
  smartWallet,
458
459
  lifetimeEarnings: sumTokenEarnings(
459
- raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
460
+ raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
460
461
  ),
461
462
  tokens
462
463
  };
463
464
  }
464
- function mapWeightedApyByChain(raw) {
465
- if (!raw) return void 0;
465
+ function mapWeightedApyByChain(raw2) {
466
+ if (!raw2) return void 0;
466
467
  const out = {};
467
- for (const [chainKey, tokenApy] of Object.entries(raw)) {
468
+ for (const [chainKey, tokenApy] of Object.entries(raw2)) {
468
469
  const chainId = Number(chainKey);
469
470
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
470
471
  const perAsset = {};
@@ -504,15 +505,15 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
504
505
  }
505
506
  return totalBalance > 0 ? weightedSum / totalBalance : null;
506
507
  }
507
- function mapApyHistory(raw, chainId, tokenSymbol) {
508
- const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
508
+ function mapApyHistory(raw2, chainId, tokenSymbol) {
509
+ const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
509
510
  date,
510
511
  apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
511
512
  })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
512
513
  return {
513
- walletAddress: raw.walletAddress,
514
- weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
515
- apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
514
+ walletAddress: raw2.walletAddress,
515
+ weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
516
+ apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
516
517
  history
517
518
  };
518
519
  }
@@ -608,23 +609,23 @@ function mapEntries(rawEntries, chainId) {
608
609
  };
609
610
  });
610
611
  }
611
- function mapUserProfile(raw, userAddress) {
612
+ function mapUserProfile(raw2, userAddress) {
612
613
  return {
613
614
  address: userAddress,
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
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
621
622
  };
622
623
  }
623
- function mapApyByStrategy(raw) {
624
+ function mapApyByStrategy(raw2) {
624
625
  const apyPerAsset = {};
625
626
  let apySum = 0;
626
627
  let apyCount = 0;
627
- for (const entry of raw.data) {
628
+ for (const entry of raw2.data) {
628
629
  const supported = SupportedAssets.find(
629
630
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
630
631
  );
@@ -795,15 +796,15 @@ var readSession = (address, _chainId) => {
795
796
  if (typeof window === "undefined") return null;
796
797
  const key2 = buildKey(address);
797
798
  const store = storage();
798
- let raw = null;
799
+ let raw2 = null;
799
800
  try {
800
- raw = store?.getItem(key2) ?? null;
801
+ raw2 = store?.getItem(key2) ?? null;
801
802
  } catch {
802
- raw = null;
803
+ raw2 = null;
803
804
  }
804
- if (raw) {
805
+ if (raw2) {
805
806
  try {
806
- const parsed = JSON.parse(raw);
807
+ const parsed = JSON.parse(raw2);
807
808
  if (isFreshSession(parsed)) return parsed;
808
809
  } catch {
809
810
  }
@@ -971,8 +972,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
971
972
  }
972
973
  return result;
973
974
  }
974
- function flattenAvailablePools(raw) {
975
- const byChain = raw ?? {};
975
+ function flattenAvailablePools(raw2) {
976
+ const byChain = raw2 ?? {};
976
977
  const names = [];
977
978
  for (const byToken of Object.values(byChain ?? {})) {
978
979
  for (const entry of Object.values(byToken ?? {})) {
@@ -1473,8 +1474,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1473
1474
  const poolResults = await Promise.all(
1474
1475
  universe.map(async (protocol) => {
1475
1476
  try {
1476
- const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1477
- return [protocol.id, flattenAvailablePools(raw)];
1477
+ const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
1478
+ return [protocol.id, flattenAvailablePools(raw2)];
1478
1479
  } catch (error) {
1479
1480
  console.warn(
1480
1481
  `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
@@ -1540,14 +1541,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1540
1541
  async readWalletState(ownerAddress) {
1541
1542
  try {
1542
1543
  const { portfolio } = await this.sdk.getPositions(ownerAddress);
1543
- const raw = portfolio;
1544
+ const raw2 = portfolio;
1544
1545
  debugLog("zyfai:onboard", "wallet state from getPositions", {
1545
- predeployed: raw?.predeployed,
1546
- hasActiveSessionKey: raw?.hasActiveSessionKey
1546
+ predeployed: raw2?.predeployed,
1547
+ hasActiveSessionKey: raw2?.hasActiveSessionKey
1547
1548
  });
1548
1549
  return {
1549
- predeployed: raw?.predeployed,
1550
- hasActiveSessionKey: raw?.hasActiveSessionKey
1550
+ predeployed: raw2?.predeployed,
1551
+ hasActiveSessionKey: raw2?.hasActiveSessionKey
1551
1552
  };
1552
1553
  } catch (error) {
1553
1554
  console.warn(
@@ -1827,14 +1828,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1827
1828
  return { txHash, smartWallet, amount };
1828
1829
  }
1829
1830
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
1830
- const raw = await this.sdk.depositFunds(
1831
+ const raw2 = await this.sdk.depositFunds(
1831
1832
  this.getAddress(),
1832
1833
  validChainId,
1833
1834
  amount,
1834
1835
  asset,
1835
1836
  "aggressive"
1836
1837
  );
1837
- return mapDeposit(raw);
1838
+ return mapDeposit(raw2);
1838
1839
  } catch (error) {
1839
1840
  throw error;
1840
1841
  }
@@ -1843,27 +1844,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
1843
1844
  async withdraw(state, chainId, token, amount) {
1844
1845
  const validChainId = isValidChainId(chainId);
1845
1846
  await this.ensureConnected(state, validChainId);
1846
- const raw = await this.sdk.withdrawFunds(
1847
+ const raw2 = await this.sdk.withdrawFunds(
1847
1848
  this.getAddress(),
1848
1849
  validChainId,
1849
1850
  amount,
1850
1851
  token
1851
1852
  );
1852
- if (!raw.success) {
1853
+ if (!raw2.success) {
1853
1854
  throw new OwneyError(
1854
1855
  "WITHDRAW_FAILED",
1855
- raw.message || "Zyfai withdraw failed.",
1856
- { chainId: validChainId, token, amount, response: raw },
1856
+ raw2.message || "Zyfai withdraw failed.",
1857
+ { chainId: validChainId, token, amount, response: raw2 },
1857
1858
  this.id
1858
1859
  );
1859
1860
  }
1860
- return mapWithdraw(raw);
1861
+ return mapWithdraw(raw2);
1861
1862
  }
1862
1863
  // --- IAgent: Portfolio reads ---
1863
1864
  async getBalances(state, chainId) {
1864
1865
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
1865
- const raw = await this.sdk.getPortfolio(this.getAddress());
1866
- return mapBalances(raw, validChainId, smartWallet);
1866
+ const raw2 = await this.sdk.getPortfolio(this.getAddress());
1867
+ return mapBalances(raw2, validChainId, smartWallet);
1867
1868
  }
1868
1869
  earningsKey(state, chainId, smartWallet) {
1869
1870
  return JSON.stringify([
@@ -1876,11 +1877,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
1876
1877
  const existing = this.earningsReads.get(key2);
1877
1878
  if (existing) return existing;
1878
1879
  const generation = this.earningsGeneration;
1879
- const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw) => {
1880
+ const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw2) => {
1880
1881
  if (generation === this.earningsGeneration) {
1881
- this.earningsSnapshot = { key: key2, raw, at: Date.now() };
1882
+ this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
1882
1883
  }
1883
- return raw;
1884
+ return raw2;
1884
1885
  }).finally(() => {
1885
1886
  if (this.earningsReads.get(key2) === pending)
1886
1887
  this.earningsReads.delete(key2);
@@ -1890,11 +1891,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
1890
1891
  }
1891
1892
  async getEarnings(state, chainId) {
1892
1893
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1893
- const raw = await this.readEarnings(
1894
+ const raw2 = await this.readEarnings(
1894
1895
  this.earningsKey(state, chainId, smartWallet),
1895
1896
  smartWallet
1896
1897
  );
1897
- return mapEarnings(raw, smartWallet);
1898
+ return mapEarnings(raw2, smartWallet);
1898
1899
  }
1899
1900
  async refreshEarnings(state, chainId) {
1900
1901
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -1921,8 +1922,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1921
1922
  }
1922
1923
  async getAccountApy(state, chainId, days, tokenSymbol) {
1923
1924
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1924
- const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
1925
- return mapApyHistory(raw, chainId, tokenSymbol);
1925
+ const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
1926
+ return mapApyHistory(raw2, chainId, tokenSymbol);
1926
1927
  }
1927
1928
  /**
1928
1929
  * Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
@@ -1957,7 +1958,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1957
1958
  const matched = [];
1958
1959
  let backendExhausted = false;
1959
1960
  for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
1960
- const raw = await this.sdk.getHistory(smartWallet, validChainId, {
1961
+ const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
1961
1962
  limit: backendPageSize,
1962
1963
  offset,
1963
1964
  fromDate: options?.fromDate,
@@ -1968,13 +1969,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
1968
1969
  // asset's rows and handing back a page that filters to nothing.
1969
1970
  assetType
1970
1971
  });
1971
- raw.data.forEach((entry, idx) => {
1972
+ raw2.data.forEach((entry, idx) => {
1972
1973
  if (entry.chainId === validChainId) {
1973
1974
  matched.push({ entry, rawIdx: offset + idx });
1974
1975
  }
1975
1976
  });
1976
- offset += raw.data.length;
1977
- if (raw.data.length < backendPageSize) {
1977
+ offset += raw2.data.length;
1978
+ if (raw2.data.length < backendPageSize) {
1978
1979
  backendExhausted = true;
1979
1980
  break;
1980
1981
  }
@@ -1996,18 +1997,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
1996
1997
  }
1997
1998
  async getUserProfile(state, chainId) {
1998
1999
  await this.connectAuth(state, chainId);
1999
- const raw = await this.sdk.getUserDetails();
2000
+ const raw2 = await this.sdk.getUserDetails();
2000
2001
  debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
2001
2002
  asset: "USDC (default \u2014 no asset passed)",
2002
- splitting: raw.splitting,
2003
- minSplits: raw.minSplits,
2004
- strategy: raw.strategy,
2005
- chains: raw.chains,
2006
- protocolCount: raw.protocols?.length,
2007
- hasActiveSessionKey: raw.hasActiveSessionKey,
2008
- smartWallet: raw.smartWallet
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
2009
2010
  });
2010
- return mapUserProfile(raw, this.connectedAddress);
2011
+ return mapUserProfile(raw2, this.connectedAddress);
2011
2012
  }
2012
2013
  async ensureAutoSelectProtocols(state, chainId, asset) {
2013
2014
  await this.connectAuth(state, chainId);
@@ -2026,236 +2027,58 @@ var ZyfaiAgent = class _ZyfaiAgent {
2026
2027
  }
2027
2028
  // --- IAgent: Discovery (no wallet required) ---
2028
2029
  async getAgentApy(days, options) {
2029
- const raw = await this.sdk.getAPYPerStrategy(
2030
+ const raw2 = await this.sdk.getAPYPerStrategy(
2030
2031
  false,
2031
2032
  DayFilterMapping[days],
2032
2033
  "aggressive",
2033
2034
  options?.chainId,
2034
2035
  options?.tokenSymbol
2035
2036
  );
2036
- return mapApyByStrategy(raw);
2037
+ return mapApyByStrategy(raw2);
2037
2038
  }
2038
2039
  };
2039
2040
 
2040
- // src/lib/routing-api.ts
2041
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2042
- async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL) {
2043
- const url = `${baseUrl}/api/v1/agent/org-config`;
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;
2044
2057
  try {
2045
- const res = await fetch(url, {
2046
- method: "GET",
2047
- headers: {
2048
- "Content-Type": "application/json",
2049
- "x-owney-api-key": `${apiKey}`
2050
- }
2051
- });
2052
- if (!res.ok) {
2053
- if (res.status !== 404) {
2054
- console.warn(
2055
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
2056
- );
2057
- }
2058
- return null;
2059
- }
2060
- const json = await res.json();
2061
- const policy = json.success ? json.data ?? null : null;
2062
- debugLog(
2063
- "owney-sdk",
2064
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
2065
- policy ?? void 0
2066
- );
2067
- return policy;
2058
+ await wallet.switchChain({ id: expected });
2068
2059
  } catch (error) {
2069
- console.warn(
2070
- "[owney-sdk] Could not read org agent config (non-fatal):",
2071
- error instanceof Error ? error.message : String(error)
2072
- );
2073
- return null;
2074
- }
2075
- }
2076
- async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
2077
- const url = `${baseUrl}/api/v1/agent/keys`;
2078
- const res = await fetch(url, {
2079
- method: "GET",
2080
- headers: {
2081
- "Content-Type": "application/json",
2082
- "x-owney-api-key": `${apiKey}`
2083
- }
2084
- });
2085
- if (!res.ok) {
2086
- const text = await res.text().catch(() => "");
2087
2060
  throw new OwneyError(
2088
- "API_ROUTING_ERROR",
2089
- `Routing API error ${res.status}: ${text}`,
2090
- { statusCode: res.status, responseBody: text }
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
+ }
2091
2068
  );
2092
2069
  }
2093
- const json = await res.json();
2094
- if (!json.success) {
2070
+ const after = await pub.getChainId();
2071
+ if (after !== expected) {
2095
2072
  throw new OwneyError(
2096
- "API_ROUTING_FAILED",
2097
- `Routing API request failed: ${json.message}`,
2098
- { message: json.message }
2099
- );
2100
- }
2101
- return json.data;
2102
- }
2103
-
2104
- // src/lib/health-report.ts
2105
- var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2106
- async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
2107
- try {
2108
- await fetch(`${baseUrl}/api/v1/agent/health-report`, {
2109
- method: "POST",
2110
- headers: {
2111
- "Content-Type": "application/json",
2112
- "x-owney-api-key": apiKey
2113
- },
2114
- body: JSON.stringify({
2115
- agent_type: agentType,
2116
- error_code: errorCode,
2117
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2118
- })
2119
- });
2120
- } catch (err) {
2121
- console.warn(
2122
- `[owney-sdk] health-report failed for agent "${agentType}":`,
2123
- err instanceof Error ? err.message : err
2124
- );
2125
- }
2126
- }
2127
- async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
2128
- try {
2129
- return await fn();
2130
- } catch (err) {
2131
- const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
2132
- void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
2133
- throw err;
2134
- }
2135
- }
2136
-
2137
- // src/lib/helpers/withdraw-helper.ts
2138
- var import_viem2 = require("viem");
2139
- function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
2140
- const target = asset.toUpperCase();
2141
- return agents.map((agent) => {
2142
- const agentBalance = aggregated[agent.id];
2143
- const tokenBalance = agentBalance?.tokens.find(
2144
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
2073
+ "CHAIN_MISMATCH",
2074
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2075
+ { expectedChainId: expected, actualChainId: after }
2145
2076
  );
2146
- if (!tokenBalance) return { agent, balance: 0n };
2147
- return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
2148
- });
2149
- }
2150
- function planProportionalShares(balances, requested, totalAvailable) {
2151
- const plans = balances.map(({ agent, balance }) => ({
2152
- agent,
2153
- balance,
2154
- planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
2155
- }));
2156
- const assigned = plans.reduce((s, p) => s + p.planned, 0n);
2157
- let remainder = requested - assigned;
2158
- const byHeadroom = [...plans].sort((a, b) => {
2159
- const diff = b.balance - b.planned - (a.balance - a.planned);
2160
- return diff > 0n ? 1 : diff < 0n ? -1 : 0;
2161
- });
2162
- for (const p of byHeadroom) {
2163
- if (remainder === 0n) break;
2164
- const headroom = p.balance - p.planned;
2165
- if (headroom <= 0n) continue;
2166
- const take = headroom < remainder ? headroom : remainder;
2167
- p.planned += take;
2168
- remainder -= take;
2169
- }
2170
- return plans;
2171
- }
2172
- function planDisabledDrain(disabled, requested) {
2173
- const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
2174
- const plans = [];
2175
- let remaining = requested;
2176
- for (const { agent, balance } of sorted) {
2177
- if (remaining === 0n) {
2178
- plans.push({ agent, balance, planned: 0n });
2179
- continue;
2180
- }
2181
- const take = balance < remaining ? balance : remaining;
2182
- plans.push({ agent, balance, planned: take });
2183
- remaining -= take;
2184
- }
2185
- return { plans, remaining };
2186
- }
2187
- function redistributeShare(plans, fromIndex, amount, candidatePool) {
2188
- const pool = candidatePool ?? plans.slice(fromIndex + 1);
2189
- const candidates = pool.filter((c) => c.balance - c.planned > 0n);
2190
- const totalHeadroom = candidates.reduce(
2191
- (s, c) => s + (c.balance - c.planned),
2192
- 0n
2193
- );
2194
- if (totalHeadroom === 0n) return;
2195
- let distributed = 0n;
2196
- for (const c of candidates) {
2197
- const headroom = c.balance - c.planned;
2198
- const proportional = headroom * amount / totalHeadroom;
2199
- const give = proportional > headroom ? headroom : proportional;
2200
- c.planned += give;
2201
- distributed += give;
2202
- }
2203
- let leftover = amount - distributed;
2204
- for (const c of candidates) {
2205
- if (leftover === 0n) break;
2206
- const headroom = c.balance - c.planned;
2207
- if (headroom <= 0n) continue;
2208
- const take = headroom < leftover ? headroom : leftover;
2209
- c.planned += take;
2210
- leftover -= take;
2211
- }
2212
- }
2213
- function sumWithdrawnAmount(results) {
2214
- return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
2215
- }
2216
-
2217
- // src/lib/helpers/account-apy-helper.ts
2218
- function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2219
- const sums = {};
2220
- const weights = {};
2221
- for (const id of Object.keys(agentApys)) {
2222
- const cells = agentApys[id].apyByChainAndAsset;
2223
- const balance = agentBalances[id] ?? 0;
2224
- if (!cells || balance <= 0) continue;
2225
- for (const [chainKey, perAsset] of Object.entries(cells)) {
2226
- if (!perAsset) continue;
2227
- const chainId = Number(chainKey);
2228
- for (const [asset, apyValue] of Object.entries(perAsset)) {
2229
- const apy = Number(apyValue ?? 0);
2230
- if (apy === 0) continue;
2231
- sums[chainId] ??= {};
2232
- weights[chainId] ??= {};
2233
- sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
2234
- weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2235
- }
2236
- }
2237
- }
2238
- const out = {};
2239
- for (const chainKey of Object.keys(sums)) {
2240
- const chainId = Number(chainKey);
2241
- const perAssetOut = {};
2242
- for (const asset of Object.keys(sums[chainId])) {
2243
- const w = weights[chainId][asset];
2244
- if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
2245
- }
2246
- if (Object.keys(perAssetOut).length > 0) {
2247
- out[chainId] = perAssetOut;
2248
- }
2249
2077
  }
2250
- return out;
2251
2078
  }
2252
2079
 
2253
- // src/client.ts
2254
- var import_viem6 = require("viem");
2255
- var import_chains2 = require("viem/chains");
2256
-
2257
2080
  // src/lib/transfer-auth.ts
2258
- var import_viem3 = require("viem");
2081
+ var import_viem2 = require("viem");
2259
2082
  var ERC20_META_ABI = [
2260
2083
  { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
2261
2084
  { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
@@ -2287,28 +2110,29 @@ async function readTokenMeta(publicClient, token) {
2287
2110
  function randomAuthNonce() {
2288
2111
  const bytes = new Uint8Array(32);
2289
2112
  globalThis.crypto.getRandomValues(bytes);
2290
- return (0, import_viem3.bytesToHex)(bytes);
2113
+ return (0, import_viem2.bytesToHex)(bytes);
2291
2114
  }
2292
2115
 
2293
2116
  // src/lib/sponsor-client.ts
2294
- var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2295
- async function postSponsorTransferAuth(input) {
2296
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
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;
2297
2120
  let res;
2298
2121
  try {
2299
- res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
2122
+ res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
2300
2123
  method: "POST",
2301
2124
  headers: {
2302
2125
  "content-type": "application/json",
2303
- "x-owney-api-key": input.apiKey
2126
+ "x-owney-api-key": input.apiKey,
2127
+ Authorization: `Signature ${input.yieldseekerSignature}`
2304
2128
  },
2305
2129
  body: JSON.stringify(input.body)
2306
2130
  });
2307
2131
  } catch (networkError) {
2308
2132
  throw new OwneyError(
2309
2133
  "SPONSOR_REQUEST_FAILED",
2310
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2311
- { cause: String(networkError) }
2134
+ `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2135
+ { cause: String(networkError), safeToFallback: true }
2312
2136
  );
2313
2137
  }
2314
2138
  const text = await res.text();
@@ -2317,30 +2141,29 @@ async function postSponsorTransferAuth(input) {
2317
2141
  parsed = JSON.parse(text);
2318
2142
  } catch {
2319
2143
  }
2320
- if (!res.ok || !parsed?.success || !parsed.data) {
2144
+ if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
2321
2145
  throw new OwneyError(
2322
2146
  "SPONSOR_REQUEST_FAILED",
2323
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2147
+ `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2324
2148
  {
2325
2149
  statusCode: res.status,
2326
2150
  responseBody: text.slice(0, 500),
2327
- // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
2328
- // before broadcast, so it is safe to fall back to a user-paid deposit.
2329
- safeToFallback: res.status === 503
2151
+ safeToFallback: true
2330
2152
  }
2331
2153
  );
2332
2154
  }
2333
2155
  return parsed.data;
2334
2156
  }
2335
- async function postSponsorPermit2Transfer(input) {
2336
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2157
+ async function postSponsorTransferAuth(input) {
2158
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2337
2159
  let res;
2338
2160
  try {
2339
- res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
2161
+ res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
2340
2162
  method: "POST",
2341
2163
  headers: {
2342
2164
  "content-type": "application/json",
2343
- "x-owney-api-key": input.apiKey
2165
+ "x-owney-api-key": input.apiKey,
2166
+ ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
2344
2167
  },
2345
2168
  body: JSON.stringify(input.body)
2346
2169
  });
@@ -2348,7 +2171,7 @@ async function postSponsorPermit2Transfer(input) {
2348
2171
  throw new OwneyError(
2349
2172
  "SPONSOR_REQUEST_FAILED",
2350
2173
  `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2351
- { cause: String(networkError), safeToFallback: false }
2174
+ { cause: String(networkError) }
2352
2175
  );
2353
2176
  }
2354
2177
  const text = await res.text();
@@ -2364,21 +2187,62 @@ async function postSponsorPermit2Transfer(input) {
2364
2187
  {
2365
2188
  statusCode: res.status,
2366
2189
  responseBody: text.slice(0, 500),
2367
- safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
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
2368
2193
  }
2369
2194
  );
2370
2195
  }
2371
2196
  return parsed.data;
2372
2197
  }
2373
- async function getSponsorRelayerAddress(input) {
2374
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2198
+ async function postSponsorPermit2Transfer(input) {
2199
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2375
2200
  let res;
2376
2201
  try {
2377
- res = await fetch(
2378
- `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2379
- {
2380
- headers: { "x-owney-api-key": input.apiKey }
2381
- }
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
2232
+ }
2233
+ );
2234
+ }
2235
+ return parsed.data;
2236
+ }
2237
+ async function getSponsorRelayerAddress(input) {
2238
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2239
+ let res;
2240
+ try {
2241
+ res = await fetch(
2242
+ `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2243
+ {
2244
+ headers: { "x-owney-api-key": input.apiKey }
2245
+ }
2382
2246
  );
2383
2247
  } catch (networkError) {
2384
2248
  throw new OwneyError(
@@ -2408,7 +2272,7 @@ async function getSponsorRelayerAddress(input) {
2408
2272
  }
2409
2273
 
2410
2274
  // src/lib/permit2.ts
2411
- var import_viem4 = require("viem");
2275
+ var import_viem3 = require("viem");
2412
2276
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2413
2277
  var MAX_UINT256 = 2n ** 256n - 1n;
2414
2278
  var ERC20_ALLOWANCE_ABI = [
@@ -2466,7 +2330,7 @@ function buildPermitTransferFromTypedData(input) {
2466
2330
  function randomPermit2Nonce() {
2467
2331
  const bytes = new Uint8Array(32);
2468
2332
  globalThis.crypto.getRandomValues(bytes);
2469
- return BigInt((0, import_viem4.bytesToHex)(bytes));
2333
+ return BigInt((0, import_viem3.bytesToHex)(bytes));
2470
2334
  }
2471
2335
  async function readPermit2Allowance(publicClient, token, owner) {
2472
2336
  return publicClient.readContract({
@@ -2476,213 +2340,1972 @@ async function readPermit2Allowance(publicClient, token, owner) {
2476
2340
  args: [owner, PERMIT2_ADDRESS]
2477
2341
  });
2478
2342
  }
2479
- async function readErc20Balance(publicClient, token, owner) {
2480
- return publicClient.readContract({
2481
- address: token,
2482
- abi: ERC20_ALLOWANCE_ABI,
2483
- functionName: "balanceOf",
2484
- args: [owner]
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;
2485
4082
  });
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;
2486
4092
  }
2487
-
2488
- // src/lib/chain-guard.ts
2489
- var CHAIN_NAMES = {
2490
- 1: "Ethereum",
2491
- 8453: "Base",
2492
- 42161: "Arbitrum"
2493
- };
2494
- function chainName(chainId) {
2495
- return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
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 };
2496
4109
  }
2497
- async function ensureWalletOnChain(pub, wallet, expected) {
2498
- const actual = await pub.getChainId();
2499
- if (actual === expected) return;
2500
- try {
2501
- await wallet.switchChain({ id: expected });
2502
- } catch (error) {
2503
- throw new OwneyError(
2504
- "CHAIN_MISMATCH",
2505
- `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2506
- {
2507
- expectedChainId: expected,
2508
- actualChainId: actual,
2509
- cause: error instanceof Error ? error.message : String(error)
2510
- }
2511
- );
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;
2512
4125
  }
2513
- const after = await pub.getChainId();
2514
- if (after !== expected) {
2515
- throw new OwneyError(
2516
- "CHAIN_MISMATCH",
2517
- `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2518
- { expectedChainId: expected, actualChainId: after }
2519
- );
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;
2520
4134
  }
2521
4135
  }
4136
+ function sumWithdrawnAmount(results) {
4137
+ return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
4138
+ }
2522
4139
 
2523
- // src/lib/sponsored-deposit.ts
2524
- var AUTH_WINDOW_SECONDS = 15 * 60;
2525
- function makeSponsoredDepositCallback(deps) {
2526
- const post = deps.httpPost ?? postSponsorTransferAuth;
2527
- return async (smartWallet, chainId, amount) => {
2528
- const cid = chainId;
2529
- const token = deps.tokenAddressByChain[cid];
2530
- if (!token) {
2531
- throw new OwneyError(
2532
- "CHAIN_UNSUPPORTED",
2533
- `No sponsored token configured for chain ${chainId}`
2534
- );
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);
2535
4167
  }
2536
- const pub = deps.getPublicClient(cid);
2537
- const wallet = deps.getWalletClient(cid);
2538
- await ensureWalletOnChain(pub, wallet, cid);
2539
- try {
2540
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2541
- if (balance < BigInt(amount)) {
2542
- throw new OwneyError(
2543
- "DEPOSIT_INSUFFICIENT_BALANCE",
2544
- "Insufficient balance for this deposit.",
2545
- { token, chainId: cid, balance: balance.toString(), amount }
2546
- );
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;
2547
4191
  }
2548
- } catch (err) {
2549
- if (err instanceof OwneyError) throw err;
2550
- console.warn(
2551
- "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2552
- err instanceof Error ? err.message : String(err)
2553
- );
2554
4192
  }
2555
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2556
- const validAfter = 0n;
2557
- const validBefore = BigInt(
2558
- Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2559
- );
2560
- const nonce = randomAuthNonce();
2561
- const typedData = buildTransferWithAuthorizationTypedData({
2562
- token,
2563
- chainId: cid,
2564
- tokenName,
2565
- tokenVersion,
2566
- message: {
2567
- from: deps.ownerAddress,
2568
- to: smartWallet,
2569
- value: BigInt(amount),
2570
- validAfter,
2571
- validBefore,
2572
- nonce
2573
- }
2574
- });
2575
- const authSignature = await wallet.signTypedData({
2576
- account: deps.ownerAddress,
2577
- ...typedData
2578
- });
2579
- deps.onApproved?.();
2580
- const result = await post({
2581
- baseUrl: deps.baseUrl,
2582
- apiKey: deps.apiKey,
2583
- body: {
2584
- chainId: cid,
2585
- token,
2586
- from: deps.ownerAddress,
2587
- to: smartWallet,
2588
- value: amount,
2589
- validAfter: validAfter.toString(),
2590
- validBefore: validBefore.toString(),
2591
- nonce,
2592
- authSignature,
2593
- tokenName,
2594
- tokenVersion
2595
- }
2596
- });
2597
- return result.txHash;
2598
- };
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;
2599
4207
  }
2600
4208
 
4209
+ // src/client.ts
4210
+ var import_viem9 = require("viem");
4211
+ var import_chains4 = require("viem/chains");
4212
+
2601
4213
  // src/lib/sponsored-weth-deposit.ts
2602
4214
  var PERMIT_WINDOW_SECONDS = 15 * 60;
2603
4215
  function makeSponsoredWethCallback(deps) {
2604
4216
  const get = deps.httpGet ?? getSponsorRelayerAddress;
2605
4217
  const post = deps.httpPost ?? postSponsorPermit2Transfer;
2606
- return async (smartWallet, chainId, amount) => {
2607
- const cid = chainId;
2608
- const token = deps.tokenAddressByChain[cid];
2609
- if (!token) {
2610
- throw new OwneyError(
2611
- "CHAIN_UNSUPPORTED",
2612
- `No sponsored WETH configured for chain ${chainId}`
2613
- );
2614
- }
2615
- const amountWei = BigInt(amount);
2616
- const pub = deps.getPublicClient(cid);
2617
- const wallet = deps.getWalletClient(cid);
2618
- await ensureWalletOnChain(pub, wallet, cid);
2619
- try {
2620
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2621
- if (balance < amountWei) {
4218
+ return makeVerificationAwareDepositCallback(
4219
+ async (smartWallet, chainId, amount, verification) => {
4220
+ const cid = chainId;
4221
+ const token = deps.tokenAddressByChain[cid];
4222
+ if (!token) {
2622
4223
  throw new OwneyError(
2623
- "DEPOSIT_INSUFFICIENT_BALANCE",
2624
- "Insufficient WETH balance for this deposit.",
2625
- { token, chainId: cid, balance: balance.toString(), amount }
4224
+ "CHAIN_UNSUPPORTED",
4225
+ `No sponsored WETH configured for chain ${chainId}`
2626
4226
  );
2627
4227
  }
2628
- } catch (err) {
2629
- if (err instanceof OwneyError) throw err;
2630
- console.warn(
2631
- "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2632
- err instanceof Error ? err.message : String(err)
2633
- );
2634
- }
2635
- const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2636
- if (allowance < amountWei) {
2637
- throw new OwneyError(
2638
- "PERMIT2_APPROVAL_REQUIRED",
2639
- "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
2640
- { token, chainId: cid, allowance: allowance.toString(), amount }
2641
- );
2642
- }
2643
- const relayer = await get({
2644
- baseUrl: deps.baseUrl,
2645
- apiKey: deps.apiKey,
2646
- chainId: cid
2647
- });
2648
- const nonce = randomPermit2Nonce();
2649
- const deadline = BigInt(
2650
- Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
2651
- );
2652
- const typedData = buildPermitTransferFromTypedData({
2653
- chainId: cid,
2654
- message: {
2655
- permitted: { token, amount: amountWei },
2656
- spender: relayer,
2657
- nonce,
2658
- deadline
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
+ );
2659
4247
  }
2660
- });
2661
- const signature = await wallet.signTypedData({
2662
- account: deps.ownerAddress,
2663
- ...typedData
2664
- });
2665
- deps.onApproved?.();
2666
- const result = await post({
2667
- baseUrl: deps.baseUrl,
2668
- apiKey: deps.apiKey,
2669
- body: {
2670
- chainId: cid,
4248
+ const allowance = await readPermit2Allowance(
4249
+ pub,
2671
4250
  token,
2672
- from: deps.ownerAddress,
2673
- to: smartWallet,
2674
- amount,
2675
- nonce: nonce.toString(),
2676
- deadline: deadline.toString(),
2677
- signature
4251
+ deps.ownerAddress
4252
+ );
4253
+ if (allowance < amountWei) {
4254
+ 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 }
4258
+ );
2678
4259
  }
2679
- });
2680
- return result.txHash;
2681
- };
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
4268
+ );
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
+ }
4304
+ );
2682
4305
  }
2683
4306
 
2684
4307
  // src/lib/sponsored-calls-deposit.ts
2685
- var import_viem5 = require("viem");
4308
+ var import_viem8 = require("viem");
2686
4309
  var DEFAULT_POLL_INTERVAL_MS = 1500;
2687
4310
  var DEFAULT_MAX_POLLS = 30;
2688
4311
  async function paymasterSupported(provider, owner, chainId) {
@@ -2690,7 +4313,7 @@ async function paymasterSupported(provider, owner, chainId) {
2690
4313
  method: "wallet_getCapabilities",
2691
4314
  params: [owner]
2692
4315
  });
2693
- const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
4316
+ const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
2694
4317
  return Boolean(forChain?.paymasterService?.supported);
2695
4318
  }
2696
4319
  function makeSponsoredCallsCallback(deps) {
@@ -2708,7 +4331,7 @@ function makeSponsoredCallsCallback(deps) {
2708
4331
  }
2709
4332
  return new URL(configured, origin).toString();
2710
4333
  };
2711
- return async (smartWallet, chainId, amount) => {
4334
+ return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
2712
4335
  const cid = chainId;
2713
4336
  const token = deps.tokenAddressByChain[cid];
2714
4337
  if (!token) {
@@ -2724,22 +4347,48 @@ function makeSponsoredCallsCallback(deps) {
2724
4347
  { chainId }
2725
4348
  );
2726
4349
  }
2727
- const data = (0, import_viem5.encodeFunctionData)({
2728
- abi: import_viem5.erc20Abi,
4350
+ const data = (0, import_viem8.encodeFunctionData)({
4351
+ abi: import_viem8.erc20Abi,
2729
4352
  functionName: "transfer",
2730
4353
  args: [smartWallet, BigInt(amount)]
2731
4354
  });
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
+ }
2732
4381
  const sendResult = await deps.provider.request({
2733
4382
  method: "wallet_sendCalls",
2734
4383
  params: [
2735
4384
  {
2736
4385
  version: "2.0.0",
2737
4386
  from: deps.ownerAddress,
2738
- chainId: (0, import_viem5.toHex)(chainId),
4387
+ chainId: (0, import_viem8.toHex)(chainId),
2739
4388
  atomicRequired: false,
2740
4389
  calls: [{ to: token, value: "0x0", data }],
2741
4390
  capabilities: {
2742
- paymasterService: { url: absolutePaymasterUrl() }
4391
+ paymasterService: { url: paymasterUrl }
2743
4392
  }
2744
4393
  }
2745
4394
  ]
@@ -2769,7 +4418,7 @@ function makeSponsoredCallsCallback(deps) {
2769
4418
  `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
2770
4419
  { chainId, callsId }
2771
4420
  );
2772
- };
4421
+ });
2773
4422
  }
2774
4423
 
2775
4424
  // src/client.ts
@@ -2799,9 +4448,9 @@ var SPONSORED_USDC_BY_CHAIN = {
2799
4448
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
2800
4449
  };
2801
4450
  var VIEM_CHAIN2 = {
2802
- 8453: import_chains2.base,
2803
- 42161: import_chains2.arbitrum,
2804
- 1: import_chains2.mainnet
4451
+ 8453: import_chains4.base,
4452
+ 42161: import_chains4.arbitrum,
4453
+ 1: import_chains4.mainnet
2805
4454
  };
2806
4455
  var SPONSORED_WETH_BY_CHAIN = {
2807
4456
  8453: "0x4200000000000000000000000000000000000006",
@@ -2829,6 +4478,8 @@ var OwneySDK = class {
2829
4478
  orgAgentConfig;
2830
4479
  orgAgentConfigPromise = null;
2831
4480
  zyfaiRpcUrls;
4481
+ yieldseekerApiBaseUrl;
4482
+ yieldseekerSiweOrigin;
2832
4483
  routingApiBaseUrl;
2833
4484
  referralSource;
2834
4485
  cachedSponsoredCallback = null;
@@ -2851,6 +4502,8 @@ var OwneySDK = class {
2851
4502
  this.apiKey = config.apiKey;
2852
4503
  if (config.debug) setOwneyDebug(true);
2853
4504
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
4505
+ this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
4506
+ this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
2854
4507
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2855
4508
  this.paymasterServiceUrl = config.paymasterServiceUrl;
2856
4509
  this.referralSource = config.referralSource;
@@ -2957,14 +4610,14 @@ var OwneySDK = class {
2957
4610
  // Casts work around viem's chain-narrowed Client vs the generic
2958
4611
  // PublicClient/WalletClient param types — structurally identical at
2959
4612
  // runtime, but the two share a name TS treats as unrelated.
2960
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
4613
+ getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
2961
4614
  chain: VIEM_CHAIN2[cid],
2962
- transport: (0, import_viem6.custom)(provider)
4615
+ transport: (0, import_viem9.custom)(provider)
2963
4616
  }),
2964
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
4617
+ getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
2965
4618
  account: owner,
2966
4619
  chain: VIEM_CHAIN2[cid],
2967
- transport: (0, import_viem6.custom)(provider)
4620
+ transport: (0, import_viem9.custom)(provider)
2968
4621
  })
2969
4622
  });
2970
4623
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -2981,6 +4634,8 @@ var OwneySDK = class {
2981
4634
  if (!onApproved && cached) return cached;
2982
4635
  const provider = this.requireConnectedProvider();
2983
4636
  const callback = makeSponsoredCallsCallback({
4637
+ apiKey: this.apiKey,
4638
+ routingApiBaseUrl: this.routingApiBaseUrl,
2984
4639
  provider,
2985
4640
  ownerAddress: this.state.walletAddress,
2986
4641
  paymasterServiceUrl: this.paymasterServiceUrl,
@@ -3010,14 +4665,14 @@ var OwneySDK = class {
3010
4665
  // Casts work around viem's chain-narrowed Client vs the generic
3011
4666
  // PublicClient/WalletClient param types — structurally identical at
3012
4667
  // runtime, but the two share a name TS treats as unrelated.
3013
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
4668
+ getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
3014
4669
  chain: VIEM_CHAIN2[cid],
3015
- transport: (0, import_viem6.custom)(provider)
4670
+ transport: (0, import_viem9.custom)(provider)
3016
4671
  }),
3017
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
4672
+ getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
3018
4673
  account: owner,
3019
4674
  chain: VIEM_CHAIN2[cid],
3020
- transport: (0, import_viem6.custom)(provider)
4675
+ transport: (0, import_viem9.custom)(provider)
3021
4676
  })
3022
4677
  });
3023
4678
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -3048,12 +4703,10 @@ var OwneySDK = class {
3048
4703
  this.orgAgentConfigPromise = fetchOrgAgentConfig(
3049
4704
  this.apiKey,
3050
4705
  this.routingApiBaseUrl
3051
- ).then(
3052
- (config) => {
3053
- this.orgAgentConfig = config;
3054
- return config;
3055
- }
3056
- );
4706
+ ).then((config) => {
4707
+ this.orgAgentConfig = config;
4708
+ return config;
4709
+ });
3057
4710
  }
3058
4711
  return this.orgAgentConfigPromise;
3059
4712
  }
@@ -3093,7 +4746,14 @@ var OwneySDK = class {
3093
4746
  this.routingApiBaseUrl
3094
4747
  );
3095
4748
  this.disabledAgents.clear();
3096
- for (const { key: key2, agent_type, is_enabled } of agentKeys) {
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;
3097
4757
  const agent = this.createAgent(agent_type, key2);
3098
4758
  if (!agent) continue;
3099
4759
  this.agents.set(agent_type, agent);
@@ -3117,8 +4777,15 @@ var OwneySDK = class {
3117
4777
  }
3118
4778
  createAgent(agentId, key2) {
3119
4779
  if (agentId === "zyfai") {
4780
+ if (!key2) return null;
3120
4781
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
3121
4782
  }
4783
+ if (agentId === "yieldseeker") {
4784
+ return new YieldseekerAgent(this.apiKey, {
4785
+ auth: { origin: this.yieldseekerSiweOrigin },
4786
+ baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
4787
+ });
4788
+ }
3122
4789
  return null;
3123
4790
  }
3124
4791
  /**
@@ -3163,7 +4830,7 @@ var OwneySDK = class {
3163
4830
  * If provided, ALL specified agents must support the chainId or the call
3164
4831
  * throws before activating any agent.
3165
4832
  */
3166
- async activateAgent(chainId, agentId) {
4833
+ async activateAgent(chainId, agentId, asset) {
3167
4834
  const state = this.requireState();
3168
4835
  await this.ensureAgentsInitialized();
3169
4836
  if (agentId !== void 0) {
@@ -3199,7 +4866,7 @@ var OwneySDK = class {
3199
4866
  this.activeAgents.add(id);
3200
4867
  }
3201
4868
  state.chainId = chainId;
3202
- await this.activateAgentsInTurn(agents, state, chainId);
4869
+ await this.activateAgentsInTurn(agents, state, chainId, asset);
3203
4870
  return;
3204
4871
  }
3205
4872
  const compatible = [...this.agents.values()].filter(
@@ -3220,7 +4887,7 @@ var OwneySDK = class {
3220
4887
  const enabledCompatible = compatible.filter(
3221
4888
  (agent) => !this.isAgentDisabled(agent.id)
3222
4889
  );
3223
- await this.activateAgentsInTurn(enabledCompatible, state, chainId);
4890
+ await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
3224
4891
  }
3225
4892
  /**
3226
4893
  * Activate agents ONE AT A TIME, each followed by its org policy.
@@ -3236,17 +4903,25 @@ var OwneySDK = class {
3236
4903
  * at a time anyway.
3237
4904
  *
3238
4905
  * Every agent is attempted even if an earlier one fails, so one declined
3239
- * signature can't deny the remaining agents their turn. The first failure is
3240
- * rethrown (matching the previous `Promise.all` rejection) once all agents
3241
- * have had a chance to activate.
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.
3242
4909
  */
3243
- async activateAgentsInTurn(agents, state, chainId) {
4910
+ async activateAgentsInTurn(agents, state, chainId, asset) {
3244
4911
  let firstError = null;
4912
+ const activatedAgentIds = [];
4913
+ const failedAgents = [];
3245
4914
  for (const agent of agents) {
3246
4915
  try {
3247
- await agent.activateAgent(state, chainId);
4916
+ await agent.activateAgent(state, chainId, asset);
3248
4917
  await this.applyOrgPolicyTo(agent, state, chainId);
4918
+ activatedAgentIds.push(agent.id);
3249
4919
  } 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
+ });
3250
4925
  if (firstError === null) {
3251
4926
  firstError = error;
3252
4927
  } else {
@@ -3254,7 +4929,16 @@ var OwneySDK = class {
3254
4929
  }
3255
4930
  }
3256
4931
  }
3257
- if (firstError !== null) throw firstError;
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
+ );
3258
4942
  }
3259
4943
  /**
3260
4944
  * Deposit into a specific agent, or distribute across all eligible agents.
@@ -3468,10 +5152,10 @@ var OwneySDK = class {
3468
5152
  agent,
3469
5153
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
3470
5154
  }));
3471
- const valid = splits.filter(
5155
+ const valid2 = splits.filter(
3472
5156
  (s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
3473
5157
  );
3474
- if (valid.length === agents.length) {
5158
+ if (valid2.length === agents.length) {
3475
5159
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
3476
5160
  }
3477
5161
  const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
@@ -3490,6 +5174,11 @@ var OwneySDK = class {
3490
5174
  )
3491
5175
  }));
3492
5176
  }
5177
+ formatAgentName(agentId) {
5178
+ if (agentId === "zyfai") return "Zyfai";
5179
+ if (agentId === "yieldseeker") return "Yieldseeker";
5180
+ return agentId;
5181
+ }
3493
5182
  async validateMinDepositAmount(agent, state, chainId, asset, amount) {
3494
5183
  const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
3495
5184
  const parsedAmount = BigInt(amount);
@@ -3521,12 +5210,12 @@ var OwneySDK = class {
3521
5210
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3522
5211
  );
3523
5212
  const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
3524
- const position = (balance.positions ?? []).find((p) => {
5213
+ const position2 = (balance.positions ?? []).find((p) => {
3525
5214
  const positionChain = p.chain.trim().toUpperCase();
3526
5215
  const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
3527
5216
  return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
3528
5217
  });
3529
- return !!token && Number(token.amount) > 0 || !!position;
5218
+ return !!token && Number(token.amount) > 0 || !!position2;
3530
5219
  } catch (error) {
3531
5220
  if (requireReliableRead) {
3532
5221
  throw new OwneyError(
@@ -3641,6 +5330,10 @@ var OwneySDK = class {
3641
5330
  }
3642
5331
  const requested = BigInt(amount);
3643
5332
  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);
3644
5337
  const balances = projectAgentBalancesForAsset(
3645
5338
  eligibleAgents,
3646
5339
  aggregated.agentBalances,
@@ -3649,7 +5342,18 @@ var OwneySDK = class {
3649
5342
  assetInfo.decimals
3650
5343
  );
3651
5344
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
3652
- if (totalAvailable < requested) {
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) {
3653
5357
  throw new OwneyError(
3654
5358
  "WITHDRAW_INSUFFICIENT_BALANCE",
3655
5359
  `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
@@ -3660,6 +5364,7 @@ var OwneySDK = class {
3660
5364
  }
3661
5365
  );
3662
5366
  }
5367
+ const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
3663
5368
  const disabledBalances = balances.filter(
3664
5369
  (b) => this.isAgentDisabled(b.agent.id)
3665
5370
  );
@@ -3668,7 +5373,7 @@ var OwneySDK = class {
3668
5373
  );
3669
5374
  const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
3670
5375
  disabledBalances,
3671
- requested
5376
+ plannedTarget
3672
5377
  );
3673
5378
  const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
3674
5379
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
@@ -3678,7 +5383,9 @@ var OwneySDK = class {
3678
5383
  }));
3679
5384
  const plans = [...disabledPlans, ...enabledPlans];
3680
5385
  const results = {};
3681
- const agentErrors = {};
5386
+ const agentErrors = {
5387
+ ...aggregated.agentErrors ?? {}
5388
+ };
3682
5389
  for (let i = 0; i < plans.length; i++) {
3683
5390
  const p = plans[i];
3684
5391
  if (p.planned === 0n) continue;
@@ -3725,7 +5432,8 @@ var OwneySDK = class {
3725
5432
  requested: amount,
3726
5433
  withdrawn: withdrawn.toString(),
3727
5434
  partialResults: results,
3728
- agentErrors
5435
+ agentErrors,
5436
+ ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
3729
5437
  }
3730
5438
  );
3731
5439
  }
@@ -3743,7 +5451,10 @@ var OwneySDK = class {
3743
5451
  if (agentId) {
3744
5452
  const agent = this.getAgent(agentId);
3745
5453
  const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
3746
- return result;
5454
+ return {
5455
+ ...result,
5456
+ balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
5457
+ };
3747
5458
  }
3748
5459
  let totalBalance = 0;
3749
5460
  const results = {};
@@ -3751,7 +5462,13 @@ var OwneySDK = class {
3751
5462
  const balanceResults = await Promise.allSettled(
3752
5463
  entries.map(async ([id, agent]) => {
3753
5464
  const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
3754
- return [id, b];
5465
+ return [
5466
+ id,
5467
+ {
5468
+ ...b,
5469
+ balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
5470
+ }
5471
+ ];
3755
5472
  })
3756
5473
  );
3757
5474
  let successCount = 0;
@@ -3773,6 +5490,7 @@ var OwneySDK = class {
3773
5490
  const retryDelay = rateLimitDelay(reason);
3774
5491
  if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
3775
5492
  agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
5493
+ console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
3776
5494
  }
3777
5495
  if (successCount === 0) {
3778
5496
  throw new OwneyError(
@@ -3885,7 +5603,10 @@ var OwneySDK = class {
3885
5603
  Promise.all(
3886
5604
  entries.map(async ([id, agent]) => {
3887
5605
  const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
3888
- return [id, Number(b.totalBalance)];
5606
+ return [
5607
+ id,
5608
+ balanceForApyScope(b, chainId, tokenSymbol)
5609
+ ];
3889
5610
  })
3890
5611
  )
3891
5612
  ]);
@@ -3913,10 +5634,12 @@ var OwneySDK = class {
3913
5634
  }
3914
5635
  }
3915
5636
  const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
5637
+ const history = aggregateApyHistory(results, balances);
3916
5638
  return {
3917
5639
  totalApy: String(totalApy),
3918
5640
  agentApy: results,
3919
- apyByChainAndAsset
5641
+ apyByChainAndAsset,
5642
+ history
3920
5643
  };
3921
5644
  }
3922
5645
  /**
@@ -4064,10 +5787,10 @@ var OwneySDK = class {
4064
5787
  );
4065
5788
  }
4066
5789
  const provider = this.requireConnectedProvider();
4067
- const wallet = (0, import_viem6.createWalletClient)({
5790
+ const wallet = (0, import_viem9.createWalletClient)({
4068
5791
  account: state.walletAddress,
4069
5792
  chain: VIEM_CHAIN2[chainId],
4070
- transport: (0, import_viem6.custom)(provider)
5793
+ transport: (0, import_viem9.custom)(provider)
4071
5794
  });
4072
5795
  const hash = await wallet.writeContract({
4073
5796
  address: token,
@@ -4077,9 +5800,9 @@ var OwneySDK = class {
4077
5800
  account: state.walletAddress,
4078
5801
  chain: VIEM_CHAIN2[chainId]
4079
5802
  });
4080
- const publicClient = (0, import_viem6.createPublicClient)({
5803
+ const publicClient = (0, import_viem9.createPublicClient)({
4081
5804
  chain: VIEM_CHAIN2[chainId],
4082
- transport: (0, import_viem6.custom)(provider)
5805
+ transport: (0, import_viem9.custom)(provider)
4083
5806
  });
4084
5807
  const receipt = await publicClient.waitForTransactionReceipt({
4085
5808
  hash,
@@ -4116,7 +5839,9 @@ var OwneySDK = class {
4116
5839
  return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
4117
5840
  }
4118
5841
  const results = {};
4119
- const agentEntries = [...this.agents.entries()];
5842
+ const agentEntries = [...this.agents.entries()].filter(
5843
+ ([id]) => !this.isAgentDisabled(id)
5844
+ );
4120
5845
  const apyResults = await Promise.all(
4121
5846
  agentEntries.map(async ([id, agent]) => {
4122
5847
  const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
@@ -4191,13 +5916,13 @@ var OwneySDK = class {
4191
5916
  };
4192
5917
 
4193
5918
  // src/agents/zyfai/zyfai.siwx.ts
4194
- var import_viem7 = require("viem");
4195
- var import_siwe = require("siwe");
5919
+ var import_viem10 = require("viem");
5920
+ var import_siwe2 = require("siwe");
4196
5921
  var import_sdk2 = require("@zyfai/sdk");
4197
5922
 
4198
5923
  // src/agents/zyfai/zyfai.siwx-cache.ts
4199
- var KEY_PREFIX2 = "owney.siwx.session";
4200
- var storage2 = () => {
5924
+ var KEY_PREFIX4 = "owney.siwx.session";
5925
+ var storage4 = () => {
4201
5926
  if (typeof window === "undefined") return null;
4202
5927
  try {
4203
5928
  return window.localStorage;
@@ -4205,8 +5930,8 @@ var storage2 = () => {
4205
5930
  return null;
4206
5931
  }
4207
5932
  };
4208
- var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
4209
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
5933
+ var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
5934
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
4210
5935
  var memorySiwxSessions = /* @__PURE__ */ new Map();
4211
5936
  var readLegacySiwxSession = (store, address) => {
4212
5937
  if (!store) return null;
@@ -4237,17 +5962,17 @@ var readLegacySiwxSession = (store, address) => {
4237
5962
  };
4238
5963
  var readSiwxSession = (address, chainId) => {
4239
5964
  if (typeof window === "undefined") return null;
4240
- const key2 = buildKey2(address);
4241
- const store = storage2();
4242
- let raw = null;
5965
+ const key2 = buildKey3(address);
5966
+ const store = storage4();
5967
+ let raw2 = null;
4243
5968
  try {
4244
- raw = store?.getItem(key2) ?? null;
5969
+ raw2 = store?.getItem(key2) ?? null;
4245
5970
  } catch {
4246
- raw = null;
5971
+ raw2 = null;
4247
5972
  }
4248
- if (raw) {
5973
+ if (raw2) {
4249
5974
  try {
4250
- return JSON.parse(raw);
5975
+ return JSON.parse(raw2);
4251
5976
  } catch {
4252
5977
  memorySiwxSessions.delete(key2);
4253
5978
  try {
@@ -4266,18 +5991,18 @@ var readSiwxSession = (address, chainId) => {
4266
5991
  };
4267
5992
  var writeSiwxSession = (address, _chainId, session) => {
4268
5993
  if (typeof window === "undefined") return;
4269
- const key2 = buildKey2(address);
5994
+ const key2 = buildKey3(address);
4270
5995
  memorySiwxSessions.set(key2, session);
4271
- const store = storage2();
5996
+ const store = storage4();
4272
5997
  try {
4273
5998
  store?.setItem(key2, JSON.stringify(session));
4274
5999
  } catch {
4275
6000
  }
4276
6001
  };
4277
6002
  var clearSiwxSession = (address, _chainId) => {
4278
- const key2 = buildKey2(address);
6003
+ const key2 = buildKey3(address);
4279
6004
  memorySiwxSessions.delete(key2);
4280
- const store = storage2();
6005
+ const store = storage4();
4281
6006
  try {
4282
6007
  store?.removeItem(key2);
4283
6008
  } catch {
@@ -4317,8 +6042,8 @@ function buildSIWXConfig(deps) {
4317
6042
  statement: STATEMENT,
4318
6043
  issuedAt,
4319
6044
  toString() {
4320
- return new import_siwe.SiweMessage({
4321
- address: (0, import_viem7.getAddress)(accountAddress),
6045
+ return new import_siwe2.SiweMessage({
6046
+ address: (0, import_viem10.getAddress)(accountAddress),
4322
6047
  chainId: numericChainId(chainId),
4323
6048
  domain,
4324
6049
  uri,
@@ -4360,7 +6085,7 @@ function buildSIWXConfig(deps) {
4360
6085
  const persistSession = async (session) => {
4361
6086
  const address = session.data.accountAddress;
4362
6087
  const id = numericChainId(session.data.chainId);
4363
- const message = new import_siwe.SiweMessage(session.message);
6088
+ const message = new import_siwe2.SiweMessage(session.message);
4364
6089
  const login = await post("/auth/login", {
4365
6090
  message,
4366
6091
  signature: session.signature,
@@ -4409,6 +6134,7 @@ function createOwneySIWX(config) {
4409
6134
  NotConnectedError,
4410
6135
  OwneyError,
4411
6136
  OwneySDK,
6137
+ YieldseekerAgent,
4412
6138
  createOwneySIWX,
4413
6139
  setOwneyDebug
4414
6140
  });