@owney/sdk 0.7.25-beta.4 → 0.7.26-beta.0

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,32 +26,12 @@ __export(index_exports, {
26
26
  NotConnectedError: () => NotConnectedError,
27
27
  OwneyError: () => OwneyError,
28
28
  OwneySDK: () => OwneySDK,
29
- YieldseekerAgent: () => YieldseekerAgent,
30
29
  createOwneySIWX: () => createOwneySIWX,
30
+ listPendingSwaps: () => listOrders,
31
31
  setOwneyDebug: () => setOwneyDebug
32
32
  });
33
33
  module.exports = __toCommonJS(index_exports);
34
34
 
35
- // src/lib/deposit-batch-callback.ts
36
- var batchCallbacks = /* @__PURE__ */ new WeakMap();
37
- var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
38
- function toBatchTransfer(to, amount, verification) {
39
- return {
40
- to,
41
- amount,
42
- ...verification ? {
43
- yieldseeker: {
44
- signature: verification.signature,
45
- userId: verification.userId,
46
- agentId: verification.yieldseekerAgentId
47
- }
48
- } : {}
49
- };
50
- }
51
- function registerDepositBatch(callback, transfer) {
52
- batchCallbacks.set(callback, transfer);
53
- }
54
-
55
35
  // src/errors.ts
56
36
  var OwneyError = class extends Error {
57
37
  code;
@@ -331,18 +311,18 @@ function tokenDecimals(symbol, explicit) {
331
311
  return explicit;
332
312
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
333
313
  }
334
- function mapDeposit(raw2) {
314
+ function mapDeposit(raw) {
335
315
  return {
336
- txHash: raw2.txHash,
337
- smartWallet: raw2.smartWallet,
338
- amount: raw2.amount
316
+ txHash: raw.txHash,
317
+ smartWallet: raw.smartWallet,
318
+ amount: raw.amount
339
319
  };
340
320
  }
341
- function mapWithdraw(raw2) {
321
+ function mapWithdraw(raw) {
342
322
  return {
343
- txHash: raw2.txHash,
344
- type: raw2.type,
345
- amount: raw2.amount
323
+ txHash: raw.txHash,
324
+ type: raw.type,
325
+ amount: raw.amount
346
326
  };
347
327
  }
348
328
  var CHAIN_ID_TO_NAME = {
@@ -361,10 +341,10 @@ function resolveChainId(chain) {
361
341
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
362
342
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
363
343
  }
364
- function mapPendingAllocations(raw2) {
365
- if (!Array.isArray(raw2)) return void 0;
344
+ function mapPendingAllocations(raw) {
345
+ if (!Array.isArray(raw)) return void 0;
366
346
  const pending = [];
367
- for (const entry of raw2) {
347
+ for (const entry of raw) {
368
348
  if (typeof entry !== "object" || entry === null) continue;
369
349
  const e = entry;
370
350
  if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
@@ -387,8 +367,8 @@ function mapPendingAllocations(raw2) {
387
367
  }
388
368
  return pending.length > 0 ? pending : void 0;
389
369
  }
390
- function mapBalances(raw2, _chainId, smartWallet) {
391
- const portfolio = raw2.portfolio;
370
+ function mapBalances(raw, _chainId, smartWallet) {
371
+ const portfolio = raw.portfolio;
392
372
  const portfolioByChain = portfolio.portfolioByChain ?? {};
393
373
  let totalBalance = 0;
394
374
  const tokens = [];
@@ -457,8 +437,8 @@ function sumTokenValues(tokens) {
457
437
  function sumTokenEarnings(tokens) {
458
438
  return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
459
439
  }
460
- function mapEarnings(raw2, smartWallet) {
461
- const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
440
+ function mapEarnings(raw, smartWallet) {
441
+ const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
462
442
  const tokens = [];
463
443
  for (const [chainIdKey, tokensBySymbol] of Object.entries(
464
444
  totalEarningsByChain
@@ -477,15 +457,15 @@ function mapEarnings(raw2, smartWallet) {
477
457
  return {
478
458
  smartWallet,
479
459
  lifetimeEarnings: sumTokenEarnings(
480
- raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
460
+ raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
481
461
  ),
482
462
  tokens
483
463
  };
484
464
  }
485
- function mapWeightedApyByChain(raw2) {
486
- if (!raw2) return void 0;
465
+ function mapWeightedApyByChain(raw) {
466
+ if (!raw) return void 0;
487
467
  const out = {};
488
- for (const [chainKey, tokenApy] of Object.entries(raw2)) {
468
+ for (const [chainKey, tokenApy] of Object.entries(raw)) {
489
469
  const chainId = Number(chainKey);
490
470
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
491
471
  const perAsset = {};
@@ -525,8 +505,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
525
505
  }
526
506
  return totalBalance > 0 ? weightedSum / totalBalance : null;
527
507
  }
528
- function mapApyHistory(raw2, chainId, tokenSymbol) {
529
- const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
508
+ function mapApyHistory(raw, chainId, tokenSymbol) {
509
+ const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
530
510
  date,
531
511
  apy: rawPoolApyForChain(entry, chainId, tokenSymbol),
532
512
  // Provider position balances are treated as decimal amounts of the
@@ -542,9 +522,9 @@ function mapApyHistory(raw2, chainId, tokenSymbol) {
542
522
  } : {}
543
523
  })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
544
524
  return {
545
- walletAddress: raw2.walletAddress,
546
- weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
547
- apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
525
+ walletAddress: raw.walletAddress,
526
+ weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
527
+ apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
548
528
  history
549
529
  };
550
530
  }
@@ -640,23 +620,23 @@ function mapEntries(rawEntries, chainId) {
640
620
  };
641
621
  });
642
622
  }
643
- function mapUserProfile(raw2, userAddress) {
623
+ function mapUserProfile(raw, userAddress) {
644
624
  return {
645
625
  address: userAddress,
646
- smartWallet: raw2.smartWallet || "",
647
- chains: raw2.chains || [],
648
- strategy: raw2.strategy,
649
- hasActiveSessionKey: raw2.hasActiveSessionKey || false,
650
- protocols: raw2.protocols || [],
651
- splitting: raw2.splitting,
652
- minSplits: raw2.minSplits
626
+ smartWallet: raw.smartWallet || "",
627
+ chains: raw.chains || [],
628
+ strategy: raw.strategy,
629
+ hasActiveSessionKey: raw.hasActiveSessionKey || false,
630
+ protocols: raw.protocols || [],
631
+ splitting: raw.splitting,
632
+ minSplits: raw.minSplits
653
633
  };
654
634
  }
655
- function mapApyByStrategy(raw2) {
635
+ function mapApyByStrategy(raw) {
656
636
  const apyPerAsset = {};
657
637
  let apySum = 0;
658
638
  let apyCount = 0;
659
- for (const entry of raw2.data) {
639
+ for (const entry of raw.data) {
660
640
  const supported = SupportedAssets.find(
661
641
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
662
642
  );
@@ -768,9 +748,9 @@ function netDeltaForSnapshot(entry, chainId, asset) {
768
748
  debugLog("zyfai:earnings", "gross fallback for daily earnings", { gross });
769
749
  return gross;
770
750
  }
771
- function mapDailyEarnings(raw2, chainId, tokenSymbol) {
751
+ function mapDailyEarnings(raw, chainId, tokenSymbol) {
772
752
  const wanted = tokenSymbol?.toUpperCase();
773
- const snapshots = [...raw2.data ?? []].sort(
753
+ const snapshots = [...raw.data ?? []].sort(
774
754
  (a, b) => a.snapshot_date.localeCompare(b.snapshot_date)
775
755
  );
776
756
  const byAsset = /* @__PURE__ */ new Map();
@@ -785,7 +765,7 @@ function mapDailyEarnings(raw2, chainId, tokenSymbol) {
785
765
  }
786
766
  }
787
767
  const assets = [...byAsset.entries()].map(([asset, series]) => ({ asset, points: series.points })).sort((a, b) => a.asset.localeCompare(b.asset));
788
- return { walletAddress: raw2.walletAddress, chainId, assets };
768
+ return { walletAddress: raw.walletAddress, chainId, assets };
789
769
  }
790
770
 
791
771
  // src/agents/zyfai/zyfai.withdraw-amount.ts
@@ -896,15 +876,15 @@ var readSession = (address, _chainId) => {
896
876
  if (typeof window === "undefined") return null;
897
877
  const key2 = buildKey(address);
898
878
  const store = storage();
899
- let raw2 = null;
879
+ let raw = null;
900
880
  try {
901
- raw2 = store?.getItem(key2) ?? null;
881
+ raw = store?.getItem(key2) ?? null;
902
882
  } catch {
903
- raw2 = null;
883
+ raw = null;
904
884
  }
905
- if (raw2) {
885
+ if (raw) {
906
886
  try {
907
- const parsed = JSON.parse(raw2);
887
+ const parsed = JSON.parse(raw);
908
888
  if (isFreshSession(parsed)) return parsed;
909
889
  } catch {
910
890
  }
@@ -1072,8 +1052,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
1072
1052
  }
1073
1053
  return result;
1074
1054
  }
1075
- function flattenAvailablePools(raw2) {
1076
- const byChain = raw2 ?? {};
1055
+ function flattenAvailablePools(raw) {
1056
+ const byChain = raw ?? {};
1077
1057
  const names = [];
1078
1058
  for (const byToken of Object.values(byChain ?? {})) {
1079
1059
  for (const entry of Object.values(byToken ?? {})) {
@@ -1576,8 +1556,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1576
1556
  const poolResults = await Promise.all(
1577
1557
  universe.map(async (protocol) => {
1578
1558
  try {
1579
- const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
1580
- return [protocol.id, flattenAvailablePools(raw2)];
1559
+ const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1560
+ return [protocol.id, flattenAvailablePools(raw)];
1581
1561
  } catch (error) {
1582
1562
  console.warn(
1583
1563
  `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
@@ -1643,14 +1623,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1643
1623
  async readWalletState(ownerAddress) {
1644
1624
  try {
1645
1625
  const { portfolio } = await this.sdk.getPositions(ownerAddress);
1646
- const raw2 = portfolio;
1626
+ const raw = portfolio;
1647
1627
  debugLog("zyfai:onboard", "wallet state from getPositions", {
1648
- predeployed: raw2?.predeployed,
1649
- hasActiveSessionKey: raw2?.hasActiveSessionKey
1628
+ predeployed: raw?.predeployed,
1629
+ hasActiveSessionKey: raw?.hasActiveSessionKey
1650
1630
  });
1651
1631
  return {
1652
- predeployed: raw2?.predeployed,
1653
- hasActiveSessionKey: raw2?.hasActiveSessionKey
1632
+ predeployed: raw?.predeployed,
1633
+ hasActiveSessionKey: raw?.hasActiveSessionKey
1654
1634
  };
1655
1635
  } catch (error) {
1656
1636
  console.warn(
@@ -1936,14 +1916,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1936
1916
  return { txHash, smartWallet, amount };
1937
1917
  }
1938
1918
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
1939
- const raw2 = await this.sdk.depositFunds(
1919
+ const raw = await this.sdk.depositFunds(
1940
1920
  this.getAddress(),
1941
1921
  validChainId,
1942
1922
  amount,
1943
1923
  asset,
1944
1924
  "aggressive"
1945
1925
  );
1946
- return mapDeposit(raw2);
1926
+ return mapDeposit(raw);
1947
1927
  } catch (error) {
1948
1928
  throw error;
1949
1929
  }
@@ -1952,27 +1932,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
1952
1932
  async withdraw(state, chainId, token, amount) {
1953
1933
  const validChainId = isValidChainId(chainId);
1954
1934
  await this.ensureConnected(state, validChainId);
1955
- const raw2 = await this.sdk.withdrawFunds(
1935
+ const raw = await this.sdk.withdrawFunds(
1956
1936
  this.getAddress(),
1957
1937
  validChainId,
1958
1938
  amount,
1959
1939
  token
1960
1940
  );
1961
- if (!raw2.success) {
1941
+ if (!raw.success) {
1962
1942
  throw new OwneyError(
1963
1943
  "WITHDRAW_FAILED",
1964
- raw2.message || "Zyfai withdraw failed.",
1965
- { chainId: validChainId, token, amount, response: raw2 },
1944
+ raw.message || "Zyfai withdraw failed.",
1945
+ { chainId: validChainId, token, amount, response: raw },
1966
1946
  this.id
1967
1947
  );
1968
1948
  }
1969
- return mapWithdraw(raw2);
1949
+ return mapWithdraw(raw);
1970
1950
  }
1971
1951
  // --- IAgent: Portfolio reads ---
1972
1952
  async getBalances(state, chainId) {
1973
1953
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
1974
- const raw2 = await this.sdk.getPortfolio(this.getAddress());
1975
- return mapBalances(raw2, validChainId, smartWallet);
1954
+ const raw = await this.sdk.getPortfolio(this.getAddress());
1955
+ return mapBalances(raw, validChainId, smartWallet);
1976
1956
  }
1977
1957
  earningsKey(state, chainId, smartWallet) {
1978
1958
  return JSON.stringify([
@@ -1985,11 +1965,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
1985
1965
  const existing = this.earningsReads.get(key2);
1986
1966
  if (existing) return existing;
1987
1967
  const generation = this.earningsGeneration;
1988
- const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw2) => {
1968
+ const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw) => {
1989
1969
  if (generation === this.earningsGeneration) {
1990
- this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
1970
+ this.earningsSnapshot = { key: key2, raw, at: Date.now() };
1991
1971
  }
1992
- return raw2;
1972
+ return raw;
1993
1973
  }).finally(() => {
1994
1974
  if (this.earningsReads.get(key2) === pending)
1995
1975
  this.earningsReads.delete(key2);
@@ -1999,11 +1979,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
1999
1979
  }
2000
1980
  async getEarnings(state, chainId) {
2001
1981
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
2002
- const raw2 = await this.readEarnings(
1982
+ const raw = await this.readEarnings(
2003
1983
  this.earningsKey(state, chainId, smartWallet),
2004
1984
  smartWallet
2005
1985
  );
2006
- return mapEarnings(raw2, smartWallet);
1986
+ return mapEarnings(raw, smartWallet);
2007
1987
  }
2008
1988
  async refreshEarnings(state, chainId) {
2009
1989
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -2030,17 +2010,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
2030
2010
  }
2031
2011
  async getAccountApy(state, chainId, days, tokenSymbol) {
2032
2012
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
2033
- const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
2034
- return mapApyHistory(raw2, chainId, tokenSymbol);
2013
+ const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
2014
+ return mapApyHistory(raw, chainId, tokenSymbol);
2035
2015
  }
2036
2016
  async getDailyEarnings(state, chainId, days, tokenSymbol) {
2037
2017
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
2038
2018
  const start = new Date(Date.now() - (DayFilterMapping[days] + 1) * 864e5);
2039
- const raw2 = await this.sdk.getDailyEarnings(
2019
+ const raw = await this.sdk.getDailyEarnings(
2040
2020
  smartWallet,
2041
2021
  start.toISOString().slice(0, 10)
2042
2022
  );
2043
- return mapDailyEarnings(raw2, chainId, tokenSymbol);
2023
+ return mapDailyEarnings(raw, chainId, tokenSymbol);
2044
2024
  }
2045
2025
  /**
2046
2026
  * Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
@@ -2075,7 +2055,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2075
2055
  const matched = [];
2076
2056
  let backendExhausted = false;
2077
2057
  for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
2078
- const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
2058
+ const raw = await this.sdk.getHistory(smartWallet, validChainId, {
2079
2059
  limit: backendPageSize,
2080
2060
  offset,
2081
2061
  fromDate: options?.fromDate,
@@ -2086,13 +2066,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
2086
2066
  // asset's rows and handing back a page that filters to nothing.
2087
2067
  assetType
2088
2068
  });
2089
- raw2.data.forEach((entry, idx) => {
2069
+ raw.data.forEach((entry, idx) => {
2090
2070
  if (entry.chainId === validChainId) {
2091
2071
  matched.push({ entry, rawIdx: offset + idx });
2092
2072
  }
2093
2073
  });
2094
- offset += raw2.data.length;
2095
- if (raw2.data.length < backendPageSize) {
2074
+ offset += raw.data.length;
2075
+ if (raw.data.length < backendPageSize) {
2096
2076
  backendExhausted = true;
2097
2077
  break;
2098
2078
  }
@@ -2114,18 +2094,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
2114
2094
  }
2115
2095
  async getUserProfile(state, chainId) {
2116
2096
  await this.connectAuth(state, chainId);
2117
- const raw2 = await this.sdk.getUserDetails();
2097
+ const raw = await this.sdk.getUserDetails();
2118
2098
  debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
2119
2099
  asset: "USDC (default \u2014 no asset passed)",
2120
- splitting: raw2.splitting,
2121
- minSplits: raw2.minSplits,
2122
- strategy: raw2.strategy,
2123
- chains: raw2.chains,
2124
- protocolCount: raw2.protocols?.length,
2125
- hasActiveSessionKey: raw2.hasActiveSessionKey,
2126
- smartWallet: raw2.smartWallet
2100
+ splitting: raw.splitting,
2101
+ minSplits: raw.minSplits,
2102
+ strategy: raw.strategy,
2103
+ chains: raw.chains,
2104
+ protocolCount: raw.protocols?.length,
2105
+ hasActiveSessionKey: raw.hasActiveSessionKey,
2106
+ smartWallet: raw.smartWallet
2127
2107
  });
2128
- return mapUserProfile(raw2, this.connectedAddress);
2108
+ return mapUserProfile(raw, this.connectedAddress);
2129
2109
  }
2130
2110
  async ensureAutoSelectProtocols(state, chainId, asset) {
2131
2111
  await this.connectAuth(state, chainId);
@@ -2144,20 +2124,80 @@ var ZyfaiAgent = class _ZyfaiAgent {
2144
2124
  }
2145
2125
  // --- IAgent: Discovery (no wallet required) ---
2146
2126
  async getAgentApy(days, options) {
2147
- const raw2 = await this.sdk.getAPYPerStrategy(
2127
+ const raw = await this.sdk.getAPYPerStrategy(
2148
2128
  false,
2149
2129
  DayFilterMapping[days],
2150
2130
  "aggressive",
2151
2131
  options?.chainId,
2152
2132
  options?.tokenSymbol
2153
2133
  );
2154
- return mapApyByStrategy(raw2);
2134
+ return mapApyByStrategy(raw);
2155
2135
  }
2156
2136
  };
2157
2137
 
2158
- // src/agents/yieldseeker/yieldseeker.agent.ts
2159
- var import_viem6 = require("viem");
2160
- var import_chains3 = require("viem/chains");
2138
+ // src/lib/routing-api.ts
2139
+ var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2140
+ async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL) {
2141
+ const url = `${baseUrl}/api/v1/agent/org-config`;
2142
+ try {
2143
+ const res = await fetch(url, {
2144
+ method: "GET",
2145
+ headers: {
2146
+ "Content-Type": "application/json",
2147
+ "x-owney-api-key": `${apiKey}`
2148
+ }
2149
+ });
2150
+ if (!res.ok) {
2151
+ if (res.status !== 404) {
2152
+ console.warn(
2153
+ `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
2154
+ );
2155
+ }
2156
+ return null;
2157
+ }
2158
+ const json = await res.json();
2159
+ const policy = json.success ? json.data ?? null : null;
2160
+ debugLog(
2161
+ "owney-sdk",
2162
+ policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
2163
+ policy ?? void 0
2164
+ );
2165
+ return policy;
2166
+ } catch (error) {
2167
+ console.warn(
2168
+ "[owney-sdk] Could not read org agent config (non-fatal):",
2169
+ error instanceof Error ? error.message : String(error)
2170
+ );
2171
+ return null;
2172
+ }
2173
+ }
2174
+ async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
2175
+ const url = `${baseUrl}/api/v1/agent/keys`;
2176
+ const res = await fetch(url, {
2177
+ method: "GET",
2178
+ headers: {
2179
+ "Content-Type": "application/json",
2180
+ "x-owney-api-key": `${apiKey}`
2181
+ }
2182
+ });
2183
+ if (!res.ok) {
2184
+ const text = await res.text().catch(() => "");
2185
+ throw new OwneyError(
2186
+ "API_ROUTING_ERROR",
2187
+ `Routing API error ${res.status}: ${text}`,
2188
+ { statusCode: res.status, responseBody: text }
2189
+ );
2190
+ }
2191
+ const json = await res.json();
2192
+ if (!json.success) {
2193
+ throw new OwneyError(
2194
+ "API_ROUTING_FAILED",
2195
+ `Routing API request failed: ${json.message}`,
2196
+ { message: json.message }
2197
+ );
2198
+ }
2199
+ return json.data;
2200
+ }
2161
2201
 
2162
2202
  // src/lib/chain-guard.ts
2163
2203
  var CHAIN_NAMES = {
@@ -2194,132 +2234,167 @@ async function ensureWalletOnChain(pub, wallet, expected) {
2194
2234
  }
2195
2235
  }
2196
2236
 
2197
- // src/lib/transfer-auth.ts
2198
- var import_viem2 = require("viem");
2199
-
2200
- // src/lib/sponsor-client.ts
2201
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2202
- async function postPaymasterIntent(input) {
2203
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2204
- let res;
2205
- try {
2206
- res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
2207
- method: "POST",
2208
- headers: {
2209
- "content-type": "application/json",
2210
- "x-owney-api-key": input.apiKey,
2211
- Authorization: `Signature ${input.yieldseekerSignature}`
2212
- },
2213
- body: JSON.stringify(input.body)
2214
- });
2215
- } catch (networkError) {
2237
+ // src/lib/swap/swap-api.ts
2238
+ async function request(baseUrl, apiKey, path, init) {
2239
+ const url = `${baseUrl}/api/v1/swap${path}`;
2240
+ const res = await fetch(url, {
2241
+ method: init?.method ?? "GET",
2242
+ headers: {
2243
+ "Content-Type": "application/json",
2244
+ "x-owney-api-key": apiKey
2245
+ },
2246
+ ...init ? { body: JSON.stringify(init.body) } : {}
2247
+ });
2248
+ if (!res.ok) {
2249
+ const text = await res.text().catch(() => "");
2250
+ if (res.status === 429) {
2251
+ throw new OwneyError(
2252
+ "SWAP_RATE_LIMITED",
2253
+ "Swap provider is rate limiting, retry shortly",
2254
+ { statusCode: res.status }
2255
+ );
2256
+ }
2257
+ if (res.status === 403) {
2258
+ throw new OwneyError(
2259
+ "SWAP_DISABLED",
2260
+ "Swap is not enabled for this organization",
2261
+ { statusCode: res.status }
2262
+ );
2263
+ }
2216
2264
  throw new OwneyError(
2217
- "SPONSOR_REQUEST_FAILED",
2218
- `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2219
- { cause: String(networkError), safeToFallback: true }
2265
+ "SWAP_REQUEST_FAILED",
2266
+ `Swap API error ${res.status}: ${text}`,
2267
+ { statusCode: res.status, responseBody: text }
2220
2268
  );
2221
2269
  }
2222
- const text = await res.text();
2223
- let parsed = null;
2224
- try {
2225
- parsed = JSON.parse(text);
2226
- } catch {
2227
- }
2228
- if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
2270
+ const json = await res.json();
2271
+ if (!json.success) {
2229
2272
  throw new OwneyError(
2230
- "SPONSOR_REQUEST_FAILED",
2231
- `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2232
- {
2233
- statusCode: res.status,
2234
- responseBody: text.slice(0, 500),
2235
- safeToFallback: true
2236
- }
2273
+ "SWAP_REQUEST_FAILED",
2274
+ `Swap API request failed: ${json.message ?? "unknown error"}`,
2275
+ { message: json.message }
2237
2276
  );
2238
2277
  }
2239
- return parsed.data;
2278
+ return json.data;
2240
2279
  }
2241
- async function getSponsorRelayerAddress(input) {
2242
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2243
- let res;
2244
- try {
2245
- res = await fetch(
2246
- `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2247
- {
2248
- headers: { "x-owney-api-key": input.apiKey }
2249
- }
2250
- );
2251
- } catch (networkError) {
2252
- throw new OwneyError(
2253
- "SPONSOR_REQUEST_FAILED",
2254
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2255
- { cause: String(networkError), safeToFallback: true }
2256
- );
2257
- }
2258
- const text = await res.text();
2259
- let parsed = null;
2260
- try {
2261
- parsed = JSON.parse(text);
2262
- } catch {
2263
- }
2264
- if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
2265
- throw new OwneyError(
2266
- "SPONSOR_REQUEST_FAILED",
2267
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2268
- {
2269
- statusCode: res.status,
2270
- responseBody: text.slice(0, 500),
2271
- safeToFallback: true
2280
+ function createSwapApi(baseUrl, apiKey) {
2281
+ return {
2282
+ /** Source assets the user may pay with, and each chain's deposit targets. */
2283
+ listTokens: () => request(baseUrl, apiKey, "/tokens"),
2284
+ /**
2285
+ * Search one chain's payable-with assets by name, symbol or address.
2286
+ *
2287
+ * Separate from `listTokens` on purpose: the full set is thousands of
2288
+ * tokens per chain, far too much to ship to a picker, so the short list
2289
+ * renders immediately and this reaches everything else on demand.
2290
+ *
2291
+ * Already filtered server-side to what a quote will accept, so anything
2292
+ * returned here can be paid with.
2293
+ */
2294
+ searchTokens: (params) => request(
2295
+ baseUrl,
2296
+ apiKey,
2297
+ `/tokens/search?${new URLSearchParams({
2298
+ chainId: String(params.chainId),
2299
+ query: params.query,
2300
+ ...params.limit === void 0 ? {} : { limit: String(params.limit) }
2301
+ }).toString()}`
2302
+ ),
2303
+ /**
2304
+ * `walletAddress` is required even though the routing API could not infer
2305
+ * it: the Fusion+ quoter binds a quote to whoever will sign the order and
2306
+ * rejects the request without it.
2307
+ */
2308
+ quote: (params) => request(baseUrl, apiKey, "/quote", {
2309
+ method: "POST",
2310
+ body: {
2311
+ srcChainId: params.from.chainId,
2312
+ srcSymbol: params.from.symbol,
2313
+ dstChainId: params.to.chainId,
2314
+ dstSymbol: params.to.symbol,
2315
+ amount: params.from.amount,
2316
+ walletAddress: params.walletAddress,
2317
+ ...params.direction ? { direction: params.direction } : {}
2272
2318
  }
2273
- );
2274
- }
2275
- return parsed.data.relayer;
2276
- }
2277
- async function postSponsorBatchTransfer(input) {
2278
- let res;
2279
- try {
2280
- res = await fetch(
2281
- `${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
2282
- {
2283
- method: "POST",
2284
- headers: {
2285
- "content-type": "application/json",
2286
- "x-owney-api-key": input.apiKey
2287
- },
2288
- body: JSON.stringify(input.body)
2319
+ }),
2320
+ /** Ready-to-send calldata for a same-chain swap. */
2321
+ swapTx: (params) => request(baseUrl, apiKey, "/tx", {
2322
+ method: "POST",
2323
+ body: {
2324
+ srcChainId: params.from.chainId,
2325
+ srcSymbol: params.from.symbol,
2326
+ dstChainId: params.to.chainId,
2327
+ dstSymbol: params.to.symbol,
2328
+ amount: params.from.amount,
2329
+ walletAddress: params.walletAddress,
2330
+ slippage: params.slippage,
2331
+ ...params.direction ? { direction: params.direction } : {}
2289
2332
  }
2290
- );
2291
- } catch {
2292
- throw new OwneyError(
2293
- "SPONSOR_REQUEST_FAILED",
2294
- "Deposit status is unknown. Retry the same amount to check it.",
2295
- { safeToFallback: false }
2296
- );
2297
- }
2298
- const parsed = await res.json().catch(() => null);
2299
- if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
2300
- throw new OwneyError(
2301
- "SPONSOR_REQUEST_FAILED",
2302
- "Deposit could not be confirmed. Retry the same amount to check its status.",
2303
- {
2304
- statusCode: res.status,
2305
- safeToFallback: false,
2306
- notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
2333
+ }),
2334
+ /**
2335
+ * Builds a Fusion+ order server-side and returns EIP-712 typed data.
2336
+ *
2337
+ * Only HASHES go over the wire. The preimages never leave the browser —
2338
+ * see swap.secrets.
2339
+ */
2340
+ buildOrder: (params) => request(baseUrl, apiKey, "/order/build", {
2341
+ method: "POST",
2342
+ body: {
2343
+ srcChainId: params.from.chainId,
2344
+ srcSymbol: params.from.symbol,
2345
+ dstChainId: params.to.chainId,
2346
+ dstSymbol: params.to.symbol,
2347
+ amount: params.from.amount,
2348
+ walletAddress: params.walletAddress,
2349
+ secretHashes: params.secretHashes,
2350
+ ...params.direction ? { direction: params.direction } : {},
2351
+ ...params.receiver ? { receiver: params.receiver } : {}
2307
2352
  }
2308
- );
2309
- }
2310
- return parsed.data;
2353
+ }),
2354
+ submitOrder: (body) => request(baseUrl, apiKey, "/order", { method: "POST", body }),
2355
+ /**
2356
+ * Only call once `readyForSecrets` reports the escrow deployed. Publishing
2357
+ * earlier hands a resolver the preimage while the user's funds are locked
2358
+ * and nothing has been posted on the destination chain.
2359
+ */
2360
+ submitSecret: (orderHash, secret) => request(baseUrl, apiKey, "/order/secret", {
2361
+ method: "POST",
2362
+ body: { orderHash, secret }
2363
+ }),
2364
+ orderStatus: (orderHash) => request(baseUrl, apiKey, `/order/${orderHash}`),
2365
+ readyForSecrets: (orderHash) => request(
2366
+ baseUrl,
2367
+ apiKey,
2368
+ `/order/${orderHash}/ready-for-secrets`
2369
+ )
2370
+ };
2371
+ }
2372
+
2373
+ // src/lib/swap/swap.rpc.ts
2374
+ var import_viem2 = require("viem");
2375
+ var DEFAULT_RPC_URLS = {
2376
+ 1: ["https://cloudflare-eth.com", "https://ethereum-rpc.publicnode.com"],
2377
+ 8453: ["https://mainnet.base.org", "https://base-rpc.publicnode.com"],
2378
+ 42161: [
2379
+ "https://arb1.arbitrum.io/rpc",
2380
+ "https://arbitrum-one-rpc.publicnode.com"
2381
+ ]
2382
+ };
2383
+ function swapReadTransport(chainId, overrides) {
2384
+ const override = overrides?.[chainId];
2385
+ if (override) return (0, import_viem2.http)(override);
2386
+ const urls = DEFAULT_RPC_URLS[chainId];
2387
+ if (!urls || urls.length === 0) return (0, import_viem2.http)();
2388
+ return (0, import_viem2.fallback)(urls.map((url) => (0, import_viem2.http)(url)));
2389
+ }
2390
+ function receiptTimeoutMs(chainId) {
2391
+ return chainId === 1 ? 6e5 : 18e4;
2311
2392
  }
2312
2393
 
2313
2394
  // src/lib/permit2.ts
2314
2395
  var import_viem3 = require("viem");
2315
2396
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2316
2397
  var MAX_UINT256 = 2n ** 256n - 1n;
2317
- function permit2ApprovalAmount(requiredAmount) {
2318
- if (requiredAmount <= 0n) {
2319
- throw new Error("Permit2 approval requires a positive deposit amount");
2320
- }
2321
- return MAX_UINT256;
2322
- }
2323
2398
  var ERC20_ALLOWANCE_ABI = [
2324
2399
  {
2325
2400
  type: "function",
@@ -2349,18 +2424,40 @@ var ERC20_ALLOWANCE_ABI = [
2349
2424
  outputs: [{ name: "", type: "uint256" }]
2350
2425
  }
2351
2426
  ];
2427
+ function buildPermitTransferFromTypedData(input) {
2428
+ return {
2429
+ domain: {
2430
+ name: "Permit2",
2431
+ chainId: input.chainId,
2432
+ verifyingContract: PERMIT2_ADDRESS
2433
+ },
2434
+ types: {
2435
+ PermitTransferFrom: [
2436
+ { name: "permitted", type: "TokenPermissions" },
2437
+ { name: "spender", type: "address" },
2438
+ { name: "nonce", type: "uint256" },
2439
+ { name: "deadline", type: "uint256" }
2440
+ ],
2441
+ TokenPermissions: [
2442
+ { name: "token", type: "address" },
2443
+ { name: "amount", type: "uint256" }
2444
+ ]
2445
+ },
2446
+ primaryType: "PermitTransferFrom",
2447
+ message: input.message
2448
+ };
2449
+ }
2352
2450
  function randomPermit2Nonce() {
2353
2451
  const bytes = new Uint8Array(32);
2354
2452
  globalThis.crypto.getRandomValues(bytes);
2355
2453
  return BigInt((0, import_viem3.bytesToHex)(bytes));
2356
2454
  }
2357
- async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
2455
+ async function readPermit2Allowance(publicClient, token, owner) {
2358
2456
  return publicClient.readContract({
2359
2457
  address: token,
2360
2458
  abi: ERC20_ALLOWANCE_ABI,
2361
2459
  functionName: "allowance",
2362
- args: [owner, PERMIT2_ADDRESS],
2363
- ...blockNumber === void 0 ? {} : { blockNumber }
2460
+ args: [owner, PERMIT2_ADDRESS]
2364
2461
  });
2365
2462
  }
2366
2463
  async function readErc20Balance(publicClient, token, owner) {
@@ -2372,39 +2469,122 @@ async function readErc20Balance(publicClient, token, owner) {
2372
2469
  });
2373
2470
  }
2374
2471
 
2375
- // src/lib/sponsored-deposit.ts
2376
- var AUTH_WINDOW_SECONDS = 15 * 60;
2377
- var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
2378
- function provideDepositVerificationContext(callback, context) {
2379
- callback[verificationSetter]?.(context);
2472
+ // src/lib/swap/swap.secrets.ts
2473
+ var import_viem4 = require("viem");
2474
+ var SECRET_BYTES = 32;
2475
+ function randomBytes(length) {
2476
+ const bytes = new Uint8Array(length);
2477
+ const cryptoObj = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
2478
+ if (!cryptoObj?.getRandomValues) {
2479
+ throw new Error(
2480
+ "[owney-sdk] Secure randomness is unavailable, so a swap secret cannot be generated safely."
2481
+ );
2482
+ }
2483
+ cryptoObj.getRandomValues(bytes);
2484
+ return bytes;
2380
2485
  }
2381
- function makeVerificationAwareDepositCallback(implementation) {
2382
- let nextVerification;
2383
- const callback = async (smartWallet, chainId, amount) => {
2384
- const verification = nextVerification;
2385
- nextVerification = void 0;
2386
- return implementation(smartWallet, chainId, amount, verification);
2387
- };
2388
- Object.defineProperty(callback, verificationSetter, {
2389
- value: (context) => {
2390
- nextVerification = context;
2391
- }
2392
- });
2393
- return callback;
2486
+ function mintSecrets(count) {
2487
+ if (!Number.isInteger(count) || count < 1) {
2488
+ throw new Error(
2489
+ `[owney-sdk] A swap needs at least one secret, got ${String(count)}.`
2490
+ );
2491
+ }
2492
+ const secrets = [];
2493
+ const secretHashes = [];
2494
+ for (let i = 0; i < count; i++) {
2495
+ const secret = (0, import_viem4.toHex)(randomBytes(SECRET_BYTES));
2496
+ secrets.push(secret);
2497
+ secretHashes.push((0, import_viem4.keccak256)(secret));
2498
+ }
2499
+ return { secrets, secretHashes };
2394
2500
  }
2395
2501
 
2396
- // src/agents/yieldseeker/yieldseeker.auth.ts
2397
- var import_siwe = require("siwe");
2398
- var import_viem4 = require("viem");
2399
- var import_chains2 = require("viem/chains");
2400
-
2401
- // src/agents/yieldseeker/yieldseeker.auth-cache.ts
2402
- var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
2403
- var INVALIDATED_KEY_PREFIXES = [
2404
- "owney.yieldseeker.session",
2405
- "owney.yieldseeker.session.v3",
2406
- "owney.yieldseeker.session.v4"
2502
+ // src/lib/swap/swap.types.ts
2503
+ var SWAP_TERMINAL_STATUSES = [
2504
+ "executed",
2505
+ "expired",
2506
+ "cancelled",
2507
+ "refunded"
2407
2508
  ];
2509
+ var isSwapTerminal = (status) => SWAP_TERMINAL_STATUSES.includes(status);
2510
+
2511
+ // src/lib/swap/swap.order-runner.ts
2512
+ var DEFAULT_POLL_MS = 5e3;
2513
+ var MAX_BACKOFF_MS = 3e4;
2514
+ var backoffFor = (failures, base3) => Math.min(base3 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS);
2515
+ var DEFAULT_TIMEOUT_MS = 15 * 60 * 1e3;
2516
+ async function runFusionOrder(deps, options) {
2517
+ const {
2518
+ orderHash,
2519
+ secrets,
2520
+ onStage,
2521
+ pollIntervalMs = DEFAULT_POLL_MS,
2522
+ timeoutMs = DEFAULT_TIMEOUT_MS
2523
+ } = options;
2524
+ const deadline = deps.now() + timeoutMs;
2525
+ let failures = 0;
2526
+ const published = /* @__PURE__ */ new Set();
2527
+ onStage?.("swapping");
2528
+ for (; ; ) {
2529
+ if (deps.now() >= deadline) {
2530
+ throw new OwneyError(
2531
+ "SWAP_REQUEST_FAILED",
2532
+ "Timed out waiting for the swap to settle. It may still complete \u2014 check the order status before retrying.",
2533
+ { orderHash }
2534
+ );
2535
+ }
2536
+ let ready;
2537
+ try {
2538
+ ready = await deps.readyForSecrets(orderHash);
2539
+ } catch {
2540
+ ready = {};
2541
+ }
2542
+ for (const fill of ready.fills ?? []) {
2543
+ if (published.has(fill.idx)) continue;
2544
+ const secret = secrets[fill.idx];
2545
+ if (secret === void 0) {
2546
+ throw new OwneyError(
2547
+ "SWAP_REQUEST_FAILED",
2548
+ `Swap needs a secret for fill ${fill.idx} that this session does not have. The order will refund once its timelock expires.`,
2549
+ { orderHash, fillIndex: fill.idx }
2550
+ );
2551
+ }
2552
+ try {
2553
+ await deps.submitSecret(orderHash, secret);
2554
+ published.add(fill.idx);
2555
+ } catch {
2556
+ failures += 1;
2557
+ }
2558
+ }
2559
+ let status;
2560
+ try {
2561
+ ({ status } = await deps.orderStatus(orderHash));
2562
+ failures = 0;
2563
+ } catch {
2564
+ failures += 1;
2565
+ await deps.sleep(backoffFor(failures, pollIntervalMs));
2566
+ continue;
2567
+ }
2568
+ if (status === "refunding") onStage?.("refunding");
2569
+ if (isSwapTerminal(status)) {
2570
+ if (status === "executed") {
2571
+ onStage?.("swapped");
2572
+ return { status, filled: true };
2573
+ }
2574
+ if (status === "refunded") onStage?.("refunded");
2575
+ throw new OwneyError(
2576
+ status === "refunded" ? "SWAP_ORDER_REFUNDED" : status === "cancelled" ? "SWAP_ORDER_CANCELLED" : "SWAP_ORDER_EXPIRED",
2577
+ status === "refunded" ? "The swap did not complete and your funds have been returned." : "The swap did not complete in time. Your funds will be returned once the timelock expires.",
2578
+ { orderHash, status }
2579
+ );
2580
+ }
2581
+ await deps.sleep(pollIntervalMs);
2582
+ }
2583
+ }
2584
+
2585
+ // src/lib/swap/swap.secret-store.ts
2586
+ var KEY_PREFIX2 = "owney.swap.order";
2587
+ var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
2408
2588
  var storage2 = () => {
2409
2589
  if (typeof window === "undefined") return null;
2410
2590
  try {
@@ -2413,1649 +2593,287 @@ var storage2 = () => {
2413
2593
  return null;
2414
2594
  }
2415
2595
  };
2416
- var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2417
- var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
2418
- (prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
2419
- );
2420
- var clearInvalidatedSessions = (store, address, chainId) => {
2421
- for (const key2 of invalidatedKeys(address, chainId)) {
2422
- memorySessions2.delete(key2);
2423
- try {
2424
- store?.removeItem(key2);
2425
- } catch {
2426
- }
2427
- }
2428
- };
2429
- var memorySessions2 = /* @__PURE__ */ new Map();
2430
- var isValidSession = (session) => {
2431
- if (!session?.token) return false;
2596
+ var keyFor = (orderHash) => `${KEY_PREFIX2}.${orderHash}`;
2597
+ function saveOrder(order) {
2598
+ const store = storage2();
2599
+ if (!store) return;
2432
2600
  try {
2433
- const parsed = JSON.parse(atob(session.token));
2434
- return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
2601
+ store.setItem(keyFor(order.orderHash), JSON.stringify(order));
2435
2602
  } catch {
2436
- return false;
2437
2603
  }
2438
- };
2439
- var readYieldseekerSession = (address, chainId) => {
2440
- if (typeof window === "undefined") return null;
2441
- const key2 = buildKey2(address, chainId);
2604
+ }
2605
+ function clearOrder(orderHash) {
2442
2606
  const store = storage2();
2443
- clearInvalidatedSessions(store, address, chainId);
2444
- let raw2 = null;
2607
+ if (!store) return;
2445
2608
  try {
2446
- raw2 = store?.getItem(key2) ?? null;
2609
+ store.removeItem(keyFor(orderHash));
2447
2610
  } catch {
2448
- raw2 = null;
2449
2611
  }
2450
- if (raw2) {
2451
- try {
2452
- const parsed = JSON.parse(raw2);
2453
- if (isValidSession(parsed)) return parsed.token;
2454
- } catch {
2455
- }
2456
- memorySessions2.delete(key2);
2457
- try {
2458
- store?.removeItem(key2);
2459
- } catch {
2612
+ }
2613
+ function listOrders(now = Date.now()) {
2614
+ const store = storage2();
2615
+ if (!store) return [];
2616
+ const out = [];
2617
+ try {
2618
+ const keys = [];
2619
+ for (let i = 0; i < store.length; i++) {
2620
+ const key2 = store.key(i);
2621
+ if (key2?.startsWith(`${KEY_PREFIX2}.`)) keys.push(key2);
2460
2622
  }
2461
- return null;
2462
- }
2463
- const cached = memorySessions2.get(key2);
2464
- if (isValidSession(cached)) return cached.token;
2465
- if (cached) memorySessions2.delete(key2);
2466
- return null;
2467
- };
2468
- var writeYieldseekerSession = (address, chainId, token) => {
2469
- if (typeof window === "undefined") return;
2470
- const session = { token };
2471
- if (!isValidSession(session)) return;
2472
- const key2 = buildKey2(address, chainId);
2473
- memorySessions2.set(key2, session);
2474
- const store = storage2();
2475
- try {
2476
- store?.setItem(key2, JSON.stringify(session));
2477
- } catch {
2478
- }
2479
- };
2480
- var clearYieldseekerSession = (address, chainId) => {
2481
- const key2 = buildKey2(address, chainId);
2482
- memorySessions2.delete(key2);
2483
- const store = storage2();
2484
- clearInvalidatedSessions(store, address, chainId);
2485
- try {
2486
- store?.removeItem(key2);
2487
- } catch {
2488
- }
2489
- };
2490
-
2491
- // src/agents/yieldseeker/yieldseeker.auth.ts
2492
- function resolveSiweOrigin(override) {
2493
- const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
2494
- if (!origin || origin === "null") {
2495
- throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
2496
- }
2497
- const url = new URL(origin);
2498
- if (url.protocol !== "https:" && url.protocol !== "http:") {
2499
- throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
2500
- }
2501
- if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
2502
- throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
2503
- }
2504
- return url;
2505
- }
2506
- function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2507
- const url = resolveSiweOrigin(dependencies.origin);
2508
- return new import_siwe.SiweMessage({
2509
- scheme: url.protocol.slice(0, -1),
2510
- domain: url.host,
2511
- address: (0, import_viem4.getAddress)(address),
2512
- uri: url.origin,
2513
- version: "1",
2514
- chainId,
2515
- nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
2516
- issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
2517
- }).prepareMessage();
2518
- }
2519
- function encodeYieldseekerAuthToken(token) {
2520
- const bytes = new TextEncoder().encode(JSON.stringify(token));
2521
- let binary = "";
2522
- for (const byte of bytes) binary += String.fromCharCode(byte);
2523
- return btoa(binary);
2524
- }
2525
- var YieldseekerAuth = class {
2526
- constructor(dependencies = {}) {
2527
- this.dependencies = dependencies;
2528
- }
2529
- dependencies;
2530
- tokens = /* @__PURE__ */ new Map();
2531
- pending = /* @__PURE__ */ new Map();
2532
- scopes = /* @__PURE__ */ new Map();
2533
- key(state, chainId) {
2534
- return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
2535
- }
2536
- async getToken(state, chainId) {
2537
- const key2 = this.key(state, chainId);
2538
- const scope = { address: state.walletAddress, chainId };
2539
- this.scopes.set(key2, scope);
2540
- const cached = this.tokens.get(key2);
2541
- if (cached) return cached;
2542
- const persisted = readYieldseekerSession(scope.address, scope.chainId);
2543
- if (persisted && this.matchesOrigin(persisted)) {
2544
- this.tokens.set(key2, persisted);
2545
- return persisted;
2546
- }
2547
- if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
2548
- const inFlight = this.pending.get(key2);
2549
- if (inFlight) return inFlight;
2550
- const request = this.sign(state, chainId).then((token) => {
2551
- if (this.pending.get(key2) !== request) throw new Error("Wallet sign-in session changed. Please try again.");
2552
- this.tokens.set(key2, token);
2553
- writeYieldseekerSession(scope.address, scope.chainId, token);
2554
- return token;
2555
- });
2556
- this.pending.set(key2, request);
2557
- try {
2558
- return await request;
2559
- } finally {
2560
- if (this.pending.get(key2) === request) this.pending.delete(key2);
2561
- }
2562
- }
2563
- async refreshToken(state, chainId, rejectedToken) {
2564
- const key2 = this.key(state, chainId);
2565
- if (this.tokens.get(key2) === rejectedToken) {
2566
- this.tokens.delete(key2);
2567
- clearYieldseekerSession(state.walletAddress, chainId);
2568
- }
2569
- return this.getToken(state, chainId);
2570
- }
2571
- matchesOrigin(token) {
2572
- try {
2573
- const message = new import_siwe.SiweMessage(JSON.parse(atob(token)).message);
2574
- const url = resolveSiweOrigin(this.dependencies.origin);
2575
- return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
2576
- } catch {
2577
- return false;
2578
- }
2579
- }
2580
- clear(state, chainId) {
2581
- if (!state || chainId === void 0) {
2582
- for (const scope of this.scopes.values()) {
2583
- clearYieldseekerSession(scope.address, scope.chainId);
2584
- }
2585
- this.tokens.clear();
2586
- this.pending.clear();
2587
- this.scopes.clear();
2588
- return;
2589
- }
2590
- const key2 = this.key(state, chainId);
2591
- this.tokens.delete(key2);
2592
- this.pending.delete(key2);
2593
- this.scopes.delete(key2);
2594
- clearYieldseekerSession(state.walletAddress, chainId);
2595
- }
2596
- async sign(state, chainId) {
2597
- const account = (0, import_viem4.getAddress)(state.walletAddress);
2598
- const publicClient = (0, import_viem4.createPublicClient)({
2599
- chain: import_chains2.base,
2600
- transport: (0, import_viem4.custom)(state.provider)
2601
- });
2602
- const walletClient = (0, import_viem4.createWalletClient)({
2603
- account,
2604
- chain: import_chains2.base,
2605
- transport: (0, import_viem4.custom)(state.provider)
2606
- });
2607
- await ensureWalletOnChain(
2608
- publicClient,
2609
- walletClient,
2610
- 8453
2611
- );
2612
- const message = createYieldseekerSiweMessage(
2613
- account,
2614
- chainId,
2615
- this.dependencies
2616
- );
2617
- const signature = await walletClient.signMessage({ account, message });
2618
- return encodeYieldseekerAuthToken({ message, signature });
2619
- }
2620
- };
2621
-
2622
- // src/agents/yieldseeker/yieldseeker.identity-cache.ts
2623
- var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
2624
- var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
2625
- var memoryIdentities = /* @__PURE__ */ new Map();
2626
- var storage3 = () => {
2627
- if (typeof window === "undefined") return null;
2628
- try {
2629
- return window.localStorage;
2630
- } catch {
2631
- return null;
2632
- }
2633
- };
2634
- var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
2635
- function valid(value, walletAddress, chainId, now) {
2636
- return Boolean(
2637
- 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
2638
- );
2639
- }
2640
- function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
2641
- if (typeof window === "undefined") return null;
2642
- const key2 = keyFor(walletAddress, chainId);
2643
- const store = storage3();
2644
- let parsed = null;
2645
- try {
2646
- const raw2 = store?.getItem(key2);
2647
- parsed = raw2 ? JSON.parse(raw2) : null;
2648
- } catch {
2649
- parsed = null;
2650
- }
2651
- const candidate = parsed ?? memoryIdentities.get(key2);
2652
- if (valid(candidate, walletAddress, chainId, now)) {
2653
- memoryIdentities.set(key2, candidate);
2654
- return { userId: candidate.userId };
2655
- }
2656
- memoryIdentities.delete(key2);
2657
- try {
2658
- store?.removeItem(key2);
2659
- } catch {
2660
- }
2661
- return null;
2662
- }
2663
- function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
2664
- if (typeof window === "undefined") return;
2665
- const identity = {
2666
- userId,
2667
- walletAddress,
2668
- chainId,
2669
- expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
2670
- };
2671
- if (!valid(identity, walletAddress, chainId, now)) return;
2672
- const key2 = keyFor(walletAddress, chainId);
2673
- memoryIdentities.set(key2, identity);
2674
- try {
2675
- storage3()?.setItem(key2, JSON.stringify(identity));
2676
- } catch {
2677
- }
2678
- }
2679
- function clearYieldseekerIdentity(walletAddress, chainId) {
2680
- const key2 = keyFor(walletAddress, chainId);
2681
- memoryIdentities.delete(key2);
2682
- try {
2683
- storage3()?.removeItem(key2);
2684
- } catch {
2685
- }
2686
- }
2687
-
2688
- // src/agents/yieldseeker/yieldseeker.client.ts
2689
- var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2690
- function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
2691
- return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
2692
- }
2693
- var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
2694
- var YieldseekerApiError = class extends Error {
2695
- constructor(status, providerCode, responseFields) {
2696
- super(`Yieldseeker request failed (${status}): ${providerCode}`);
2697
- this.status = status;
2698
- this.providerCode = providerCode;
2699
- this.responseFields = responseFields;
2700
- this.name = "YieldseekerApiError";
2701
- }
2702
- status;
2703
- providerCode;
2704
- responseFields;
2705
- get isAuthenticationError() {
2706
- return this.status === 401 || this.status === 403;
2707
- }
2708
- };
2709
- function providerError(body, fallback) {
2710
- if (!body || typeof body !== "object") return { code: fallback };
2711
- const record = body;
2712
- return {
2713
- code: typeof record.message === "string" ? record.message : fallback,
2714
- fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
2715
- };
2716
- }
2717
- var YieldseekerApiClient = class {
2718
- constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch.bind(globalThis)) {
2719
- this.owneyApiKey = owneyApiKey;
2720
- this.baseUrl = baseUrl;
2721
- this.fetchFn = fetchFn;
2722
- }
2723
- owneyApiKey;
2724
- baseUrl;
2725
- fetchFn;
2726
- async request(path, options = {}) {
2727
- const controller = new AbortController();
2728
- const timer = setTimeout(
2729
- () => controller.abort(),
2730
- options.timeoutMs ?? 15e3
2731
- );
2732
- try {
2733
- const response = await this.fetchFn(`${this.baseUrl}${path}`, {
2734
- method: options.method ?? "GET",
2735
- headers: {
2736
- "Content-Type": "application/json",
2737
- "x-owney-api-key": this.owneyApiKey,
2738
- ...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
2739
- },
2740
- body: options.body ? JSON.stringify(options.body) : void 0,
2741
- signal: controller.signal
2742
- });
2743
- const payload = await response.json().catch(() => null);
2744
- if (!response.ok) {
2745
- const error = providerError(payload, `HTTP_${response.status}`);
2746
- throw new YieldseekerApiError(
2747
- response.status,
2748
- error.code,
2749
- error.fields
2750
- );
2751
- }
2752
- if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
2753
- return payload.data;
2754
- }
2755
- return payload;
2756
- } catch (error) {
2757
- if (error instanceof YieldseekerApiError) throw error;
2758
- if (error instanceof DOMException && error.name === "AbortError") {
2759
- throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
2760
- }
2761
- throw new YieldseekerApiError(0, "NETWORK_ERROR", {
2762
- cause: error instanceof Error ? error.message : String(error)
2763
- });
2764
- } finally {
2765
- clearTimeout(timer);
2766
- }
2767
- }
2768
- };
2769
-
2770
- // src/agents/yieldseeker/yieldseeker.mapper.ts
2771
- var import_viem5 = require("viem");
2772
-
2773
- // src/lib/helpers/snapshot-apy.ts
2774
- var DAY_MS = 864e5;
2775
- function snapshotTime(date) {
2776
- if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return void 0;
2777
- const time = Date.parse(date);
2778
- return Number.isFinite(time) && new Date(time).toISOString().slice(0, 10) === date ? time : void 0;
2779
- }
2780
- function returnFactor(value) {
2781
- if (typeof value !== "number" && typeof value !== "string") return void 0;
2782
- if (typeof value === "string" && value.trim() === "") return void 0;
2783
- const factor = Number(value);
2784
- return Number.isFinite(factor) && factor >= 0 ? factor : void 0;
2785
- }
2786
- function calculateSnapshotApy(snapshots, lookbackDays, now = Date.now()) {
2787
- if (!Number.isFinite(lookbackDays) || lookbackDays <= 0 || !Number.isFinite(now)) {
2788
- return void 0;
2789
- }
2790
- const points = snapshots.flatMap((snapshot) => {
2791
- const time = snapshotTime(snapshot.date);
2792
- return time !== void 0 && time <= now ? [{ snapshot, time }] : [];
2793
- }).sort((a, b) => a.time - b.time);
2794
- const end = points.at(-1);
2795
- if (!end) return void 0;
2796
- const cutoff = end.time - lookbackDays * DAY_MS;
2797
- const start = points.find((point) => point.time >= cutoff);
2798
- const actualDays = (end.time - start.time) / DAY_MS;
2799
- if (actualDays <= 0) return void 0;
2800
- const startFactor = returnFactor(start.snapshot.cumulativeReturnFactor);
2801
- const endFactor = returnFactor(end.snapshot.cumulativeReturnFactor);
2802
- if (startFactor === void 0 || startFactor === 0 || endFactor === void 0) {
2803
- return void 0;
2804
- }
2805
- const periodReturn = endFactor / startFactor - 1;
2806
- const apy = Math.pow(1 + periodReturn, 365 / actualDays) - 1;
2807
- return Number.isFinite(apy) ? apy : void 0;
2808
- }
2809
-
2810
- // src/agents/yieldseeker/yieldseeker.types.ts
2811
- var YIELDSEEKER_ASSET_METADATA = {
2812
- USDC: {
2813
- address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2814
- decimals: 6
2815
- },
2816
- WETH: {
2817
- address: "0x4200000000000000000000000000000000000006",
2818
- decimals: 18
2819
- }
2820
- };
2821
-
2822
- // src/agents/yieldseeker/yieldseeker.mapper.ts
2823
- function invalid(endpoint, detail) {
2824
- throw new OwneyError(
2825
- "AGENT_INVALID_RESPONSE",
2826
- `Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
2827
- { endpoint, detail },
2828
- "yieldseeker"
2829
- );
2830
- }
2831
- function raw(value, endpoint) {
2832
- if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
2833
- return invalid(endpoint, "expected a base-10 integer string");
2834
- }
2835
- return BigInt(value);
2836
- }
2837
- function decimal(value, decimals, endpoint) {
2838
- return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
2839
- }
2840
- function usd(rawAmount, decimals, price) {
2841
- return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
2842
- }
2843
- function percent(value) {
2844
- const result = Number(value);
2845
- return Number.isFinite(result) ? result * 100 : 0;
2846
- }
2847
- var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
2848
- function publicApyAfterYieldseekerFee(value) {
2849
- const grossPercent = percent(value);
2850
- if (grossPercent <= 0) return grossPercent;
2851
- const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
2852
- return Math.round(netPercent * 1e12) / 1e12;
2853
- }
2854
- function riskAdjustedApyForDays(option, days) {
2855
- if (days === "7D") return option.riskAdjustedApy7dAverage;
2856
- if (days === "30D") return option.riskAdjustedApy30dAverage;
2857
- return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
2858
- }
2859
- function assetAddressValue(record, address) {
2860
- const entry = Object.entries(record).find(
2861
- ([key2]) => key2.toLowerCase() === address.toLowerCase()
2862
- );
2863
- return entry?.[1] ?? "0";
2864
- }
2865
- function position(value, asset, baseAssetDecimals) {
2866
- const option = value?.yieldOption;
2867
- if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
2868
- return invalid("yield positions", "missing vault metadata");
2869
- }
2870
- return {
2871
- chain: "BASE",
2872
- protocol: option.provider,
2873
- protocolId: option.address,
2874
- pool: option.name,
2875
- asset,
2876
- // `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
2877
- // differ from the underlying asset. Yieldseeker already converts it to
2878
- // underlying base-asset units in `assetsBase`; pair that value with the
2879
- // snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
2880
- // share quantity separately because withdraw-from-position expects it.
2881
- amount: decimal(
2882
- value.assetsBase,
2883
- baseAssetDecimals,
2884
- "yield positions"
2885
- ),
2886
- amountRaw: String(value.assetsRaw),
2887
- apy: percent(option.riskAdjustedApy),
2888
- tvl: Number(option.totalDepositsUsd),
2889
- liquidity: Number(option.withdrawableDepositsUsd)
2890
- };
2891
- }
2892
- function mapYieldseekerBalances(contexts) {
2893
- const tokens = [];
2894
- const assetBalances = [];
2895
- const positions = [];
2896
- let totalUsd = 0;
2897
- for (const context of contexts) {
2898
- const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
2899
- assetBalances.push({
2900
- chain: "BASE",
2901
- chainId: 8453,
2902
- asset: context.asset,
2903
- amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
2904
- });
2905
- const idle = assetAddressValue(
2906
- context.snapshot.tokenBalances,
2907
- metadata.address
2908
- );
2909
- tokens.push({
2910
- chain: "BASE",
2911
- chainId: 8453,
2912
- asset: context.asset,
2913
- amount: decimal(idle, metadata.decimals, "snapshot")
2914
- });
2915
- positions.push(
2916
- ...context.positions.map(
2917
- (entry) => position(
2918
- entry,
2919
- context.asset,
2920
- context.snapshot.baseAssetDecimals
2921
- )
2922
- )
2923
- );
2924
- totalUsd += usd(
2925
- raw(context.snapshot.totalValueBase, "snapshot"),
2926
- context.snapshot.baseAssetDecimals,
2927
- context.snapshot.baseAssetPriceUsd
2928
- );
2929
- }
2930
- return {
2931
- ...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
2932
- totalBalance: String(totalUsd),
2933
- totalBalanceAsset: "usdc",
2934
- assetBalances,
2935
- tokens,
2936
- positions
2937
- };
2938
- }
2939
- function mapYieldseekerEarnings(contexts) {
2940
- const tokens = [];
2941
- let lifetimeEarnings = 0;
2942
- for (const context of contexts) {
2943
- const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
2944
- tokens.push({
2945
- chain: "BASE",
2946
- chainId: 8453,
2947
- asset: context.asset,
2948
- amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
2949
- });
2950
- lifetimeEarnings += usd(
2951
- amount,
2952
- context.snapshot.baseAssetDecimals,
2953
- context.snapshot.baseAssetPriceUsd
2954
- );
2955
- }
2956
- return {
2957
- smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2958
- lifetimeEarnings,
2959
- tokens
2960
- };
2961
- }
2962
- function apyForDays(context, days, now) {
2963
- if (days === "7D") return percent(context.snapshot.apy7d);
2964
- if (days === "30D") return percent(context.snapshot.apy30d);
2965
- const apy = calculateSnapshotApy(context.historic?.dailyYieldSnapshots ?? [], 14, now);
2966
- const apyPercent = apy === void 0 ? void 0 : apy * 100;
2967
- return apyPercent !== void 0 && Number.isFinite(apyPercent) ? apyPercent : void 0;
2968
- }
2969
- function dailyApy(point) {
2970
- const total = raw(point.totalValueBase, "historic position");
2971
- const earned = raw(point.dailyYieldBase, "historic position");
2972
- const principal = total - earned;
2973
- if (principal <= 0n || earned === 0n) return 0;
2974
- return Number(earned) / Number(principal) * 365 * 100;
2975
- }
2976
- function aggregateHistory(contexts, dayCount, now) {
2977
- const today = new Date(now).toISOString().slice(0, 10);
2978
- const cutoff = new Date(Date.parse(today) - (dayCount - 1) * 864e5).toISOString().slice(0, 10);
2979
- const assets = new Set(contexts.map((context) => context.asset.toUpperCase()));
2980
- const unit = assets.size === 1 ? [...assets][0] : "USD";
2981
- const byDate = /* @__PURE__ */ new Map();
2982
- for (const context of contexts) {
2983
- const points = context.historic?.dailyYieldSnapshots ?? [];
2984
- for (const point of points) {
2985
- if (!/^\d{4}-\d{2}-\d{2}$/.test(point.date) || !Number.isFinite(Date.parse(point.date)) || point.date < cutoff || point.date > today) continue;
2986
- const tokens = Number(decimal(point.totalValueBase, point.baseAssetDecimals, "historic position"));
2987
- const amount = assets.size === 1 ? tokens : tokens * point.baseAssetPriceUsd;
2988
- if (!Number.isFinite(amount) || amount < 0) {
2989
- invalid("historic position", "expected a finite non-negative balance");
2990
- }
2991
- const current = byDate.get(point.date) ?? { weighted: 0, amount: 0 };
2992
- current.weighted += dailyApy(point) * amount;
2993
- current.amount += amount;
2994
- byDate.set(point.date, current);
2995
- }
2996
- }
2997
- return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
2998
- date,
2999
- apy: value.amount > 0 ? value.weighted / value.amount : 0,
3000
- historicalBalance: { amount: value.amount, unit }
3001
- }));
3002
- }
3003
- function mapYieldseekerApy(walletAddress, contexts, days, now = Date.now()) {
3004
- let weighted = 0;
3005
- let totalUsd = 0;
3006
- const byAsset = {};
3007
- for (const context of contexts) {
3008
- const valueUsd = usd(
3009
- raw(context.snapshot.totalValueBase, "snapshot"),
3010
- context.snapshot.baseAssetDecimals,
3011
- context.snapshot.baseAssetPriceUsd
3012
- );
3013
- const apy = apyForDays(context, days, now);
3014
- if (apy === void 0) continue;
3015
- weighted += apy * valueUsd;
3016
- totalUsd += valueUsd;
3017
- byAsset[context.asset] = apy;
3018
- }
3019
- const dayCount = Number(days.slice(0, -1));
3020
- return {
3021
- walletAddress,
3022
- ...days !== "14D" || Object.keys(byAsset).length > 0 ? { weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0 } : {},
3023
- apyByChainAndAsset: { 8453: byAsset },
3024
- history: aggregateHistory(contexts, dayCount, now)
3025
- };
3026
- }
3027
- function actionType(value) {
3028
- const normalized = value.toLowerCase();
3029
- if (normalized.includes("deposit")) return "Deposit";
3030
- if (normalized.includes("withdraw")) return "Withdraw";
3031
- if (normalized.includes("yield") || normalized.includes("earn"))
3032
- return "Earned";
3033
- return "Rebalance";
3034
- }
3035
- function transactionHashes(details) {
3036
- if (!details) return [];
3037
- const values = [
3038
- details.transactionHash,
3039
- details.txHash,
3040
- ...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
3041
- ...Array.isArray(details.txHashes) ? details.txHashes : []
3042
- ];
3043
- return values.filter(
3044
- (value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
3045
- ).filter((value, index, all) => all.indexOf(value) === index);
3046
- }
3047
- function actionEntry(action) {
3048
- if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
3049
- return {
3050
- agent: "yieldseeker",
3051
- action: actionType(action.actionType),
3052
- date: action.createdDate,
3053
- oldApy: null,
3054
- newApy: null,
3055
- transactions: [
3056
- {
3057
- txHashes: transactionHashes(action.details),
3058
- chainId: 8453
3059
- }
3060
- ],
3061
- rebalanceLog: []
3062
- };
3063
- }
3064
- function depositDestination(context, movement) {
3065
- const to = movement.toAddress.toLowerCase();
3066
- const option = context.positions.find(({ yieldOption }) => yieldOption.chainId === movement.chainId && yieldOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && yieldOption.address.toLowerCase() === to)?.yieldOption;
3067
- if (option) return { protocol: option.provider || "Vault", pool: option.name, tokenSymbol: context.asset };
3068
- const receipt = context.historic?.assets.find((asset) => asset.chainId === movement.chainId && !asset.isSpam && asset.address.toLowerCase() === to && asset.address.toLowerCase() !== context.agent.assetAddress.toLowerCase());
3069
- const minted = receipt && context.historic?.movements.some((candidate) => candidate.chainId === movement.chainId && candidate.transactionHash.toLowerCase() === movement.transactionHash.toLowerCase() && candidate.assetAddress.toLowerCase() === receipt.address.toLowerCase() && candidate.fromAddress.toLowerCase() === "0x0000000000000000000000000000000000000000" && candidate.toAddress.toLowerCase() === context.wallet.walletAddress.toLowerCase() && raw(candidate.assetAmount, "historic position") > 0n);
3070
- if (receipt && minted) return { protocol: "Vault", pool: receipt.name || receipt.symbol, tokenSymbol: context.asset };
3071
- return void 0;
3072
- }
3073
- function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses, destination) {
3074
- const from = movement.fromAddress.toLowerCase();
3075
- const to = movement.toAddress.toLowerCase();
3076
- const owner = ownerAddress.toLowerCase();
3077
- const agentWallet = wallet.walletAddress.toLowerCase();
3078
- const baseAsset = agent.assetAddress.toLowerCase();
3079
- if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
3080
- return void 0;
3081
- }
3082
- let action;
3083
- if (to === agentWallet && !vaultAddresses.has(from)) {
3084
- action = "Top up";
3085
- } else if (from === agentWallet && to === owner) {
3086
- action = "Withdraw";
3087
- } else if (from === agentWallet && destination) {
3088
- action = "Deposit";
3089
- }
3090
- if (!action) return void 0;
3091
- return {
3092
- agent: "yieldseeker",
3093
- action,
3094
- ...action === "Deposit" && destination ? { positions: [{
3095
- ...destination,
3096
- amount: decimal(movement.assetAmount, YIELDSEEKER_ASSET_METADATA[asset].decimals, "historic position")
3097
- }] } : {},
3098
- date: movement.blockDate,
3099
- oldApy: null,
3100
- newApy: null,
3101
- transactions: [
3102
- {
3103
- txHashes: [movement.transactionHash],
3104
- chainId: agent.chainId,
3105
- tokenSymbol: asset,
3106
- amount: decimal(
3107
- movement.assetAmount,
3108
- YIELDSEEKER_ASSET_METADATA[asset].decimals,
3109
- "historic position"
3110
- )
3111
- }
3112
- ],
3113
- rebalanceLog: []
3114
- };
3115
- }
3116
- function mapYieldseekerHistory(contexts, options) {
3117
- const entries = contexts.flatMap((context) => {
3118
- const seenMovements = /* @__PURE__ */ new Set();
3119
- const movements = (context.historic?.movements ?? []).filter((movement) => {
3120
- const key2 = `${movement.chainId}:${movement.transactionHash.toLowerCase()}:${movement.logIndex}`;
3121
- if (seenMovements.has(key2)) return false;
3122
- seenMovements.add(key2);
3123
- return true;
3124
- });
3125
- return [
3126
- ...movements.map(
3127
- (movement) => movementEntry(
3128
- movement,
3129
- context.wallet,
3130
- context.agent,
3131
- context.asset,
3132
- options.ownerAddress,
3133
- options.vaultAddresses,
3134
- depositDestination(context, movement)
3135
- )
3136
- ),
3137
- ...(context.actions ?? []).map(actionEntry)
3138
- ].filter((entry) => entry !== void 0);
3139
- });
3140
- const grouped = /* @__PURE__ */ new Map();
3141
- const ungrouped = [];
3142
- for (const entry of entries) {
3143
- const tx = entry.transactions[0];
3144
- const hash = tx?.txHashes[0];
3145
- if (!hash) {
3146
- ungrouped.push(entry);
3147
- continue;
3148
- }
3149
- const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}`;
3150
- const previous = grouped.get(key2);
3151
- if (!previous) {
3152
- grouped.set(key2, entry);
3153
- continue;
3154
- }
3155
- if (entry.action === "Deposit" && entry.positions?.length) {
3156
- if (!previous.positions?.length) {
3157
- grouped.set(key2, entry);
3158
- continue;
3159
- }
3160
- previous.positions.push(...entry.positions);
3161
- previous.transactions.push(...entry.transactions);
3162
- }
3163
- }
3164
- const filtered = [...grouped.values(), ...ungrouped].filter((entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)).sort((left, right) => right.date.localeCompare(left.date));
3165
- return {
3166
- data: filtered.slice(0, options.limit),
3167
- // v1 returns the whole action/movement collection and defines no cursor.
3168
- // Report a terminal page so callers never loop over the same prefix.
3169
- hasMore: false
3170
- };
3171
- }
3172
- function mapYieldseekerProfile(address, contexts) {
3173
- const protocols = /* @__PURE__ */ new Set();
3174
- for (const context of contexts) {
3175
- for (const current of context.positions) {
3176
- if (current.yieldOption?.provider) {
3177
- protocols.add(String(current.yieldOption.provider));
3178
- }
3179
- }
3180
- }
3181
- return {
3182
- address,
3183
- smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
3184
- chains: contexts.length > 0 ? [8453] : [],
3185
- hasActiveSessionKey: contexts.some(
3186
- (context) => context.wallet.initializedDate != null
3187
- ),
3188
- protocols: [...protocols]
3189
- };
3190
- }
3191
- function mapYieldseekerAgentApy(options, days) {
3192
- const perAsset = {};
3193
- const all = [];
3194
- for (const entry of options) {
3195
- const apys = entry.yieldOptions.map(
3196
- (option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
3197
- ).filter(Number.isFinite);
3198
- if (apys.length === 0) continue;
3199
- const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
3200
- perAsset[entry.asset] = average;
3201
- all.push(average);
3202
- }
3203
- return {
3204
- averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
3205
- detailedApys: { apyPerAsset: { 8453: perAsset } }
3206
- };
3207
- }
3208
-
3209
- // src/agents/yieldseeker/yieldseeker.agent.ts
3210
- var OWNEY_AGENT_NAME = "owney";
3211
- var YIELDSEEKER_USERNAME_PREFIX = "owney_";
3212
- var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
3213
- var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
3214
- var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
3215
- function generateYieldseekerUsername() {
3216
- const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
3217
- return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
3218
- }
3219
- function isUsernameConflict(error) {
3220
- if (!(error instanceof YieldseekerApiError)) return false;
3221
- const code = error.providerCode.toUpperCase();
3222
- return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
3223
- }
3224
- var YIELDSEEKER_AGENT_WALLET_ABI = [
3225
- {
3226
- type: "function",
3227
- name: "withdrawAssetToUser",
3228
- stateMutability: "nonpayable",
3229
- inputs: [
3230
- { name: "recipient", type: "address" },
3231
- { name: "asset", type: "address" },
3232
- { name: "amount", type: "uint256" }
3233
- ],
3234
- outputs: []
3235
- },
3236
- {
3237
- type: "function",
3238
- name: "withdrawAllAssetToUser",
3239
- stateMutability: "nonpayable",
3240
- inputs: [
3241
- { name: "recipient", type: "address" },
3242
- { name: "asset", type: "address" }
3243
- ],
3244
- outputs: []
3245
- }
3246
- ];
3247
- function query(params) {
3248
- const search = new URLSearchParams();
3249
- for (const [key2, value] of Object.entries(params)) {
3250
- if (value !== void 0) search.set(key2, String(value));
3251
- }
3252
- const encoded = search.toString();
3253
- return encoded ? `?${encoded}` : "";
3254
- }
3255
- var YieldseekerAgent = class {
3256
- id = "yieldseeker";
3257
- balanceComposition = "tokens-plus-positions";
3258
- supportedChainIds = [8453];
3259
- supportedAssets = [
3260
- {
3261
- chainId: 8453,
3262
- chain: "BASE",
3263
- assets: [
3264
- { symbol: "USDC", minDepositAmount: "10000000" },
3265
- { symbol: "WETH", minDepositAmount: "1" }
3266
- ]
3267
- }
3268
- ];
3269
- api;
3270
- auth;
3271
- transactionExecutor;
3272
- unwindReceiptWaiter;
3273
- agentContexts = /* @__PURE__ */ new Map();
3274
- users = /* @__PURE__ */ new Map();
3275
- pendingAgents = /* @__PURE__ */ new Map();
3276
- yieldOptions = /* @__PURE__ */ new Map();
3277
- pendingYieldOptions = /* @__PURE__ */ new Map();
3278
- constructor(owneyApiKey, options = {}) {
3279
- this.api = new YieldseekerApiClient(
3280
- owneyApiKey,
3281
- options.baseUrl ?? getYieldseekerProxyBaseUrl(),
3282
- options.fetchFn
3283
- );
3284
- this.auth = new YieldseekerAuth(options.auth);
3285
- this.transactionExecutor = options.transactionExecutor;
3286
- this.unwindReceiptWaiter = options.unwindReceiptWaiter;
3287
- }
3288
- async disconnect() {
3289
- this.auth.clear();
3290
- for (const key2 of this.users.keys()) {
3291
- const [walletAddress, chainId] = key2.split(":");
3292
- clearYieldseekerIdentity(walletAddress, Number(chainId));
3293
- }
3294
- this.users.clear();
3295
- this.agentContexts.clear();
3296
- this.pendingAgents.clear();
3297
- }
3298
- async activateAgent(state, chainId, asset) {
3299
- this.assertChain(chainId);
3300
- const targetAsset = asset ?? "USDC";
3301
- this.assertAsset(targetAsset);
3302
- await this.ensureAgent(state, chainId, targetAsset);
3303
- }
3304
- async deposit(state, chainId, amount, asset, depositCallback) {
3305
- this.assertChain(chainId);
3306
- this.assertAsset(asset);
3307
- if (BigInt(amount) <= 0n) {
3308
- throw new OwneyError(
3309
- "DEPOSIT_AMOUNT_BELOW_MINIMUM",
3310
- "Yieldseeker deposits must be greater than zero.",
3311
- { amount, minDepositAmount: "1" },
3312
- this.id
3313
- );
3314
- }
3315
- const context = await this.ensureAgent(state, chainId, asset);
3316
- let txHash;
3317
- try {
3318
- if (depositCallback) {
3319
- provideDepositVerificationContext(depositCallback, {
3320
- agentId: "yieldseeker",
3321
- signature: await this.auth.getToken(state, chainId),
3322
- userId: context.user.userId,
3323
- yieldseekerAgentId: context.agent.agentId
3324
- });
3325
- txHash = await depositCallback(
3326
- context.wallet.walletAddress,
3327
- chainId,
3328
- amount
3329
- );
3330
- await this.waitForReceipt(state, chainId, txHash);
3331
- } else {
3332
- txHash = await this.submitTransaction(state, chainId, {
3333
- from: (0, import_viem6.getAddress)(state.walletAddress),
3334
- to: YIELDSEEKER_ASSET_METADATA[asset].address,
3335
- data: (0, import_viem6.encodeFunctionData)({
3336
- abi: import_viem6.erc20Abi,
3337
- functionName: "transfer",
3338
- args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
3339
- }),
3340
- value: "0",
3341
- chainId
3342
- });
3343
- }
3344
- } finally {
3345
- await this.refreshSnapshotAfterMovement(
3346
- state,
3347
- chainId,
3348
- context,
3349
- "deposit"
3350
- );
3351
- }
3352
- return {
3353
- txHash,
3354
- smartWallet: context.wallet.walletAddress,
3355
- amount
3356
- };
3357
- }
3358
- async withdraw(state, chainId, asset, amount) {
3359
- this.assertChain(chainId);
3360
- this.assertAsset(asset);
3361
- if (amount !== void 0 && BigInt(amount) <= 0n) {
3362
- throw new OwneyError(
3363
- "WITHDRAW_FAILED",
3364
- "Yieldseeker withdrawals must be greater than zero.",
3365
- { amount },
3366
- this.id
3367
- );
3368
- }
3369
- const context = await this.findAgent(state, chainId, asset);
3370
- if (!context) {
3371
- throw new OwneyError(
3372
- "WITHDRAW_INSUFFICIENT_BALANCE",
3373
- `No Yieldseeker ${asset} agent exists for this wallet.`,
3374
- { asset, available: "0" },
3375
- this.id
3376
- );
3377
- }
3378
- try {
3379
- const portfolio = await this.loadPortfolioContext(
3380
- state,
3381
- chainId,
3382
- context
3383
- );
3384
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3385
- const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
3386
- ([address]) => address.toLowerCase() === metadata.address.toLowerCase()
3387
- );
3388
- const idle = BigInt(idleEntry?.[1] ?? "0");
3389
- const deployed = portfolio.positions.reduce(
3390
- (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
3391
- 0n
3392
- );
3393
- const totalAvailable = idle + deployed;
3394
- const requested = amount === void 0 ? totalAvailable : BigInt(amount);
3395
- if (requested > totalAvailable) {
3396
- throw new OwneyError(
3397
- "WITHDRAW_INSUFFICIENT_BALANCE",
3398
- `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
3399
- {
3400
- asset,
3401
- requested: requested.toString(),
3402
- available: totalAvailable.toString()
3403
- },
3404
- this.id
3405
- );
3406
- }
3407
- let remaining = requested > idle ? requested - idle : 0n;
3408
- for (const position2 of portfolio.positions) {
3409
- if (remaining === 0n) break;
3410
- const available = BigInt(position2.withdrawableAssetsRaw);
3411
- if (available <= 0n) continue;
3412
- const assetsRaw = available < remaining ? available : remaining;
3413
- const response = await this.walletRequest(
3414
- state,
3415
- chainId,
3416
- this.agentPath(context, "withdraw-from-position"),
3417
- {
3418
- method: "POST",
3419
- body: {
3420
- chainId,
3421
- vaultAddress: position2.yieldOption.address,
3422
- assetsRaw: assetsRaw.toString()
3423
- }
3424
- }
3425
- );
3426
- if (!this.isTransactionHash(response?.transactionHash)) {
3427
- throw this.invalidResponse("position withdrawal");
3428
- }
3429
- await this.waitForReceipt(state, chainId, response.transactionHash);
3430
- remaining -= assetsRaw;
3431
- }
3432
- if (remaining > 0n) {
3433
- throw this.invalidResponse("yield positions", {
3434
- reason: "Withdrawable positions could not cover the request.",
3435
- remaining: remaining.toString()
3436
- });
3437
- }
3438
- const account = (0, import_viem6.getAddress)(state.walletAddress);
3439
- const txHash = await this.submitTransaction(state, chainId, {
3440
- from: account,
3441
- to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
3442
- data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
3443
- abi: YIELDSEEKER_AGENT_WALLET_ABI,
3444
- functionName: "withdrawAllAssetToUser",
3445
- args: [account, metadata.address]
3446
- }) : (0, import_viem6.encodeFunctionData)({
3447
- abi: YIELDSEEKER_AGENT_WALLET_ABI,
3448
- functionName: "withdrawAssetToUser",
3449
- args: [account, metadata.address, requested]
3450
- }),
3451
- value: "0",
3452
- chainId
3453
- });
3454
- return {
3455
- txHash,
3456
- type: amount === void 0 ? "full" : "partial",
3457
- amount: requested.toString()
3458
- };
3459
- } finally {
3460
- await this.refreshSnapshotAfterMovement(
3461
- state,
3462
- chainId,
3463
- context,
3464
- "withdrawal"
3465
- );
3466
- }
3467
- }
3468
- async getBalances(state, chainId) {
3469
- this.assertChain(chainId);
3470
- return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
3471
- }
3472
- async getEarnings(state, chainId) {
3473
- this.assertChain(chainId);
3474
- return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
3475
- }
3476
- async getAccountApy(state, chainId, days, tokenSymbol) {
3477
- this.assertChain(chainId);
3478
- const asset = tokenSymbol?.toUpperCase();
3479
- if (asset !== void 0) this.assertAsset(asset);
3480
- const contexts = await this.loadPortfolio(state, chainId, {
3481
- ...asset ? { asset } : {},
3482
- historic: true
3483
- });
3484
- return mapYieldseekerApy(state.walletAddress, contexts, days);
3485
- }
3486
- async getHistory(state, chainId, options) {
3487
- this.assertChain(chainId);
3488
- const asset = options?.tokenSymbol?.toUpperCase();
3489
- if (asset !== void 0) this.assertAsset(asset);
3490
- const contexts = await this.loadPortfolio(state, chainId, {
3491
- ...asset ? { asset } : {},
3492
- historic: true,
3493
- actions: true
3494
- });
3495
- const catalog = await Promise.all(
3496
- [...new Set(contexts.map((context) => context.asset))].map(
3497
- (contextAsset) => this.loadYieldOptions(contextAsset)
3498
- )
3499
- );
3500
- const vaultAddresses = new Set(
3501
- catalog.flat().filter(
3502
- (yieldOption) => yieldOption.chainId === chainId && (0, import_viem6.isAddress)(yieldOption.address)
3503
- ).map((yieldOption) => yieldOption.address.toLowerCase())
3504
- );
3505
- return mapYieldseekerHistory(contexts, {
3506
- limit: options?.limit ?? 10,
3507
- ownerAddress: state.walletAddress,
3508
- vaultAddresses,
3509
- ...options?.fromDate ? { fromDate: options.fromDate } : {},
3510
- ...options?.toDate ? { toDate: options.toDate } : {}
3511
- });
3512
- }
3513
- async getUserProfile(state, chainId) {
3514
- this.assertChain(chainId);
3515
- return mapYieldseekerProfile(
3516
- state.walletAddress,
3517
- await this.loadPortfolio(state, chainId, {})
3518
- );
3519
- }
3520
- async getAgentApy(days, options) {
3521
- this.assertOptionalChain(options?.chainId);
3522
- const requested = options?.tokenSymbol?.toUpperCase();
3523
- if (requested !== void 0) this.assertAsset(requested);
3524
- const assets = requested ? [requested] : ["USDC", "WETH"];
3525
- const values = await Promise.all(
3526
- assets.map(async (asset) => {
3527
- return { asset, yieldOptions: await this.loadYieldOptions(asset) };
3528
- })
3529
- );
3530
- return mapYieldseekerAgentApy(values, days);
3531
- }
3532
- async loadYieldOptions(asset) {
3533
- const cached = this.yieldOptions.get(asset);
3534
- if (cached && cached.expiresAt > Date.now()) return cached.value;
3535
- const pending = this.pendingYieldOptions.get(asset);
3536
- if (pending) return pending;
3537
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3538
- const request = this.api.request(
3539
- `/chains/8453/assets/${metadata.address}/yield-options`
3540
- ).then((response) => {
3541
- if (!Array.isArray(response?.yieldOptions)) {
3542
- throw this.invalidResponse("yield options");
3543
- }
3544
- this.yieldOptions.set(asset, {
3545
- expiresAt: Date.now() + YIELDSEEKER_YIELD_OPTIONS_CACHE_MS,
3546
- value: response.yieldOptions
3547
- });
3548
- return response.yieldOptions;
3549
- }).finally(() => this.pendingYieldOptions.delete(asset));
3550
- this.pendingYieldOptions.set(asset, request);
3551
- return request;
3552
- }
3553
- userKey(state, chainId) {
3554
- return `${state.walletAddress.toLowerCase()}:${chainId}`;
3555
- }
3556
- contextKey(state, chainId, asset) {
3557
- return `${this.userKey(state, chainId)}:${asset}`;
3558
- }
3559
- async resolveUser(state, chainId) {
3560
- const key2 = this.userKey(state, chainId);
3561
- const inMemory = this.users.get(key2);
3562
- if (inMemory) return inMemory;
3563
- const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
3564
- if (persisted) {
3565
- this.users.set(key2, persisted);
3566
- return persisted;
3567
- }
3568
- const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
3569
- let user = null;
3570
- try {
3571
- const login = await this.providerRequest(
3572
- state,
3573
- chainId,
3574
- "/users/login-with-wallet",
3575
- { method: "POST", body: { walletAddress } }
3576
- );
3577
- user = login?.user ?? null;
3578
- if (!user) {
3579
- throw this.invalidResponse("wallet login", {
3580
- reason: "A successful login returned no user."
3581
- });
3582
- }
3583
- } catch (error) {
3584
- if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
3585
- if (error instanceof OwneyError) throw error;
3586
- throw this.mapApiError(error);
3587
- }
3588
- let created;
3589
- for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
3590
- try {
3591
- created = await this.providerRequest(
3592
- state,
3593
- chainId,
3594
- "/users",
3595
- {
3596
- method: "POST",
3597
- body: {
3598
- walletAddress,
3599
- username: generateYieldseekerUsername()
3600
- }
3601
- }
3602
- );
3603
- break;
3604
- } catch (createError) {
3605
- const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
3606
- if (canRetry) continue;
3607
- throw this.mapApiError(createError);
3608
- }
3609
- }
3610
- user = created?.user ?? null;
3611
- }
3612
- if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
3613
- throw this.invalidResponse("wallet identity");
3614
- }
3615
- const resolved = { userId: user.userId };
3616
- this.users.set(key2, resolved);
3617
- writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
3618
- return resolved;
3619
- }
3620
- forgetUser(state, chainId) {
3621
- this.users.delete(this.userKey(state, chainId));
3622
- clearYieldseekerIdentity(state.walletAddress, chainId);
3623
- }
3624
- async ensureAgent(state, chainId, asset) {
3625
- const key2 = this.contextKey(state, chainId, asset);
3626
- const cached = this.agentContexts.get(key2);
3627
- if (cached) return cached;
3628
- const pending = this.pendingAgents.get(key2);
3629
- if (pending) return pending;
3630
- const request = this.resolveAgent(state, chainId, asset, true).then(
3631
- async (context) => {
3632
- if (!context) throw this.invalidResponse("agent creation");
3633
- await this.deployAgent(state, chainId, context);
3634
- this.agentContexts.set(key2, context);
3635
- return context;
3636
- }
3637
- );
3638
- this.pendingAgents.set(key2, request);
3639
- try {
3640
- return await request;
3641
- } finally {
3642
- this.pendingAgents.delete(key2);
3643
- }
3644
- }
3645
- async findAgent(state, chainId, asset) {
3646
- const key2 = this.contextKey(state, chainId, asset);
3647
- const cached = this.agentContexts.get(key2);
3648
- if (cached) return cached;
3649
- const context = await this.resolveAgent(state, chainId, asset, false);
3650
- if (context) this.agentContexts.set(key2, context);
3651
- return context;
3652
- }
3653
- async resolveAgent(state, chainId, asset, createIfMissing) {
3654
- const user = await this.resolveUser(state, chainId);
3655
- const response = await this.walletRequest(
3656
- state,
3657
- chainId,
3658
- `/users/${user.userId}/agents`
3659
- );
3660
- if (!Array.isArray(response?.agents)) {
3661
- throw this.invalidResponse("agent list");
3662
- }
3663
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3664
- let agent = response.agents.find(
3665
- (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3666
- );
3667
- if (!agent && createIfMissing) {
3668
- const created = await this.walletRequest(
3669
- state,
3670
- chainId,
3671
- `/users/${user.userId}/agents`,
3672
- {
3673
- method: "POST",
3674
- body: {
3675
- name: OWNEY_AGENT_NAME,
3676
- emoji: "\u{1F989}",
3677
- chainId,
3678
- assetAddress: metadata.address,
3679
- type: "vault",
3680
- rulePreset: null
3681
- }
3682
- }
3683
- );
3684
- agent = created?.agent;
3685
- }
3686
- if (!agent) return null;
3687
- this.assertAgent(agent);
3688
- const walletResponse = await this.walletRequest(
3689
- state,
3690
- chainId,
3691
- `/users/${user.userId}/agents/${agent.agentId}/wallet`
3692
- );
3693
- if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3694
- throw this.invalidResponse("agent wallet");
3695
- }
3696
- return { user, agent, wallet: walletResponse.agentWallet, asset };
3697
- }
3698
- async loadPortfolio(state, chainId, options) {
3699
- const user = await this.resolveUser(state, chainId);
3700
- const response = await this.walletRequest(
3701
- state,
3702
- chainId,
3703
- `/users/${user.userId}/agents`
3704
- );
3705
- if (!Array.isArray(response?.agents)) {
3706
- throw this.invalidResponse("agent list");
3707
- }
3708
- const contexts = [];
3709
- for (const agent of response.agents) {
3710
- const asset = this.assetForAgent(agent);
3711
- if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3712
- continue;
3713
- }
3714
- this.assertAgent(agent);
3715
- const walletResponse = await this.walletRequest(
3716
- state,
3717
- chainId,
3718
- `/users/${user.userId}/agents/${agent.agentId}/wallet`
3719
- );
3720
- if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3721
- throw this.invalidResponse("agent wallet");
3722
- }
3723
- const context = {
3724
- user,
3725
- agent,
3726
- wallet: walletResponse.agentWallet,
3727
- asset
3728
- };
3729
- this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3730
- contexts.push(context);
3731
- }
3732
- return Promise.all(
3733
- contexts.map(
3734
- (context) => this.loadPortfolioContext(state, chainId, context, options)
3735
- )
3736
- );
3737
- }
3738
- async loadPortfolioContext(state, chainId, context, options = {}) {
3739
- const [snapshot, positions, historic, actions] = await Promise.all([
3740
- this.walletRequest(
3741
- state,
3742
- chainId,
3743
- `${this.agentPath(context, "snapshot")}${query({
3744
- shouldOnlyUseRecentValue: true,
3745
- shouldAllowStaleOnError: true
3746
- })}`
3747
- ),
3748
- this.walletRequest(
3749
- state,
3750
- chainId,
3751
- this.agentPath(context, "yield-positions")
3752
- ),
3753
- options.historic ? this.walletRequest(
3754
- state,
3755
- chainId,
3756
- this.agentPath(context, "wallet/historic-position")
3757
- ) : Promise.resolve(void 0),
3758
- options.actions ? this.walletRequest(
3759
- state,
3760
- chainId,
3761
- this.agentPath(context, "actions")
3762
- ) : Promise.resolve(void 0)
3763
- ]);
3764
- if (!snapshot?.agentSnapshot) {
3765
- throw this.invalidResponse("agent snapshot");
3766
- }
3767
- if (!Array.isArray(positions?.yieldPositions)) {
3768
- throw this.invalidResponse("yield positions");
3769
- }
3770
- return {
3771
- ...context,
3772
- snapshot: snapshot.agentSnapshot,
3773
- positions: positions.yieldPositions,
3774
- ...historic?.position ? { historic: historic.position } : {},
3775
- ...actions?.actions ? { actions: actions.actions } : {}
3776
- };
3777
- }
3778
- async deployAgent(state, chainId, context) {
3779
- if (context.wallet.initializedDate != null) return;
3780
- const walletAddress = context.wallet.walletAddress.toLowerCase();
3781
- const deployed = await this.walletRequest(
3782
- state,
3783
- chainId,
3784
- this.agentPath(context, "deploy"),
3785
- { method: "POST", body: {} }
3786
- );
3787
- if (!deployed?.agentWallet || !(0, import_viem6.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
3788
- throw this.invalidResponse("agent deployment", {
3789
- reason: "Deploy did not return the expected Agent Wallet."
3790
- });
3791
- }
3792
- context.wallet = deployed.agentWallet;
3793
- }
3794
- async refreshSnapshotAfterMovement(state, chainId, context, movement) {
3795
- try {
3796
- const response = await this.walletRequest(
3797
- state,
3798
- chainId,
3799
- `${this.agentPath(context, "snapshot")}${query({
3800
- shouldForceRefresh: true
3801
- })}`
3802
- );
3803
- if (!response?.agentSnapshot) {
3804
- throw this.invalidResponse("agent snapshot refresh");
3805
- }
3806
- } catch (error) {
3807
- console.warn(
3808
- `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3809
- error
3810
- );
3811
- }
3812
- }
3813
- agentPath(context, suffix) {
3814
- return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
3815
- }
3816
- async walletRequest(state, chainId, path, options = {}) {
3817
- try {
3818
- return await this.providerRequest(state, chainId, path, options);
3819
- } catch (error) {
3820
- throw this.mapApiError(error);
3821
- }
3822
- }
3823
- async providerRequest(state, chainId, path, options = {}) {
3824
- this.assertChain(chainId);
3825
- const request = (signature2) => this.api.request(path, {
3826
- ...options,
3827
- signature: signature2
3828
- });
3829
- let signature = await this.auth.getToken(state, chainId);
3830
- try {
3831
- return await request(signature);
3832
- } catch (error) {
3833
- if (!(error instanceof YieldseekerApiError)) throw error;
3834
- if (error.providerCode === "NO_USER") throw error;
3835
- if (!error.isAuthenticationError) throw error;
3836
- signature = await this.auth.refreshToken(state, chainId, signature);
2623
+ for (const key2 of keys) {
2624
+ const raw = store.getItem(key2);
2625
+ if (!raw) continue;
3837
2626
  try {
3838
- return await request(signature);
3839
- } catch (retryError) {
3840
- if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
3841
- this.forgetUser(state, chainId);
2627
+ const parsed = JSON.parse(raw);
2628
+ if (now - parsed.createdAt > MAX_AGE_MS) {
2629
+ store.removeItem(key2);
2630
+ continue;
3842
2631
  }
3843
- throw retryError;
3844
- }
3845
- }
3846
- }
3847
- mapApiError(error) {
3848
- if (!(error instanceof YieldseekerApiError)) {
3849
- return new OwneyError(
3850
- "AGENT_API_ERROR",
3851
- "Yieldseeker request failed.",
3852
- { cause: error instanceof Error ? error.message : String(error) },
3853
- this.id
3854
- );
3855
- }
3856
- const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
3857
- return new OwneyError(
3858
- code,
3859
- `Yieldseeker request failed: ${error.providerCode}.`,
3860
- {
3861
- statusCode: error.status,
3862
- providerCode: error.providerCode,
3863
- ...error.responseFields ? { fields: error.responseFields } : {}
3864
- },
3865
- this.id
3866
- );
3867
- }
3868
- async submitTransaction(state, chainId, transaction) {
3869
- if (this.transactionExecutor) {
3870
- return this.transactionExecutor(state, chainId, transaction);
3871
- }
3872
- this.assertTransaction(transaction, state, chainId);
3873
- const account = (0, import_viem6.getAddress)(state.walletAddress);
3874
- const walletClient = (0, import_viem6.createWalletClient)({
3875
- account,
3876
- chain: import_chains3.base,
3877
- transport: (0, import_viem6.custom)(state.provider)
3878
- });
3879
- const publicClient = (0, import_viem6.createPublicClient)({
3880
- chain: import_chains3.base,
3881
- transport: (0, import_viem6.custom)(state.provider)
3882
- });
3883
- await ensureWalletOnChain(
3884
- publicClient,
3885
- walletClient,
3886
- 8453
3887
- );
3888
- const hash = await walletClient.sendTransaction({
3889
- account,
3890
- chain: import_chains3.base,
3891
- to: (0, import_viem6.getAddress)(transaction.to),
3892
- data: transaction.data,
3893
- value: BigInt(transaction.value)
3894
- });
3895
- const receipt = await publicClient.waitForTransactionReceipt({
3896
- hash,
3897
- confirmations: 1
3898
- });
3899
- if (receipt.status !== "success") {
3900
- throw new OwneyError(
3901
- "AGENT_TRANSACTION_REVERTED",
3902
- `Yieldseeker transaction reverted (${hash}).`,
3903
- { transactionHash: hash },
3904
- this.id
3905
- );
3906
- }
3907
- return hash;
3908
- }
3909
- async waitForReceipt(state, chainId, transactionHash) {
3910
- if (this.unwindReceiptWaiter) {
3911
- await this.unwindReceiptWaiter(state, chainId, transactionHash);
3912
- return;
3913
- }
3914
- const publicClient = (0, import_viem6.createPublicClient)({
3915
- chain: import_chains3.base,
3916
- transport: (0, import_viem6.custom)(state.provider)
3917
- });
3918
- const receipt = await publicClient.waitForTransactionReceipt({
3919
- hash: transactionHash,
3920
- confirmations: 1
3921
- });
3922
- if (receipt.status !== "success") {
3923
- throw new OwneyError(
3924
- "AGENT_TRANSACTION_REVERTED",
3925
- `Yieldseeker transaction reverted (${transactionHash}).`,
3926
- { transactionHash },
3927
- this.id
3928
- );
3929
- }
3930
- }
3931
- assertTransaction(transaction, state, chainId) {
3932
- 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)) {
3933
- throw this.invalidResponse("transaction");
3934
- }
3935
- }
3936
- assertAgent(agent) {
3937
- if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
3938
- throw this.invalidResponse("agent");
3939
- }
3940
- }
3941
- isOwneyAgent(agent) {
3942
- return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
3943
- }
3944
- assetForAgent(agent) {
3945
- for (const asset of ["USDC", "WETH"]) {
3946
- if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
3947
- return asset;
3948
- }
3949
- }
3950
- return null;
3951
- }
3952
- isTransactionHash(value) {
3953
- return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
3954
- }
3955
- assertChain(chainId) {
3956
- if (chainId !== 8453) {
3957
- throw new OwneyError(
3958
- "CHAIN_UNSUPPORTED",
3959
- `Yieldseeker does not support chain ${chainId}.`,
3960
- { chainId, supportedChainIds: [8453] },
3961
- this.id
3962
- );
3963
- }
3964
- }
3965
- assertOptionalChain(chainId) {
3966
- if (chainId !== void 0) this.assertChain(chainId);
3967
- }
3968
- assertAsset(asset) {
3969
- if (asset !== "USDC" && asset !== "WETH") {
3970
- throw new OwneyError(
3971
- "ASSET_UNSUPPORTED",
3972
- `Yieldseeker does not support asset ${asset} in the Owney rollout.`,
3973
- {
3974
- asset,
3975
- supportedAssets: ["USDC", "WETH"],
3976
- providerAlsoAdvertises: ["cbBTC"]
3977
- },
3978
- this.id
3979
- );
3980
- }
3981
- }
3982
- invalidResponse(operation, details = {}) {
3983
- return new OwneyError(
3984
- "AGENT_INVALID_RESPONSE",
3985
- `Yieldseeker returned an invalid ${operation} response.`,
3986
- details,
3987
- this.id
3988
- );
3989
- }
3990
- };
3991
-
3992
- // src/lib/routing-api.ts
3993
- var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3994
- async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3995
- const url = `${baseUrl}/api/v1/agent/org-config`;
3996
- try {
3997
- const res = await fetch(url, {
3998
- method: "GET",
3999
- headers: {
4000
- "Content-Type": "application/json",
4001
- "x-owney-api-key": `${apiKey}`
4002
- }
4003
- });
4004
- if (!res.ok) {
4005
- if (res.status !== 404) {
4006
- console.warn(
4007
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
4008
- );
4009
- }
4010
- return null;
4011
- }
4012
- const json = await res.json();
4013
- const policy = json.success ? json.data ?? null : null;
4014
- debugLog(
4015
- "owney-sdk",
4016
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
4017
- policy ?? void 0
4018
- );
4019
- return policy;
4020
- } catch (error) {
4021
- console.warn(
4022
- "[owney-sdk] Could not read org agent config (non-fatal):",
4023
- error instanceof Error ? error.message : String(error)
4024
- );
4025
- return null;
2632
+ if (Array.isArray(parsed.secrets) && parsed.secrets.length > 0) {
2633
+ out.push(parsed);
2634
+ }
2635
+ } catch {
2636
+ store.removeItem(key2);
2637
+ }
2638
+ }
2639
+ } catch {
2640
+ return out;
4026
2641
  }
2642
+ return out.sort((a, b) => b.createdAt - a.createdAt);
4027
2643
  }
4028
- async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
4029
- const url = `${baseUrl}/api/v1/agent/keys`;
4030
- const res = await fetch(url, {
4031
- method: "GET",
4032
- headers: {
4033
- "Content-Type": "application/json",
4034
- "x-owney-api-key": `${apiKey}`
4035
- }
2644
+
2645
+ // src/lib/swap/swap.executor.ts
2646
+ var DEFAULT_SLIPPAGE = 1;
2647
+ async function affordableAmount(deps, quoted) {
2648
+ const balance = await deps.readSourceBalance();
2649
+ if (balance >= quoted) return quoted;
2650
+ debugLog("owney-sdk", "swap: trimming to the current source balance", {
2651
+ quoted: quoted.toString(),
2652
+ balance: balance.toString(),
2653
+ short: (quoted - balance).toString()
4036
2654
  });
4037
- if (!res.ok) {
4038
- const text = await res.text().catch(() => "");
2655
+ return balance;
2656
+ }
2657
+ async function executeSwap(deps, options) {
2658
+ const { quote, walletAddress, onStage } = options;
2659
+ debugLog("owney-sdk", "swap: start", {
2660
+ rail: quote.rail,
2661
+ from: `${quote.src.amount} ${quote.src.symbol} on ${quote.src.chainId}`,
2662
+ to: `${quote.dst.symbol} on ${quote.dst.chainId}`,
2663
+ expected: quote.dst.amount,
2664
+ floor: quote.dstAmountMin
2665
+ });
2666
+ const before = await deps.readTargetBalance();
2667
+ debugLog("owney-sdk", "swap: target balance before", before.toString());
2668
+ const result = quote.rail === "classic" ? await runClassic(deps, options) : await runFusion(deps, options, walletAddress);
2669
+ const after = await deps.readTargetBalance();
2670
+ const received = after - before;
2671
+ debugLog("owney-sdk", "swap: target balance after", {
2672
+ after: after.toString(),
2673
+ received: received.toString()
2674
+ });
2675
+ if (received <= 0n) {
4039
2676
  throw new OwneyError(
4040
- "API_ROUTING_ERROR",
4041
- `Routing API error ${res.status}: ${text}`,
4042
- { statusCode: res.status, responseBody: text }
2677
+ "SWAP_REQUEST_FAILED",
2678
+ "The swap completed but no funds arrived in the wallet. Check the transaction before retrying.",
2679
+ { rail: quote.rail, ...result }
4043
2680
  );
4044
2681
  }
4045
- const json = await res.json();
4046
- if (!json.success) {
4047
- throw new OwneyError(
4048
- "API_ROUTING_FAILED",
4049
- `Routing API request failed: ${json.message}`,
4050
- { message: json.message }
4051
- );
2682
+ return { received: received.toString(), ...result };
2683
+ }
2684
+ async function runClassic(deps, options) {
2685
+ const {
2686
+ quote,
2687
+ walletAddress,
2688
+ slippage = DEFAULT_SLIPPAGE,
2689
+ direction,
2690
+ onStage
2691
+ } = options;
2692
+ const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
2693
+ const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
2694
+ const swapTxRequest = {
2695
+ from: {
2696
+ chainId: quote.src.chainId,
2697
+ symbol: quote.src.symbol,
2698
+ amount: amount.toString()
2699
+ },
2700
+ to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
2701
+ walletAddress,
2702
+ slippage,
2703
+ ...direction ? { direction } : {}
2704
+ };
2705
+ onStage?.("quoting");
2706
+ debugLog("owney-sdk", "swap: fetching classic calldata");
2707
+ let { tx } = await deps.api.swapTx(swapTxRequest);
2708
+ const isNative = BigInt(tx.value ?? "0") > 0n;
2709
+ if (!isNative) {
2710
+ const needed = amount;
2711
+ const current = await deps.readAllowance(tx.to);
2712
+ debugLog("owney-sdk", "swap: allowance", {
2713
+ spender: tx.to,
2714
+ current: current.toString(),
2715
+ needed: needed.toString()
2716
+ });
2717
+ if (current < needed) {
2718
+ onStage?.("approving");
2719
+ await deps.ensureChain(quote.src.chainId);
2720
+ await deps.approve(tx.to, MAX_UINT256);
2721
+ debugLog(
2722
+ "owney-sdk",
2723
+ "swap: re-fetching classic calldata after approval"
2724
+ );
2725
+ ({ tx } = await deps.api.swapTx(swapTxRequest));
2726
+ }
2727
+ }
2728
+ onStage?.("signing");
2729
+ await deps.ensureChain(quote.src.chainId);
2730
+ debugLog("owney-sdk", "swap: sending classic swap tx", { to: tx.to });
2731
+ const txHash = await deps.sendTransaction({
2732
+ to: tx.to,
2733
+ data: tx.data,
2734
+ value: tx.value ?? "0"
2735
+ });
2736
+ onStage?.("swapped");
2737
+ return { txHash };
2738
+ }
2739
+ async function runFusion(deps, options, walletAddress) {
2740
+ const { quote, direction, onStage } = options;
2741
+ const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
2742
+ const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
2743
+ if (quote.spender && !isNativeSource) {
2744
+ const needed = amount;
2745
+ const current = await deps.readAllowance(quote.spender);
2746
+ debugLog("owney-sdk", "swap: fusion allowance", {
2747
+ spender: quote.spender,
2748
+ current: current.toString(),
2749
+ needed: needed.toString()
2750
+ });
2751
+ if (current < needed) {
2752
+ onStage?.("approving");
2753
+ await deps.ensureChain(quote.src.chainId);
2754
+ await deps.approve(quote.spender, MAX_UINT256);
2755
+ debugLog("owney-sdk", "swap: approved limit order protocol");
2756
+ }
2757
+ }
2758
+ const { secrets, secretHashes } = mintSecrets(quote.secretsCount ?? 1);
2759
+ onStage?.("quoting");
2760
+ debugLog("owney-sdk", "swap: building fusion order", {
2761
+ secrets: secretHashes.length
2762
+ });
2763
+ const built = await deps.api.buildOrder({
2764
+ from: {
2765
+ chainId: quote.src.chainId,
2766
+ symbol: quote.src.symbol,
2767
+ // The trimmed amount — the order is re-quoted at this size server-side.
2768
+ amount: amount.toString()
2769
+ },
2770
+ to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
2771
+ walletAddress,
2772
+ secretHashes,
2773
+ ...direction ? { direction } : {}
2774
+ });
2775
+ saveOrder({
2776
+ orderHash: built.orderHash,
2777
+ secrets,
2778
+ srcChainId: quote.src.chainId,
2779
+ srcSymbol: quote.src.symbol,
2780
+ dstChainId: quote.dst.chainId,
2781
+ dstSymbol: quote.dst.symbol,
2782
+ amount: amount.toString(),
2783
+ createdAt: Date.now()
2784
+ });
2785
+ debugLog("owney-sdk", "swap: order built", { orderHash: built.orderHash });
2786
+ onStage?.("signing");
2787
+ await deps.ensureChain(quote.src.chainId);
2788
+ debugLog("owney-sdk", "swap: awaiting signature in wallet", {
2789
+ signingOnChain: quote.src.chainId
2790
+ });
2791
+ const signature = await deps.signTypedData(built.typedData);
2792
+ debugLog("owney-sdk", "swap: signed, submitting to relayer");
2793
+ await deps.api.submitOrder({
2794
+ srcChainId: quote.src.chainId,
2795
+ // The ORDER STRUCT, not the typed-data envelope we just signed. Sending
2796
+ // the envelope here gets a bare 500 from the relayer.
2797
+ order: built.order,
2798
+ signature,
2799
+ quoteId: built.quoteId,
2800
+ // Single-fill orders must NOT carry secretHashes — the relayer rejects
2801
+ // them with SECRET_HASHES_NOT_REQUIRED. The one hash is already inside the
2802
+ // order's hashlock, so repeating it here is redundant, and only a
2803
+ // multi-fill order (a Merkle tree of hashes) needs them listed.
2804
+ ...secretHashes.length > 1 ? { secretHashes } : {},
2805
+ ...built.extension ? { extension: built.extension } : {}
2806
+ });
2807
+ debugLog("owney-sdk", "swap: order submitted, polling escrows");
2808
+ try {
2809
+ await runFusionOrder(deps.runner, {
2810
+ orderHash: built.orderHash,
2811
+ secrets,
2812
+ ...onStage ? { onStage } : {}
2813
+ });
2814
+ } catch (error) {
2815
+ if (error instanceof OwneyError && (error.code === "SWAP_ORDER_REFUNDED" || error.code === "SWAP_ORDER_CANCELLED")) {
2816
+ clearOrder(built.orderHash);
2817
+ }
2818
+ throw error;
2819
+ }
2820
+ clearOrder(built.orderHash);
2821
+ return { orderHash: built.orderHash };
2822
+ }
2823
+
2824
+ // src/lib/swap/swap.arrival.ts
2825
+ var DEFAULT_TIMEOUT_MS2 = 18e4;
2826
+ var DEFAULT_POLL_MS2 = 4e3;
2827
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
2828
+ async function awaitWithdrawalArrival(options) {
2829
+ const {
2830
+ readBalance,
2831
+ baseline,
2832
+ timeoutMs = DEFAULT_TIMEOUT_MS2,
2833
+ pollMs = DEFAULT_POLL_MS2
2834
+ } = options;
2835
+ const deadline = Date.now() + timeoutMs;
2836
+ debugLog("owney-sdk", "withdraw: waiting for funds to land", {
2837
+ baseline: baseline.toString(),
2838
+ timeoutMs
2839
+ });
2840
+ let lastError;
2841
+ for (; ; ) {
2842
+ try {
2843
+ const balance = await readBalance();
2844
+ if (balance > baseline) {
2845
+ const arrived = balance - baseline;
2846
+ debugLog("owney-sdk", "withdraw: funds landed", {
2847
+ arrived: arrived.toString()
2848
+ });
2849
+ return arrived;
2850
+ }
2851
+ } catch (error) {
2852
+ lastError = error;
2853
+ debugLog("owney-sdk", "withdraw: balance read failed, retrying", {
2854
+ message: error instanceof Error ? error.message : String(error)
2855
+ });
2856
+ }
2857
+ if (Date.now() >= deadline) {
2858
+ throw new OwneyError(
2859
+ "WITHDRAW_ARRIVAL_TIMEOUT",
2860
+ "The withdrawal was accepted but the funds had not arrived in time to swap them. They are on their way to your wallet in the original asset.",
2861
+ {
2862
+ baseline: baseline.toString(),
2863
+ waitedMs: timeoutMs,
2864
+ ...lastError ? {
2865
+ lastReadError: lastError instanceof Error ? lastError.message : String(lastError)
2866
+ } : {}
2867
+ }
2868
+ );
2869
+ }
2870
+ await sleep(pollMs);
4052
2871
  }
4053
- return json.data;
4054
2872
  }
4055
2873
 
4056
2874
  // src/lib/health-report.ts
4057
- var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
4058
- async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
2875
+ var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2876
+ async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
4059
2877
  try {
4060
2878
  await fetch(`${baseUrl}/api/v1/agent/health-report`, {
4061
2879
  method: "POST",
@@ -4087,7 +2905,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
4087
2905
  }
4088
2906
 
4089
2907
  // src/lib/helpers/withdraw-helper.ts
4090
- var import_viem7 = require("viem");
2908
+ var import_viem5 = require("viem");
4091
2909
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
4092
2910
  const target = asset.toUpperCase();
4093
2911
  return agents.map((agent) => {
@@ -4095,29 +2913,8 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
4095
2913
  const tokenBalance = agentBalance?.tokens.find(
4096
2914
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
4097
2915
  );
4098
- let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
4099
- if (agent.balanceComposition === "tokens-plus-positions") {
4100
- const chainNameById = {
4101
- 1: "ETHEREUM",
4102
- 8453: "BASE",
4103
- 42161: "ARBITRUM"
4104
- };
4105
- const targetChain = chainNameById[chainId];
4106
- for (const position2 of agentBalance?.positions ?? []) {
4107
- const positionChain = position2.chain.trim().toUpperCase();
4108
- const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
4109
- if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
4110
- if (position2.amountRaw !== void 0) {
4111
- try {
4112
- balance += BigInt(position2.amountRaw);
4113
- continue;
4114
- } catch {
4115
- }
4116
- }
4117
- balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
4118
- }
4119
- }
4120
- return { agent, balance };
2916
+ if (!tokenBalance) return { agent, balance: 0n };
2917
+ return { agent, balance: (0, import_viem5.parseUnits)(tokenBalance.amount, decimals) };
4121
2918
  });
4122
2919
  }
4123
2920
  function planProportionalShares(balances, requested, totalAvailable) {
@@ -4143,9 +2940,7 @@ function planProportionalShares(balances, requested, totalAvailable) {
4143
2940
  return plans;
4144
2941
  }
4145
2942
  function planDisabledDrain(disabled, requested) {
4146
- const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
4147
- (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
4148
- );
2943
+ const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
4149
2944
  const plans = [];
4150
2945
  let remaining = requested;
4151
2946
  for (const { agent, balance } of sorted) {
@@ -4196,13 +2991,6 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
4196
2991
  return Number.isFinite(total) && total > 0 ? total : 0;
4197
2992
  }
4198
2993
  const normalizedToken = tokenSymbol.toUpperCase();
4199
- const snapshots = balance.assetBalances?.filter(
4200
- (token) => Number(token.chainId) === chainId && String(token.asset).toUpperCase() === normalizedToken
4201
- );
4202
- if (snapshots?.length) {
4203
- const amount = snapshots.reduce((sum, token) => sum + Number(token.amount), 0);
4204
- if (Number.isFinite(amount)) return Math.max(0, amount);
4205
- }
4206
2994
  return balance.tokens.reduce((total, token) => {
4207
2995
  if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
4208
2996
  return total;
@@ -4273,305 +3061,325 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
4273
3061
  }
4274
3062
 
4275
3063
  // src/client.ts
4276
- var import_viem11 = require("viem");
4277
- var import_chains4 = require("viem/chains");
4278
-
4279
- // src/lib/sponsored-token-batch.ts
4280
- var import_viem9 = require("viem");
4281
-
4282
- // src/lib/permit2-batch.ts
4283
3064
  var import_viem8 = require("viem");
4284
- var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
4285
- var PERMIT_BATCH_TYPES = {
4286
- PermitBatchWitnessTransferFrom: [
4287
- { name: "permitted", type: "TokenPermissions[]" },
4288
- { name: "spender", type: "address" },
4289
- { name: "nonce", type: "uint256" },
4290
- { name: "deadline", type: "uint256" },
4291
- { name: "witness", type: "Deposit" }
4292
- ],
4293
- Deposit: [{ name: "recipients", type: "address[]" }],
4294
- TokenPermissions: [
4295
- { name: "token", type: "address" },
4296
- { name: "amount", type: "uint256" }
4297
- ]
4298
- };
4299
- var PERMIT2_BATCH_ABI = (0, import_viem8.parseAbi)([
4300
- "struct TokenPermissions { address token; uint256 amount; }",
4301
- "struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
4302
- "struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
4303
- "function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
4304
- "function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
4305
- ]);
4306
- function batchPermit(b) {
4307
- return {
4308
- permitted: b.transfers.map((t) => ({
4309
- token: b.token,
4310
- amount: BigInt(t.amount)
4311
- })),
4312
- nonce: BigInt(b.nonce),
4313
- deadline: BigInt(b.deadline)
4314
- };
4315
- }
4316
- function batchTypedData(b, spender) {
3065
+ var import_chains2 = require("viem/chains");
3066
+
3067
+ // src/lib/transfer-auth.ts
3068
+ var import_viem6 = require("viem");
3069
+ var ERC20_META_ABI = [
3070
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
3071
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
3072
+ ];
3073
+ function buildTransferWithAuthorizationTypedData(input) {
4317
3074
  return {
4318
- domain: {
4319
- name: "Permit2",
4320
- chainId: b.chainId,
4321
- verifyingContract: BATCH_PERMIT2_ADDRESS
3075
+ domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
3076
+ types: {
3077
+ TransferWithAuthorization: [
3078
+ { name: "from", type: "address" },
3079
+ { name: "to", type: "address" },
3080
+ { name: "value", type: "uint256" },
3081
+ { name: "validAfter", type: "uint256" },
3082
+ { name: "validBefore", type: "uint256" },
3083
+ { name: "nonce", type: "bytes32" }
3084
+ ]
4322
3085
  },
4323
- types: PERMIT_BATCH_TYPES,
4324
- primaryType: "PermitBatchWitnessTransferFrom",
4325
- message: {
4326
- ...batchPermit(b),
4327
- spender,
4328
- witness: { recipients: b.transfers.map((t) => t.to) }
4329
- }
3086
+ primaryType: "TransferWithAuthorization",
3087
+ message: input.message
4330
3088
  };
4331
3089
  }
4332
-
4333
- // src/lib/sponsored-token-batch.ts
4334
- var memory = /* @__PURE__ */ new Map();
4335
- var inflight = /* @__PURE__ */ new Map();
4336
- var planOf = (transfers) => JSON.stringify(
4337
- transfers.map((t) => ({
4338
- to: t.to.toLowerCase(),
4339
- amount: BigInt(t.amount).toString()
4340
- }))
4341
- );
4342
- function read(key2) {
4343
- return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
4344
- }
4345
- function save(key2, body) {
4346
- const value = JSON.stringify({
4347
- ...body,
4348
- transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
4349
- });
4350
- if (typeof window === "undefined") memory.set(key2, value);
4351
- else window.localStorage.setItem(key2, value);
3090
+ async function readTokenMeta(publicClient, token) {
3091
+ const [tokenName, tokenVersion] = await Promise.all([
3092
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
3093
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
3094
+ ]);
3095
+ return { tokenName, tokenVersion };
4352
3096
  }
4353
- function clear(key2) {
4354
- if (typeof window === "undefined") memory.delete(key2);
4355
- else window.localStorage.removeItem(key2);
3097
+ function randomAuthNonce() {
3098
+ const bytes = new Uint8Array(32);
3099
+ globalThis.crypto.getRandomValues(bytes);
3100
+ return (0, import_viem6.bytesToHex)(bytes);
4356
3101
  }
4357
- function sponsorTokenBatch(i) {
4358
- const key2 = `owney.token-batch.v1:${(0, import_viem9.keccak256)((0, import_viem9.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
4359
- const plan = planOf(i.transfers);
4360
- const active = inflight.get(key2);
4361
- if (active) {
4362
- if (active.plan !== plan)
4363
- return Promise.reject(
4364
- new Error(
4365
- "A token deposit is already in progress. Wait for its result before depositing again."
4366
- )
4367
- );
4368
- return active.promise;
3102
+
3103
+ // src/lib/sponsor-client.ts
3104
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3105
+ async function postSponsorTransferAuth(input) {
3106
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
3107
+ let res;
3108
+ try {
3109
+ res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
3110
+ method: "POST",
3111
+ headers: {
3112
+ "content-type": "application/json",
3113
+ "x-owney-api-key": input.apiKey
3114
+ },
3115
+ body: JSON.stringify(input.body)
3116
+ });
3117
+ } catch (networkError) {
3118
+ throw new OwneyError(
3119
+ "SPONSOR_REQUEST_FAILED",
3120
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
3121
+ { cause: String(networkError) }
3122
+ );
3123
+ }
3124
+ const text = await res.text();
3125
+ let parsed = null;
3126
+ try {
3127
+ parsed = JSON.parse(text);
3128
+ } catch {
3129
+ }
3130
+ if (!res.ok || !parsed?.success || !parsed.data) {
3131
+ throw new OwneyError(
3132
+ "SPONSOR_REQUEST_FAILED",
3133
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
3134
+ {
3135
+ statusCode: res.status,
3136
+ responseBody: text.slice(0, 500),
3137
+ // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
3138
+ // before broadcast, so it is safe to fall back to a user-paid deposit.
3139
+ safeToFallback: res.status === 503
3140
+ }
3141
+ );
4369
3142
  }
4370
- const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
4371
- inflight.set(key2, { plan, promise });
4372
- return promise;
3143
+ return parsed.data;
4373
3144
  }
4374
- async function execute(i, key2, plan) {
4375
- if (!i.transfers.length || i.transfers.length > 16 || i.transfers.some(
4376
- (t) => BigInt(t.amount) <= 0n || BigInt(t.amount) >= 1n << 256n
4377
- ) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
4378
- throw new Error("Invalid token deposit shares.");
4379
- const send = async (initial) => {
4380
- let body = initial;
4381
- save(key2, body);
4382
- try {
4383
- if (!body.serializedTransaction) {
4384
- const prepared = await postSponsorBatchTransfer({
4385
- apiKey: i.apiKey,
4386
- baseUrl: i.baseUrl,
4387
- body
4388
- });
4389
- if (!prepared.serializedTransaction || (0, import_viem9.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
4390
- throw new Error(
4391
- "Sponsorship API did not return a valid prepared transaction."
4392
- );
4393
- body = {
4394
- ...body,
4395
- serializedTransaction: prepared.serializedTransaction
4396
- };
4397
- save(key2, body);
3145
+ async function postSponsorPermit2Transfer(input) {
3146
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
3147
+ let res;
3148
+ try {
3149
+ res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
3150
+ method: "POST",
3151
+ headers: {
3152
+ "content-type": "application/json",
3153
+ "x-owney-api-key": input.apiKey
3154
+ },
3155
+ body: JSON.stringify(input.body)
3156
+ });
3157
+ } catch (networkError) {
3158
+ throw new OwneyError(
3159
+ "SPONSOR_REQUEST_FAILED",
3160
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
3161
+ { cause: String(networkError), safeToFallback: false }
3162
+ );
3163
+ }
3164
+ const text = await res.text();
3165
+ let parsed = null;
3166
+ try {
3167
+ parsed = JSON.parse(text);
3168
+ } catch {
3169
+ }
3170
+ if (!res.ok || !parsed?.success || !parsed.data) {
3171
+ throw new OwneyError(
3172
+ "SPONSOR_REQUEST_FAILED",
3173
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
3174
+ {
3175
+ statusCode: res.status,
3176
+ responseBody: text.slice(0, 500),
3177
+ safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
4398
3178
  }
4399
- const result = await postSponsorBatchTransfer({
4400
- apiKey: i.apiKey,
4401
- baseUrl: i.baseUrl,
4402
- body
4403
- });
4404
- if (result.txHash !== (0, import_viem9.keccak256)(body.serializedTransaction))
4405
- throw new Error(
4406
- "Sponsorship receipt does not match the pending transaction."
4407
- );
4408
- clear(key2);
4409
- return result.txHash;
4410
- } catch (error) {
4411
- if (!body.serializedTransaction || error instanceof OwneyError && error.details?.notSubmitted === true)
4412
- clear(key2);
4413
- throw error;
4414
- }
4415
- };
4416
- const saved = read(key2);
4417
- if (saved) {
4418
- const previous = JSON.parse(saved);
4419
- if (previous.chainId !== i.chainId || !(0, import_viem9.isAddressEqual)(previous.from, i.owner) || !(0, import_viem9.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
4420
- throw new Error(
4421
- "Retry the previous token deposit and agent split first to reconcile its status."
4422
- );
4423
- i.onApproved?.();
4424
- return send({ ...previous, transfers: i.transfers });
3179
+ );
4425
3180
  }
4426
- const total = i.transfers.reduce((sum, t) => sum + BigInt(t.amount), 0n);
4427
- const [balance, allowance] = await Promise.all([
4428
- readErc20Balance(i.pub, i.token, i.owner),
4429
- readPermit2Allowance(i.pub, i.token, i.owner)
4430
- ]);
4431
- if (balance < total)
3181
+ return parsed.data;
3182
+ }
3183
+ async function getSponsorRelayerAddress(input) {
3184
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
3185
+ let res;
3186
+ try {
3187
+ res = await fetch(
3188
+ `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
3189
+ {
3190
+ headers: { "x-owney-api-key": input.apiKey }
3191
+ }
3192
+ );
3193
+ } catch (networkError) {
4432
3194
  throw new OwneyError(
4433
- "DEPOSIT_INSUFFICIENT_BALANCE",
4434
- "Insufficient token balance for this deposit."
3195
+ "SPONSOR_REQUEST_FAILED",
3196
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
3197
+ { cause: String(networkError), safeToFallback: true }
4435
3198
  );
4436
- if (allowance < total)
3199
+ }
3200
+ const text = await res.text();
3201
+ let parsed = null;
3202
+ try {
3203
+ parsed = JSON.parse(text);
3204
+ } catch {
3205
+ }
3206
+ if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
4437
3207
  throw new OwneyError(
4438
- "PERMIT2_APPROVAL_REQUIRED",
4439
- "token deposits need a one-time Permit2 approval."
3208
+ "SPONSOR_REQUEST_FAILED",
3209
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
3210
+ {
3211
+ statusCode: res.status,
3212
+ responseBody: text.slice(0, 500),
3213
+ safeToFallback: true
3214
+ }
4440
3215
  );
4441
- const relayer = await getSponsorRelayerAddress({
4442
- apiKey: i.apiKey,
4443
- baseUrl: i.baseUrl,
4444
- chainId: i.chainId
4445
- });
4446
- const now = (await i.pub.getBlock()).timestamp;
4447
- const unsigned = {
4448
- chainId: i.chainId,
4449
- token: i.token,
4450
- from: i.owner,
4451
- transfers: i.transfers,
4452
- nonce: randomPermit2Nonce().toString(),
4453
- deadline: (now + 900n).toString()
4454
- };
4455
- const signature = await i.wallet.signTypedData({
4456
- account: i.owner,
4457
- ...batchTypedData(unsigned, relayer)
4458
- });
4459
- i.onApproved?.();
4460
- return send({ ...unsigned, signature });
3216
+ }
3217
+ return parsed.data.relayer;
4461
3218
  }
4462
3219
 
4463
- // src/lib/sponsored-token-deposit.ts
4464
- function makeSponsoredTokenCallback(deps) {
4465
- const batch = async (chainId, transfers) => {
4466
- if (chainId !== 8453 && chainId !== 42161 && chainId !== 1)
3220
+ // src/lib/sponsored-deposit.ts
3221
+ var AUTH_WINDOW_SECONDS = 15 * 60;
3222
+ function makeSponsoredDepositCallback(deps) {
3223
+ const post = deps.httpPost ?? postSponsorTransferAuth;
3224
+ return async (smartWallet, chainId, amount) => {
3225
+ const cid = chainId;
3226
+ const token = deps.tokenAddressByChain[cid];
3227
+ if (!token) {
4467
3228
  throw new OwneyError(
4468
3229
  "CHAIN_UNSUPPORTED",
4469
3230
  `No sponsored token configured for chain ${chainId}`
4470
3231
  );
4471
- const token = deps.tokenAddressByChain[chainId];
4472
- if (!token)
4473
- throw new OwneyError(
4474
- "CHAIN_UNSUPPORTED",
4475
- `No sponsored token configured for chain ${chainId}`
3232
+ }
3233
+ const pub = deps.getPublicClient(cid);
3234
+ const wallet = deps.getWalletClient(cid);
3235
+ await ensureWalletOnChain(pub, wallet, cid);
3236
+ try {
3237
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3238
+ if (balance < BigInt(amount)) {
3239
+ throw new OwneyError(
3240
+ "DEPOSIT_INSUFFICIENT_BALANCE",
3241
+ "Insufficient balance for this deposit.",
3242
+ { token, chainId: cid, balance: balance.toString(), amount }
3243
+ );
3244
+ }
3245
+ } catch (err) {
3246
+ if (err instanceof OwneyError) throw err;
3247
+ console.warn(
3248
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
3249
+ err instanceof Error ? err.message : String(err)
4476
3250
  );
4477
- const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
4478
- await ensureWalletOnChain(pub, wallet, chainId);
4479
- return sponsorTokenBatch({
4480
- apiKey: deps.apiKey,
4481
- baseUrl: deps.baseUrl,
4482
- owner: deps.ownerAddress,
3251
+ }
3252
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
3253
+ const validAfter = 0n;
3254
+ const validBefore = BigInt(
3255
+ Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
3256
+ );
3257
+ const nonce = randomAuthNonce();
3258
+ const typedData = buildTransferWithAuthorizationTypedData({
4483
3259
  token,
4484
- chainId,
4485
- transfers,
4486
- pub,
4487
- wallet,
4488
- onApproved: deps.onApproved
3260
+ chainId: cid,
3261
+ tokenName,
3262
+ tokenVersion,
3263
+ message: {
3264
+ from: deps.ownerAddress,
3265
+ to: smartWallet,
3266
+ value: BigInt(amount),
3267
+ validAfter,
3268
+ validBefore,
3269
+ nonce
3270
+ }
3271
+ });
3272
+ const authSignature = await wallet.signTypedData({
3273
+ account: deps.ownerAddress,
3274
+ ...typedData
3275
+ });
3276
+ deps.onApproved?.();
3277
+ const result = await post({
3278
+ baseUrl: deps.baseUrl,
3279
+ apiKey: deps.apiKey,
3280
+ body: {
3281
+ chainId: cid,
3282
+ token,
3283
+ from: deps.ownerAddress,
3284
+ to: smartWallet,
3285
+ value: amount,
3286
+ validAfter: validAfter.toString(),
3287
+ validBefore: validBefore.toString(),
3288
+ nonce,
3289
+ authSignature,
3290
+ tokenName,
3291
+ tokenVersion
3292
+ }
4489
3293
  });
3294
+ return result.txHash;
4490
3295
  };
4491
- const callback = makeVerificationAwareDepositCallback(
4492
- (to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
4493
- );
4494
- registerDepositBatch(callback, batch);
4495
- return callback;
4496
- }
4497
-
4498
- // src/lib/agent-deposit-batch.ts
4499
- function deferred() {
4500
- let resolve, reject;
4501
- const promise = new Promise((yes, no) => {
4502
- resolve = yes;
4503
- reject = no;
4504
- });
4505
- void promise.catch(() => {
4506
- });
4507
- return { promise, resolve, reject };
4508
- }
4509
- async function runAgentDepositBatch(chainId, legs, transfer) {
4510
- const funding = deferred();
4511
- const tasks = [];
4512
- const transfers = [];
4513
- try {
4514
- for (const leg of legs) {
4515
- const ready = deferred();
4516
- let entered = false;
4517
- const callback = makeVerificationAwareDepositCallback(
4518
- (to, cid, amount, verification) => {
4519
- if (entered || cid !== chainId || BigInt(amount) !== BigInt(leg.amount)) {
4520
- const error = new Error(
4521
- "Agent changed its prepared deposit share."
4522
- );
4523
- ready.reject(error);
4524
- throw error;
4525
- }
4526
- entered = true;
4527
- ready.resolve(toBatchTransfer(to, amount, verification));
4528
- return funding.promise;
4529
- }
4530
- );
4531
- const task = Promise.resolve().then(() => leg.run(callback));
4532
- tasks.push(task);
4533
- void task.then(
4534
- () => {
4535
- if (!entered)
4536
- ready.reject(
4537
- new Error("Agent did not prepare a deposit transfer.")
4538
- );
4539
- },
4540
- (error) => ready.reject(error)
3296
+ }
3297
+
3298
+ // src/lib/sponsored-weth-deposit.ts
3299
+ var PERMIT_WINDOW_SECONDS = 15 * 60;
3300
+ function makeSponsoredWethCallback(deps) {
3301
+ const get = deps.httpGet ?? getSponsorRelayerAddress;
3302
+ const post = deps.httpPost ?? postSponsorPermit2Transfer;
3303
+ return async (smartWallet, chainId, amount) => {
3304
+ const cid = chainId;
3305
+ const token = deps.tokenAddressByChain[cid];
3306
+ if (!token) {
3307
+ throw new OwneyError(
3308
+ "CHAIN_UNSUPPORTED",
3309
+ `No sponsored WETH configured for chain ${chainId}`
4541
3310
  );
4542
- transfers.push(await ready.promise);
4543
3311
  }
4544
- const txHash = await transfer(chainId, transfers);
4545
- funding.resolve(txHash);
4546
- const settled = await Promise.allSettled(tasks);
4547
- const agentResults = {};
4548
- const failures = [];
4549
- for (const [index, result] of settled.entries()) {
4550
- if (result.status === "fulfilled")
4551
- agentResults[legs[index].id] = result.value;
4552
- else failures.push(legs[index].id);
3312
+ const amountWei = BigInt(amount);
3313
+ const pub = deps.getPublicClient(cid);
3314
+ const wallet = deps.getWalletClient(cid);
3315
+ await ensureWalletOnChain(pub, wallet, cid);
3316
+ try {
3317
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3318
+ if (balance < amountWei) {
3319
+ throw new OwneyError(
3320
+ "DEPOSIT_INSUFFICIENT_BALANCE",
3321
+ "Insufficient WETH balance for this deposit.",
3322
+ { token, chainId: cid, balance: balance.toString(), amount }
3323
+ );
3324
+ }
3325
+ } catch (err) {
3326
+ if (err instanceof OwneyError) throw err;
3327
+ console.warn(
3328
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
3329
+ err instanceof Error ? err.message : String(err)
3330
+ );
4553
3331
  }
4554
- if (failures.length)
3332
+ const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
3333
+ if (allowance < amountWei) {
4555
3334
  throw new OwneyError(
4556
- "DEPOSIT_PARTIAL_FAILURE",
4557
- "The deposit was sent to all agents, but some agent updates could not be confirmed. Check activity before depositing again.",
4558
- {
4559
- txHash,
4560
- fundsSubmitted: true,
4561
- agentResults,
4562
- failedAgentIds: failures
4563
- }
3335
+ "PERMIT2_APPROVAL_REQUIRED",
3336
+ "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3337
+ { token, chainId: cid, allowance: allowance.toString(), amount }
4564
3338
  );
4565
- return { agentResults };
4566
- } catch (error) {
4567
- funding.reject(error);
4568
- await Promise.allSettled(tasks);
4569
- throw error;
4570
- }
3339
+ }
3340
+ const relayer = await get({
3341
+ baseUrl: deps.baseUrl,
3342
+ apiKey: deps.apiKey,
3343
+ chainId: cid
3344
+ });
3345
+ const nonce = randomPermit2Nonce();
3346
+ const deadline = BigInt(
3347
+ Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
3348
+ );
3349
+ const typedData = buildPermitTransferFromTypedData({
3350
+ chainId: cid,
3351
+ message: {
3352
+ permitted: { token, amount: amountWei },
3353
+ spender: relayer,
3354
+ nonce,
3355
+ deadline
3356
+ }
3357
+ });
3358
+ const signature = await wallet.signTypedData({
3359
+ account: deps.ownerAddress,
3360
+ ...typedData
3361
+ });
3362
+ deps.onApproved?.();
3363
+ const result = await post({
3364
+ baseUrl: deps.baseUrl,
3365
+ apiKey: deps.apiKey,
3366
+ body: {
3367
+ chainId: cid,
3368
+ token,
3369
+ from: deps.ownerAddress,
3370
+ to: smartWallet,
3371
+ amount,
3372
+ nonce: nonce.toString(),
3373
+ deadline: deadline.toString(),
3374
+ signature
3375
+ }
3376
+ });
3377
+ return result.txHash;
3378
+ };
4571
3379
  }
4572
3380
 
4573
3381
  // src/lib/sponsored-calls-deposit.ts
4574
- var import_viem10 = require("viem");
3382
+ var import_viem7 = require("viem");
4575
3383
  var DEFAULT_POLL_INTERVAL_MS = 1500;
4576
3384
  var DEFAULT_MAX_POLLS = 30;
4577
3385
  async function paymasterSupported(provider, owner, chainId) {
@@ -4579,7 +3387,7 @@ async function paymasterSupported(provider, owner, chainId) {
4579
3387
  method: "wallet_getCapabilities",
4580
3388
  params: [owner]
4581
3389
  });
4582
- const forChain = caps?.[(0, import_viem10.toHex)(chainId)] ?? caps?.[String(chainId)];
3390
+ const forChain = caps?.[(0, import_viem7.toHex)(chainId)] ?? caps?.[String(chainId)];
4583
3391
  return Boolean(forChain?.paymasterService?.supported);
4584
3392
  }
4585
3393
  function makeSponsoredCallsCallback(deps) {
@@ -4597,7 +3405,7 @@ function makeSponsoredCallsCallback(deps) {
4597
3405
  }
4598
3406
  return new URL(configured, origin).toString();
4599
3407
  };
4600
- const batch = async (chainId, transfers) => {
3408
+ return async (smartWallet, chainId, amount) => {
4601
3409
  const cid = chainId;
4602
3410
  const token = deps.tokenAddressByChain[cid];
4603
3411
  if (!token) {
@@ -4613,53 +3421,22 @@ function makeSponsoredCallsCallback(deps) {
4613
3421
  { chainId }
4614
3422
  );
4615
3423
  }
4616
- const calls = transfers.map((transfer) => ({
4617
- to: token,
4618
- value: "0x0",
4619
- data: (0, import_viem10.encodeFunctionData)({
4620
- abi: import_viem10.erc20Abi,
4621
- functionName: "transfer",
4622
- args: [transfer.to, BigInt(transfer.amount)]
4623
- })
4624
- }));
4625
- let paymasterUrl = absolutePaymasterUrl();
4626
- for (const transfer of transfers) {
4627
- const verification = transfer.yieldseeker;
4628
- if (!verification) continue;
4629
- if (chainId !== 8453)
4630
- throw new OwneyError(
4631
- "CHAIN_UNSUPPORTED",
4632
- `Yieldseeker sponsorship is not available on chain ${chainId}.`
4633
- );
4634
- const { intent } = await postPaymasterIntent({
4635
- baseUrl: deps.routingApiBaseUrl,
4636
- apiKey: deps.apiKey,
4637
- yieldseekerSignature: verification.signature,
4638
- body: {
4639
- chainId,
4640
- token,
4641
- from: deps.ownerAddress,
4642
- to: transfer.to,
4643
- amount: transfer.amount,
4644
- yieldseekerUserId: verification.userId,
4645
- yieldseekerAgentId: verification.agentId
4646
- }
4647
- });
4648
- const url = new URL(paymasterUrl);
4649
- url.searchParams.append("owneyIntent", intent);
4650
- paymasterUrl = url.toString();
4651
- }
3424
+ const data = (0, import_viem7.encodeFunctionData)({
3425
+ abi: import_viem7.erc20Abi,
3426
+ functionName: "transfer",
3427
+ args: [smartWallet, BigInt(amount)]
3428
+ });
4652
3429
  const sendResult = await deps.provider.request({
4653
3430
  method: "wallet_sendCalls",
4654
3431
  params: [
4655
3432
  {
4656
3433
  version: "2.0.0",
4657
3434
  from: deps.ownerAddress,
4658
- chainId: (0, import_viem10.toHex)(chainId),
4659
- atomicRequired: transfers.length > 1,
4660
- calls,
3435
+ chainId: (0, import_viem7.toHex)(chainId),
3436
+ atomicRequired: false,
3437
+ calls: [{ to: token, value: "0x0", data }],
4661
3438
  capabilities: {
4662
- paymasterService: { url: paymasterUrl }
3439
+ paymasterService: { url: absolutePaymasterUrl() }
4663
3440
  }
4664
3441
  }
4665
3442
  ]
@@ -4679,24 +3456,7 @@ function makeSponsoredCallsCallback(deps) {
4679
3456
  params: [callsId]
4680
3457
  });
4681
3458
  const txHash = status?.receipts?.[0]?.transactionHash;
4682
- if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
4683
- throw new OwneyError(
4684
- "SPONSOR_REQUEST_FAILED",
4685
- "The sponsored deposit did not complete successfully.",
4686
- { chainId, callsId, safeToFallback: false }
4687
- );
4688
- }
4689
- if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
4690
- if (status?.receipts?.some(
4691
- (receipt) => receipt.transactionHash !== txHash
4692
- ))
4693
- throw new OwneyError(
4694
- "SPONSORED_CALLS_NO_RECEIPT",
4695
- "The wallet returned multiple transactions for an atomic deposit. Check activity before trying again.",
4696
- { chainId, callsId }
4697
- );
4698
- return txHash;
4699
- }
3459
+ if (txHash) return txHash;
4700
3460
  if (pollIntervalMs > 0) {
4701
3461
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
4702
3462
  }
@@ -4707,16 +3467,9 @@ function makeSponsoredCallsCallback(deps) {
4707
3467
  { chainId, callsId }
4708
3468
  );
4709
3469
  };
4710
- const callback = makeVerificationAwareDepositCallback(
4711
- (to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
4712
- );
4713
- registerDepositBatch(callback, batch);
4714
- return callback;
4715
3470
  }
4716
3471
 
4717
3472
  // src/client.ts
4718
- var PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS = 6;
4719
- var PERMIT2_ALLOWANCE_VERIFY_DELAY_MS = 250;
4720
3473
  function encodeMultiAgentCursor(map) {
4721
3474
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
4722
3475
  }
@@ -4743,20 +3496,15 @@ var SPONSORED_USDC_BY_CHAIN = {
4743
3496
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
4744
3497
  };
4745
3498
  var VIEM_CHAIN2 = {
4746
- 8453: import_chains4.base,
4747
- 42161: import_chains4.arbitrum,
4748
- 1: import_chains4.mainnet
3499
+ 8453: import_chains2.base,
3500
+ 42161: import_chains2.arbitrum,
3501
+ 1: import_chains2.mainnet
4749
3502
  };
4750
3503
  var SPONSORED_WETH_BY_CHAIN = {
4751
3504
  8453: "0x4200000000000000000000000000000000000006",
4752
3505
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
4753
3506
  1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
4754
3507
  };
4755
- var SPONSORED_TOKENS_BY_ASSET = { USDC: SPONSORED_USDC_BY_CHAIN, WETH: SPONSORED_WETH_BY_CHAIN };
4756
- function sponsoredTokensFor(asset) {
4757
- if (!Object.prototype.hasOwnProperty.call(SPONSORED_TOKENS_BY_ASSET, asset)) throw new OwneyError("ASSET_UNSUPPORTED", `No sponsored token configured for ${asset}`);
4758
- return SPONSORED_TOKENS_BY_ASSET[asset];
4759
- }
4760
3508
  function shouldFallbackToUserPaid(error, asset, appCallback) {
4761
3509
  return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
4762
3510
  }
@@ -4778,8 +3526,6 @@ var OwneySDK = class {
4778
3526
  orgAgentConfig;
4779
3527
  orgAgentConfigPromise = null;
4780
3528
  zyfaiRpcUrls;
4781
- yieldseekerApiBaseUrl;
4782
- yieldseekerSiweOrigin;
4783
3529
  routingApiBaseUrl;
4784
3530
  referralSource;
4785
3531
  cachedSponsoredCallback = null;
@@ -4802,8 +3548,6 @@ var OwneySDK = class {
4802
3548
  this.apiKey = config.apiKey;
4803
3549
  if (config.debug) setOwneyDebug(true);
4804
3550
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
4805
- this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
4806
- this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
4807
3551
  this.routingApiBaseUrl = config.routingApiBaseUrl;
4808
3552
  this.paymasterServiceUrl = config.paymasterServiceUrl;
4809
3553
  this.referralSource = config.referralSource;
@@ -4837,7 +3581,6 @@ var OwneySDK = class {
4837
3581
  * After calling this, `connect()` must be called again before using agent methods.
4838
3582
  */
4839
3583
  async disconnect() {
4840
- this.state = null;
4841
3584
  for (const agent of this.agents.values()) {
4842
3585
  await agent.disconnect();
4843
3586
  }
@@ -4891,13 +3634,18 @@ var OwneySDK = class {
4891
3634
  }
4892
3635
  return this.state.provider;
4893
3636
  }
4894
- /** Builds the default USDC batch callback for the connected wallet. */
3637
+ /**
3638
+ * Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
3639
+ * used when the caller omits `depositCallback`. Wraps the connected EIP-1193
3640
+ * provider with viem `custom(provider)` to read token meta and sign the
3641
+ * `TransferWithAuthorization`, then POSTs to the sponsor API.
3642
+ */
4895
3643
  getDefaultSponsoredCallback(onApproved) {
4896
3644
  if (!onApproved && this.cachedSponsoredCallback)
4897
3645
  return this.cachedSponsoredCallback;
4898
3646
  const provider = this.requireConnectedProvider();
4899
3647
  const owner = this.state.walletAddress;
4900
- const callback = makeSponsoredTokenCallback({
3648
+ const callback = makeSponsoredDepositCallback({
4901
3649
  apiKey: this.apiKey,
4902
3650
  baseUrl: this.routingApiBaseUrl,
4903
3651
  ownerAddress: owner,
@@ -4906,32 +3654,35 @@ var OwneySDK = class {
4906
3654
  // Casts work around viem's chain-narrowed Client vs the generic
4907
3655
  // PublicClient/WalletClient param types — structurally identical at
4908
3656
  // runtime, but the two share a name TS treats as unrelated.
4909
- getPublicClient: (cid) => (0, import_viem11.createPublicClient)({
3657
+ getPublicClient: (cid) => (0, import_viem8.createPublicClient)({
4910
3658
  chain: VIEM_CHAIN2[cid],
4911
- transport: (0, import_viem11.custom)(provider)
3659
+ transport: (0, import_viem8.custom)(provider)
4912
3660
  }),
4913
- getWalletClient: (cid) => (0, import_viem11.createWalletClient)({
3661
+ getWalletClient: (cid) => (0, import_viem8.createWalletClient)({
4914
3662
  account: owner,
4915
3663
  chain: VIEM_CHAIN2[cid],
4916
- transport: (0, import_viem11.custom)(provider)
3664
+ transport: (0, import_viem8.custom)(provider)
4917
3665
  })
4918
3666
  });
4919
3667
  if (!onApproved) this.cachedSponsoredCallback = callback;
4920
3668
  return callback;
4921
3669
  }
4922
- /** Builds the wallet-native sponsored calls callback for compatible paymasters. */
3670
+ /**
3671
+ * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
3672
+ * callback used when the caller omits `depositCallback` for a WETH
3673
+ * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
3674
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
3675
+ */
4923
3676
  getDefaultSponsoredCallsCallback(asset, onApproved) {
4924
3677
  const cached = this.cachedSponsoredCallsCallbacks.get(asset);
4925
3678
  if (!onApproved && cached) return cached;
4926
3679
  const provider = this.requireConnectedProvider();
4927
3680
  const callback = makeSponsoredCallsCallback({
4928
- apiKey: this.apiKey,
4929
- routingApiBaseUrl: this.routingApiBaseUrl,
4930
3681
  provider,
4931
3682
  ownerAddress: this.state.walletAddress,
4932
3683
  paymasterServiceUrl: this.paymasterServiceUrl,
4933
3684
  onApproved,
4934
- tokenAddressByChain: sponsoredTokensFor(asset)
3685
+ tokenAddressByChain: asset === "WETH" ? SPONSORED_WETH_BY_CHAIN : SPONSORED_USDC_BY_CHAIN
4935
3686
  });
4936
3687
  if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
4937
3688
  return callback;
@@ -4940,14 +3691,14 @@ var OwneySDK = class {
4940
3691
  * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
4941
3692
  * callback used when the caller omits `depositCallback` for a WETH deposit.
4942
3693
  * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
4943
- * single-use batch authorization instead of an EIP-3009 authorization.
3694
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
4944
3695
  */
4945
3696
  getDefaultWethSponsoredCallback(onApproved) {
4946
3697
  if (!onApproved && this.cachedWethSponsoredCallback)
4947
3698
  return this.cachedWethSponsoredCallback;
4948
3699
  const provider = this.requireConnectedProvider();
4949
3700
  const owner = this.state.walletAddress;
4950
- const callback = makeSponsoredTokenCallback({
3701
+ const callback = makeSponsoredWethCallback({
4951
3702
  apiKey: this.apiKey,
4952
3703
  baseUrl: this.routingApiBaseUrl,
4953
3704
  ownerAddress: owner,
@@ -4956,14 +3707,14 @@ var OwneySDK = class {
4956
3707
  // Casts work around viem's chain-narrowed Client vs the generic
4957
3708
  // PublicClient/WalletClient param types — structurally identical at
4958
3709
  // runtime, but the two share a name TS treats as unrelated.
4959
- getPublicClient: (cid) => (0, import_viem11.createPublicClient)({
3710
+ getPublicClient: (cid) => (0, import_viem8.createPublicClient)({
4960
3711
  chain: VIEM_CHAIN2[cid],
4961
- transport: (0, import_viem11.custom)(provider)
3712
+ transport: (0, import_viem8.custom)(provider)
4962
3713
  }),
4963
- getWalletClient: (cid) => (0, import_viem11.createWalletClient)({
3714
+ getWalletClient: (cid) => (0, import_viem8.createWalletClient)({
4964
3715
  account: owner,
4965
3716
  chain: VIEM_CHAIN2[cid],
4966
- transport: (0, import_viem11.custom)(provider)
3717
+ transport: (0, import_viem8.custom)(provider)
4967
3718
  })
4968
3719
  });
4969
3720
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -5037,14 +3788,7 @@ var OwneySDK = class {
5037
3788
  this.routingApiBaseUrl
5038
3789
  );
5039
3790
  this.disabledAgents.clear();
5040
- for (const {
5041
- key: key2,
5042
- agent_type,
5043
- is_enabled,
5044
- is_configured
5045
- } of agentKeys) {
5046
- const configured = is_configured ?? Boolean(key2);
5047
- if (!configured) continue;
3791
+ for (const { key: key2, agent_type, is_enabled } of agentKeys) {
5048
3792
  const agent = this.createAgent(agent_type, key2);
5049
3793
  if (!agent) continue;
5050
3794
  this.agents.set(agent_type, agent);
@@ -5068,15 +3812,8 @@ var OwneySDK = class {
5068
3812
  }
5069
3813
  createAgent(agentId, key2) {
5070
3814
  if (agentId === "zyfai") {
5071
- if (!key2) return null;
5072
3815
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
5073
3816
  }
5074
- if (agentId === "yieldseeker") {
5075
- return new YieldseekerAgent(this.apiKey, {
5076
- auth: { origin: this.yieldseekerSiweOrigin },
5077
- baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
5078
- });
5079
- }
5080
3817
  return null;
5081
3818
  }
5082
3819
  /**
@@ -5121,10 +3858,9 @@ var OwneySDK = class {
5121
3858
  * If provided, ALL specified agents must support the chainId or the call
5122
3859
  * throws before activating any agent.
5123
3860
  */
5124
- async activateAgent(chainId, agentId, asset) {
3861
+ async activateAgent(chainId, agentId) {
5125
3862
  const state = this.requireState();
5126
3863
  await this.ensureAgentsInitialized();
5127
- this.assertActivationSession(state);
5128
3864
  if (agentId !== void 0) {
5129
3865
  if (agentId.length === 0) {
5130
3866
  throw new OwneyError(
@@ -5158,7 +3894,7 @@ var OwneySDK = class {
5158
3894
  this.activeAgents.add(id);
5159
3895
  }
5160
3896
  state.chainId = chainId;
5161
- await this.activateAgentsInTurn(agents, state, chainId, asset);
3897
+ await this.activateAgentsInTurn(agents, state, chainId);
5162
3898
  return;
5163
3899
  }
5164
3900
  const compatible = [...this.agents.values()].filter(
@@ -5179,12 +3915,7 @@ var OwneySDK = class {
5179
3915
  const enabledCompatible = compatible.filter(
5180
3916
  (agent) => !this.isAgentDisabled(agent.id)
5181
3917
  );
5182
- await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
5183
- }
5184
- assertActivationSession(state) {
5185
- if (this.state !== state) {
5186
- throw new OwneyError("NOT_CONNECTED", "Wallet connection changed. Please try again.");
5187
- }
3918
+ await this.activateAgentsInTurn(enabledCompatible, state, chainId);
5188
3919
  }
5189
3920
  /**
5190
3921
  * Activate agents ONE AT A TIME, each followed by its org policy.
@@ -5199,51 +3930,26 @@ var OwneySDK = class {
5199
3930
  * Serializing costs no real wall-clock: the user can only approve one prompt
5200
3931
  * at a time anyway.
5201
3932
  *
5202
- * Stop at the first failure so a canceled sign-in does not open another
5203
- * agent's wallet prompt. Report any earlier successes for diagnostics; the
5204
- * app discards the session when the complete sign-in does not succeed.
3933
+ * Every agent is attempted even if an earlier one fails, so one declined
3934
+ * signature can't deny the remaining agents their turn. The first failure is
3935
+ * rethrown (matching the previous `Promise.all` rejection) once all agents
3936
+ * have had a chance to activate.
5205
3937
  */
5206
- async activateAgentsInTurn(agents, state, chainId, asset) {
3938
+ async activateAgentsInTurn(agents, state, chainId) {
5207
3939
  let firstError = null;
5208
- const activatedAgentIds = [];
5209
- const failedAgents = [];
5210
3940
  for (const agent of agents) {
5211
- this.assertActivationSession(state);
5212
3941
  try {
5213
- await agent.activateAgent(state, chainId, asset);
5214
- this.assertActivationSession(state);
3942
+ await agent.activateAgent(state, chainId);
5215
3943
  await this.applyOrgPolicyTo(agent, state, chainId);
5216
- this.assertActivationSession(state);
5217
- activatedAgentIds.push(agent.id);
5218
3944
  } catch (error) {
5219
- this.assertActivationSession(state);
5220
- const message = error !== null && typeof error === "object" && "message" in error && typeof error.message === "string" ? error.message : typeof error === "string" ? error : "No activation error message was returned.";
5221
- failedAgents.push({
5222
- agentId: agent.id,
5223
- code: error instanceof OwneyError ? error.code : void 0,
5224
- message,
5225
- ...error instanceof OwneyError && error.details ? { details: error.details } : {}
5226
- });
5227
3945
  if (firstError === null) {
5228
3946
  firstError = error;
5229
3947
  } else {
5230
3948
  console.error(`activateAgent(${agent.id}) failed:`, error);
5231
3949
  }
5232
- break;
5233
3950
  }
5234
3951
  }
5235
- if (firstError === null) return;
5236
- if (activatedAgentIds.length === 0) throw firstError;
5237
- const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
5238
- const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
5239
- const failureMessages = failedAgents.map(
5240
- ({ agentId, message }) => `${this.formatAgentName(agentId)} activation failed: ${message}`
5241
- ).join(" ");
5242
- throw new OwneyError(
5243
- "AGENT_ACTIVATION_PARTIAL_FAILURE",
5244
- `${activeNames} activated. ${failureMessages}`,
5245
- { activatedAgentIds, failedAgentIds, failures: failedAgents }
5246
- );
3952
+ if (firstError !== null) throw firstError;
5247
3953
  }
5248
3954
  /**
5249
3955
  * Deposit into a specific agent, or distribute across all eligible agents.
@@ -5253,8 +3959,7 @@ var OwneySDK = class {
5253
3959
  * @param options.asset - Asset symbol to deposit (e.g. "USDC")
5254
3960
  * @param options.depositCallback - Callback that performs the token transfer and returns a tx hash.
5255
3961
  * When agentId is omitted, this callback is invoked once per eligible agent with that agent's
5256
- * split amount and smart wallet address. Default sponsored deposits batch
5257
- * all shares into one signature; custom callbacks still run once per agent.
3962
+ * split amount and smart wallet address — expect multiple wallet prompts.
5258
3963
  * @param options.agentId - Optional explicit target. Otherwise split equally,
5259
3964
  * or fund remaining agents when a recovery deposit cannot meet every minimum.
5260
3965
  * @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
@@ -5339,40 +4044,6 @@ var OwneySDK = class {
5339
4044
  }
5340
4045
  );
5341
4046
  }
5342
- const batchTransfer = getDepositBatchTransfer(effectiveCallback);
5343
- if (!depositCallback && batchTransfer) {
5344
- return runAgentDepositBatch(
5345
- chainId,
5346
- agentAmounts.map(({ agent, amount: amount2 }) => ({
5347
- id: agent.id,
5348
- amount: amount2,
5349
- run: (callback) => withFailureReporting(
5350
- this.apiKey,
5351
- agent.id,
5352
- () => agent.deposit(state, chainId, amount2, asset, callback),
5353
- this.routingApiBaseUrl
5354
- )
5355
- })),
5356
- async (cid, transfers) => {
5357
- try {
5358
- return await batchTransfer(cid, transfers);
5359
- } catch (error) {
5360
- if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
5361
- throw error;
5362
- const requiredAmount = transfers.reduce(
5363
- (sum, transfer) => sum + BigInt(transfer.amount),
5364
- 0n
5365
- );
5366
- await this.approvePermit2(
5367
- asset,
5368
- requiredAmount,
5369
- cid
5370
- );
5371
- return batchTransfer(cid, transfers);
5372
- }
5373
- }
5374
- );
5375
- }
5376
4047
  const agentResults = {};
5377
4048
  for (const [
5378
4049
  index,
@@ -5412,7 +4083,7 @@ var OwneySDK = class {
5412
4083
  *
5413
4084
  * 1. Missing Permit2 allowance: when the app did not supply its own
5414
4085
  * callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
5415
- * token deposit, this is the wallet's first Permit2 deposit for that token. We send
4086
+ * WETH deposit, this is the wallet's first gasless WETH deposit. We send
5416
4087
  * the one-time (user-paid) Permit2 approval via `approvePermit2()` and
5417
4088
  * retry the SAME sponsored attempt once. Bounded to one approval attempt
5418
4089
  * per call so a wallet/agent that keeps reporting the allowance as
@@ -5449,16 +4120,12 @@ var OwneySDK = class {
5449
4120
  try {
5450
4121
  return await attempt(effectiveCallback);
5451
4122
  } catch (error) {
5452
- if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
4123
+ if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
5453
4124
  approvalAttempted = true;
5454
4125
  console.warn(
5455
- "[owney-sdk] First token deposit: sending one-time Permit2 approval..."
5456
- );
5457
- await this.approvePermit2(
5458
- asset,
5459
- BigInt(amount),
5460
- chainId
4126
+ "[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
5461
4127
  );
4128
+ await this.approvePermit2();
5462
4129
  continue;
5463
4130
  }
5464
4131
  if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
@@ -5496,10 +4163,10 @@ var OwneySDK = class {
5496
4163
  agent,
5497
4164
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
5498
4165
  }));
5499
- const valid2 = splits.filter(
4166
+ const valid = splits.filter(
5500
4167
  (s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
5501
4168
  );
5502
- if (valid2.length === agents.length) {
4169
+ if (valid.length === agents.length) {
5503
4170
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
5504
4171
  }
5505
4172
  const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
@@ -5518,11 +4185,6 @@ var OwneySDK = class {
5518
4185
  )
5519
4186
  }));
5520
4187
  }
5521
- formatAgentName(agentId) {
5522
- if (agentId === "zyfai") return "Zyfai";
5523
- if (agentId === "yieldseeker") return "Yieldseeker";
5524
- return agentId;
5525
- }
5526
4188
  async validateMinDepositAmount(agent, state, chainId, asset, amount) {
5527
4189
  const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
5528
4190
  const parsedAmount = BigInt(amount);
@@ -5554,12 +4216,12 @@ var OwneySDK = class {
5554
4216
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
5555
4217
  );
5556
4218
  const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
5557
- const position2 = (balance.positions ?? []).find((p) => {
4219
+ const position = (balance.positions ?? []).find((p) => {
5558
4220
  const positionChain = p.chain.trim().toUpperCase();
5559
4221
  const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
5560
4222
  return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
5561
4223
  });
5562
- return !!token && Number(token.amount) > 0 || !!position2;
4224
+ return !!token && Number(token.amount) > 0 || !!position;
5563
4225
  } catch (error) {
5564
4226
  if (requireReliableRead) {
5565
4227
  throw new OwneyError(
@@ -5605,6 +4267,354 @@ var OwneySDK = class {
5605
4267
  return eligible;
5606
4268
  }
5607
4269
  // --- Fund operations ---
4270
+ // --- Swap to yield (ROUT-242) ---
4271
+ /** Lazily built so an app that never swaps pays nothing for it. */
4272
+ swapApiClient;
4273
+ swapApi() {
4274
+ this.swapApiClient ??= createSwapApi(
4275
+ this.routingApiBaseUrl ?? ROUTING_API_BASE_URL,
4276
+ this.apiKey
4277
+ );
4278
+ return this.swapApiClient;
4279
+ }
4280
+ /**
4281
+ * Put the wallet on `chainId`, or fail with something actionable.
4282
+ *
4283
+ * Reuses the same guard the deposit rail uses, which re-reads the chain after
4284
+ * switching — some wallets resolve wallet_switchEthereumChain before the
4285
+ * network has actually changed.
4286
+ */
4287
+ async ensureSwapChain(chainId) {
4288
+ const provider = this.requireConnectedProvider();
4289
+ const state = this.requireState();
4290
+ const chain = VIEM_CHAIN2[chainId];
4291
+ if (!chain) {
4292
+ throw new OwneyError(
4293
+ "CHAIN_UNSUPPORTED",
4294
+ `Chain ${chainId} is not supported`,
4295
+ { chainId }
4296
+ );
4297
+ }
4298
+ await ensureWalletOnChain(
4299
+ (0, import_viem8.createPublicClient)({ chain, transport: (0, import_viem8.custom)(provider) }),
4300
+ (0, import_viem8.createWalletClient)({
4301
+ account: state.walletAddress,
4302
+ chain,
4303
+ transport: (0, import_viem8.custom)(provider)
4304
+ }),
4305
+ chainId
4306
+ );
4307
+ }
4308
+ /**
4309
+ * Binds the executor's abstract deps to this client's wallet.
4310
+ *
4311
+ * Kept as a builder rather than baked into the executor so the whole swap
4312
+ * flow stays testable without a provider — the executor never imports viem.
4313
+ */
4314
+ buildSwapDeps(quote) {
4315
+ const state = this.requireState();
4316
+ const provider = this.requireConnectedProvider();
4317
+ const srcChain = VIEM_CHAIN2[quote.src.chainId];
4318
+ const dstChain = VIEM_CHAIN2[quote.dst.chainId];
4319
+ const wallet = (0, import_viem8.createWalletClient)({
4320
+ account: state.walletAddress,
4321
+ chain: srcChain,
4322
+ transport: (0, import_viem8.custom)(provider)
4323
+ });
4324
+ const srcPublic = (0, import_viem8.createPublicClient)({
4325
+ chain: srcChain,
4326
+ transport: swapReadTransport(quote.src.chainId, this.zyfaiRpcUrls)
4327
+ });
4328
+ const dstPublic = (0, import_viem8.createPublicClient)({
4329
+ chain: dstChain,
4330
+ transport: swapReadTransport(quote.dst.chainId, this.zyfaiRpcUrls)
4331
+ });
4332
+ return {
4333
+ api: this.swapApi(),
4334
+ // Native-aware, like readSourceBalance below. Native ETH is never a
4335
+ // DEPOSIT target, so this only ever mattered once withdrawal shipped —
4336
+ // and there it is the headline case. balanceOf() on the 0xEeee sentinel
4337
+ // reverts, which would have read as "the swap landed nothing".
4338
+ readTargetBalance: async () => {
4339
+ const dst = quote.dst.address;
4340
+ if (dst.toLowerCase().startsWith("0xeeee")) {
4341
+ return dstPublic.getBalance({ address: state.walletAddress });
4342
+ }
4343
+ return dstPublic.readContract({
4344
+ address: dst,
4345
+ abi: import_viem8.erc20Abi,
4346
+ functionName: "balanceOf",
4347
+ args: [state.walletAddress]
4348
+ });
4349
+ },
4350
+ sendTransaction: async (tx) => {
4351
+ const hash = await wallet.sendTransaction({
4352
+ to: tx.to,
4353
+ data: tx.data,
4354
+ value: BigInt(tx.value || "0"),
4355
+ account: state.walletAddress,
4356
+ chain: srcChain
4357
+ });
4358
+ const receipt = await srcPublic.waitForTransactionReceipt({
4359
+ timeout: receiptTimeoutMs(quote.src.chainId),
4360
+ hash,
4361
+ confirmations: 1
4362
+ });
4363
+ if (receipt.status !== "success") {
4364
+ throw new OwneyError(
4365
+ "SWAP_REQUEST_FAILED",
4366
+ `Swap transaction reverted (tx ${hash})`,
4367
+ { hash }
4368
+ );
4369
+ }
4370
+ return hash;
4371
+ },
4372
+ signTypedData: (typedData) => wallet.signTypedData({
4373
+ account: state.walletAddress,
4374
+ ...typedData
4375
+ }),
4376
+ // Chain-bound like every other read here: the wallet provider's chain is
4377
+ // not ours to rely on mid-swap.
4378
+ readSourceBalance: async () => {
4379
+ const src = quote.src.address;
4380
+ if (src.toLowerCase().startsWith("0xeeee")) {
4381
+ return srcPublic.getBalance({ address: state.walletAddress });
4382
+ }
4383
+ return srcPublic.readContract({
4384
+ address: src,
4385
+ abi: ERC20_ALLOWANCE_ABI,
4386
+ functionName: "balanceOf",
4387
+ args: [state.walletAddress]
4388
+ });
4389
+ },
4390
+ readAllowance: (spender) => srcPublic.readContract({
4391
+ address: quote.src.address,
4392
+ abi: ERC20_ALLOWANCE_ABI,
4393
+ functionName: "allowance",
4394
+ args: [state.walletAddress, spender]
4395
+ }),
4396
+ approve: async (spender, amount) => {
4397
+ const hash = await wallet.writeContract({
4398
+ address: quote.src.address,
4399
+ abi: ERC20_ALLOWANCE_ABI,
4400
+ functionName: "approve",
4401
+ args: [spender, amount],
4402
+ account: state.walletAddress,
4403
+ chain: srcChain
4404
+ });
4405
+ await srcPublic.waitForTransactionReceipt({
4406
+ hash,
4407
+ confirmations: 1,
4408
+ timeout: receiptTimeoutMs(quote.src.chainId)
4409
+ });
4410
+ return hash;
4411
+ },
4412
+ ensureChain: (chainId) => this.ensureSwapChain(chainId),
4413
+ runner: {
4414
+ readyForSecrets: (h) => this.swapApi().readyForSecrets(h),
4415
+ submitSecret: (h, secret) => this.swapApi().submitSecret(h, secret),
4416
+ orderStatus: (h) => this.swapApi().orderStatus(h),
4417
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
4418
+ now: () => Date.now()
4419
+ }
4420
+ };
4421
+ }
4422
+ /**
4423
+ * Assets the user may pay with, and what each chain deposits into.
4424
+ *
4425
+ * The source list is deliberately wider than the deposit list: it includes
4426
+ * native ETH and USDT, which Owney never holds but users often do.
4427
+ */
4428
+ async getSwapTokens() {
4429
+ return this.swapApi().listTokens();
4430
+ }
4431
+ /**
4432
+ * Search the assets a user may pay with on one chain.
4433
+ *
4434
+ * `getSwapTokens` returns the short list worth rendering unprompted. This
4435
+ * reaches everything else the routing API will accept — thousands per chain
4436
+ * once the wider allowlist is enabled, which is why it is a query rather
4437
+ * than a download.
4438
+ *
4439
+ * Results are filtered server-side to what a quote will accept, so anything
4440
+ * returned can be paid with. They are NOT ranked by trustworthiness: several
4441
+ * tokens can share a ticker, and `providers` (how many token lists carry the
4442
+ * address) is the only usable signal for telling them apart. Surface it.
4443
+ *
4444
+ * Returns nothing for a blank query rather than asking for the whole list.
4445
+ */
4446
+ async searchSwapTokens(params) {
4447
+ const query = params.query.trim();
4448
+ if (!query) return { tokens: [] };
4449
+ return this.swapApi().searchTokens({
4450
+ chainId: params.chainId,
4451
+ query,
4452
+ ...params.limit === void 0 ? {} : { limit: params.limit }
4453
+ });
4454
+ }
4455
+ /**
4456
+ * Price a swap without committing to it.
4457
+ *
4458
+ * `dstAmountMin` is the number to validate against a deposit minimum —
4459
+ * `dst.amount` is an estimate that a decaying auction or slippage can undercut,
4460
+ * and a swap landing below the floor leaves the user swapped but not
4461
+ * deposited.
4462
+ */
4463
+ async getSwapQuote(params) {
4464
+ const state = this.requireState();
4465
+ return this.swapApi().quote({
4466
+ ...params,
4467
+ walletAddress: state.walletAddress
4468
+ });
4469
+ }
4470
+ /**
4471
+ * Swap an asset the user holds into a deposit asset, then deposit it.
4472
+ *
4473
+ * Kept separate from `deposit()` rather than bolted on as an option: the
4474
+ * return shape differs, the staging callback is meaningless on the plain
4475
+ * path, and integrators who never swap should not have to reason about any
4476
+ * of it.
4477
+ *
4478
+ * The deposit runs on the MEASURED arrival, not the quote. A quote is an
4479
+ * estimate, so depositing the quoted figure would either strand dust or try
4480
+ * to move funds that never came.
4481
+ *
4482
+ * Failure modes differ in a way callers must respect. A same-chain swap is
4483
+ * atomic — if it fails, nothing moved. A cross-chain swap escrows the user's
4484
+ * funds first, so SWAP_ORDER_EXPIRED / REFUNDED / CANCELLED all mean the
4485
+ * money left the wallet. Only the former can honestly say "nothing has left
4486
+ * your wallet".
4487
+ */
4488
+ async swapAndDeposit(options) {
4489
+ const state = this.requireState();
4490
+ const api = this.swapApi();
4491
+ const quote = await api.quote({
4492
+ from: options.from,
4493
+ to: options.to,
4494
+ walletAddress: state.walletAddress
4495
+ });
4496
+ await this.ensureSwapChain(quote.src.chainId);
4497
+ const swap = await executeSwap(this.buildSwapDeps(quote), {
4498
+ quote,
4499
+ walletAddress: state.walletAddress,
4500
+ ...options.slippage === void 0 ? {} : { slippage: options.slippage },
4501
+ ...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
4502
+ });
4503
+ options.onSwapProgress?.("depositing");
4504
+ await this.ensureSwapChain(quote.dst.chainId);
4505
+ const deposit = await this.deposit({
4506
+ amount: swap.received,
4507
+ asset: options.to.symbol,
4508
+ ...options.agentId ? { agentId: options.agentId } : {}
4509
+ });
4510
+ return { swap, deposit };
4511
+ }
4512
+ /**
4513
+ * Withdraw from an agent and swap the proceeds into whatever the user wants
4514
+ * to hold, delivered to their own wallet.
4515
+ *
4516
+ * The mirror of `swapAndDeposit()`, with one structural difference that
4517
+ * drives the whole implementation: a deposit swap starts from funds already
4518
+ * sitting in the wallet, but a withdrawal has to wait for them. The agent's
4519
+ * provider acknowledges a withdrawal and *then* queues the on-chain transfer
4520
+ * to the EOA, so `withdraw()` resolving means "accepted", not "arrived".
4521
+ * Quoting before the tokens land would size the swap against a balance that
4522
+ * is not there yet.
4523
+ *
4524
+ * The swap is therefore sized from the MEASURED arrival, exactly as the
4525
+ * deposit path sizes its deposit from the measured swap output. On a full
4526
+ * withdrawal there is no other number available — "MAX" has no figure until
4527
+ * the agent picks one.
4528
+ *
4529
+ * **Failure here is not symmetrical with the deposit path.** A failed
4530
+ * deposit-swap leaves the user holding what they started with. A failed
4531
+ * withdrawal-swap leaves them holding the AGENT'S asset in their own wallet:
4532
+ * the money is out, safe, and in the wrong denomination. Both
4533
+ * `WITHDRAW_ARRIVAL_TIMEOUT` and `WITHDRAW_SWAP_FAILED` carry `withdrawn` for
4534
+ * that reason — the UI has to tell the user where their money actually is,
4535
+ * and must never present either as a lost withdrawal.
4536
+ */
4537
+ async withdrawAndSwap(options) {
4538
+ const state = this.requireState();
4539
+ const activeChainId = this.requireChainId();
4540
+ if (options.from.chainId !== activeChainId) {
4541
+ throw new OwneyError(
4542
+ "CHAIN_MISMATCH",
4543
+ `Cannot withdraw from chain ${options.from.chainId} while the active chain is ${activeChainId}. Activate on that chain first.`,
4544
+ { requested: options.from.chainId, active: activeChainId }
4545
+ );
4546
+ }
4547
+ const asset = SupportedAssets.find(
4548
+ (a) => a.chainId === options.from.chainId && a.symbol === options.from.symbol.toUpperCase()
4549
+ );
4550
+ if (!asset) {
4551
+ throw new OwneyError(
4552
+ "WITHDRAW_NO_PERMITTED_TOKENS",
4553
+ `${options.from.symbol} on chain ${options.from.chainId} is not an asset Owney holds`,
4554
+ { ...options.from }
4555
+ );
4556
+ }
4557
+ const srcChain = VIEM_CHAIN2[options.from.chainId];
4558
+ const srcPublic = (0, import_viem8.createPublicClient)({
4559
+ chain: srcChain,
4560
+ transport: swapReadTransport(options.from.chainId, this.zyfaiRpcUrls)
4561
+ });
4562
+ const readWalletBalance = () => srcPublic.readContract({
4563
+ address: asset.address,
4564
+ abi: import_viem8.erc20Abi,
4565
+ functionName: "balanceOf",
4566
+ args: [state.walletAddress]
4567
+ });
4568
+ const baseline = await readWalletBalance();
4569
+ debugLog("owney-sdk", "withdrawAndSwap: baseline", {
4570
+ asset: `${asset.symbol}@${asset.chainId}`,
4571
+ baseline: baseline.toString()
4572
+ });
4573
+ options.onSwapProgress?.("withdrawing");
4574
+ const withdraw = await this.withdraw({
4575
+ asset: options.from.symbol,
4576
+ ...options.amount === void 0 ? {} : { amount: options.amount },
4577
+ ...options.agentId ? { agentId: options.agentId } : {}
4578
+ });
4579
+ const arrived = await awaitWithdrawalArrival({
4580
+ readBalance: readWalletBalance,
4581
+ baseline,
4582
+ ...options.arrivalTimeoutMs === void 0 ? {} : { timeoutMs: options.arrivalTimeoutMs }
4583
+ });
4584
+ const withdrawn = arrived.toString();
4585
+ options.onSwapProgress?.("withdrawn");
4586
+ try {
4587
+ const quote = await this.swapApi().quote({
4588
+ from: { ...options.from, amount: withdrawn },
4589
+ to: options.to,
4590
+ direction: "withdraw",
4591
+ walletAddress: state.walletAddress
4592
+ });
4593
+ await this.ensureSwapChain(quote.src.chainId);
4594
+ const swap = await executeSwap(this.buildSwapDeps(quote), {
4595
+ quote,
4596
+ walletAddress: state.walletAddress,
4597
+ direction: "withdraw",
4598
+ ...options.slippage === void 0 ? {} : { slippage: options.slippage },
4599
+ ...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
4600
+ });
4601
+ return { withdraw, withdrawn, swap };
4602
+ } catch (error) {
4603
+ throw new OwneyError(
4604
+ "WITHDRAW_SWAP_FAILED",
4605
+ `Withdrew ${withdrawn} ${asset.symbol} to your wallet, but the swap to ${options.to.symbol} did not complete. The funds are in your wallet as ${asset.symbol}.`,
4606
+ {
4607
+ withdrawn,
4608
+ asset: asset.symbol,
4609
+ chainId: asset.chainId,
4610
+ intendedSymbol: options.to.symbol,
4611
+ intendedChainId: options.to.chainId,
4612
+ cause: error instanceof Error ? error.message : String(error),
4613
+ ...error instanceof OwneyError ? { causeCode: error.code } : {}
4614
+ }
4615
+ );
4616
+ }
4617
+ }
5608
4618
  /**
5609
4619
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
5610
4620
  * Validates that the asset is supported by the target agent(s) on the active chain.
@@ -5674,10 +4684,6 @@ var OwneySDK = class {
5674
4684
  }
5675
4685
  const requested = BigInt(amount);
5676
4686
  const aggregated = await this.getBalances();
5677
- const unavailableAgents = eligibleAgents.filter(
5678
- (agent) => !(agent.id in aggregated.agentBalances)
5679
- );
5680
- const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
5681
4687
  const balances = projectAgentBalancesForAsset(
5682
4688
  eligibleAgents,
5683
4689
  aggregated.agentBalances,
@@ -5686,18 +4692,7 @@ var OwneySDK = class {
5686
4692
  assetInfo.decimals
5687
4693
  );
5688
4694
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
5689
- if (totalAvailable === 0n && unavailableAgents.length > 0) {
5690
- throw new OwneyError(
5691
- "WITHDRAW_BALANCE_UNAVAILABLE",
5692
- `Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
5693
- {
5694
- asset,
5695
- unavailableAgents: unavailableAgentIds,
5696
- agentErrors: aggregated.agentErrors
5697
- }
5698
- );
5699
- }
5700
- if (totalAvailable < requested && unavailableAgents.length === 0) {
4695
+ if (totalAvailable < requested) {
5701
4696
  throw new OwneyError(
5702
4697
  "WITHDRAW_INSUFFICIENT_BALANCE",
5703
4698
  `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
@@ -5708,7 +4703,6 @@ var OwneySDK = class {
5708
4703
  }
5709
4704
  );
5710
4705
  }
5711
- const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
5712
4706
  const disabledBalances = balances.filter(
5713
4707
  (b) => this.isAgentDisabled(b.agent.id)
5714
4708
  );
@@ -5717,7 +4711,7 @@ var OwneySDK = class {
5717
4711
  );
5718
4712
  const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
5719
4713
  disabledBalances,
5720
- plannedTarget
4714
+ requested
5721
4715
  );
5722
4716
  const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
5723
4717
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
@@ -5727,9 +4721,7 @@ var OwneySDK = class {
5727
4721
  }));
5728
4722
  const plans = [...disabledPlans, ...enabledPlans];
5729
4723
  const results = {};
5730
- const agentErrors = {
5731
- ...aggregated.agentErrors ?? {}
5732
- };
4724
+ const agentErrors = {};
5733
4725
  for (let i = 0; i < plans.length; i++) {
5734
4726
  const p = plans[i];
5735
4727
  if (p.planned === 0n) continue;
@@ -5776,8 +4768,7 @@ var OwneySDK = class {
5776
4768
  requested: amount,
5777
4769
  withdrawn: withdrawn.toString(),
5778
4770
  partialResults: results,
5779
- agentErrors,
5780
- ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
4771
+ agentErrors
5781
4772
  }
5782
4773
  );
5783
4774
  }
@@ -5794,25 +4785,24 @@ var OwneySDK = class {
5794
4785
  const chainId = this.requireChainId();
5795
4786
  if (agentId) {
5796
4787
  const agent = this.getAgent(agentId);
5797
- const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5798
- return {
5799
- ...result,
5800
- balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
5801
- };
4788
+ const result = await this.readAgent(
4789
+ agent,
4790
+ "balances",
4791
+ () => agent.getBalances(state, chainId)
4792
+ );
4793
+ return result;
5802
4794
  }
5803
4795
  let totalBalance = 0;
5804
4796
  const results = {};
5805
4797
  const entries = [...this.getActiveAgents().entries()];
5806
4798
  const balanceResults = await Promise.allSettled(
5807
4799
  entries.map(async ([id, agent]) => {
5808
- const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5809
- return [
5810
- id,
5811
- {
5812
- ...b,
5813
- balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
5814
- }
5815
- ];
4800
+ const b = await this.readAgent(
4801
+ agent,
4802
+ "balances",
4803
+ () => agent.getBalances(state, chainId)
4804
+ );
4805
+ return [id, b];
5816
4806
  })
5817
4807
  );
5818
4808
  let successCount = 0;
@@ -5832,9 +4822,9 @@ var OwneySDK = class {
5832
4822
  const reason = settledResult.reason;
5833
4823
  agentFailures.push(reason);
5834
4824
  const retryDelay = rateLimitDelay(reason);
5835
- if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
4825
+ if (retryDelay !== void 0)
4826
+ agentRetryAt[agentId2] = Date.now() + retryDelay;
5836
4827
  agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
5837
- console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
5838
4828
  }
5839
4829
  if (successCount === 0) {
5840
4830
  throw new OwneyError(
@@ -5860,14 +4850,22 @@ var OwneySDK = class {
5860
4850
  const chainId = this.requireChainId();
5861
4851
  if (agentId) {
5862
4852
  const agent = this.getAgent(agentId);
5863
- return this.readAgent(agent, "earnings", () => agent.getEarnings(state, chainId));
4853
+ return this.readAgent(
4854
+ agent,
4855
+ "earnings",
4856
+ () => agent.getEarnings(state, chainId)
4857
+ );
5864
4858
  }
5865
4859
  let totalEarnings = 0;
5866
4860
  const results = {};
5867
4861
  const entries = [...this.getActiveAgents().entries()];
5868
4862
  const earningsResults = await Promise.all(
5869
4863
  entries.map(async ([id, agent]) => {
5870
- const e = await this.readAgent(agent, "earnings", () => agent.getEarnings(state, chainId));
4864
+ const e = await this.readAgent(
4865
+ agent,
4866
+ "earnings",
4867
+ () => agent.getEarnings(state, chainId)
4868
+ );
5871
4869
  return [id, e];
5872
4870
  })
5873
4871
  );
@@ -6012,11 +5010,12 @@ var OwneySDK = class {
6012
5010
  ),
6013
5011
  Promise.all(
6014
5012
  entries.map(async ([id, agent]) => {
6015
- const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
6016
- return [
6017
- id,
6018
- balanceForApyScope(b, chainId, tokenSymbol)
6019
- ];
5013
+ const b = await this.readAgent(
5014
+ agent,
5015
+ "balances",
5016
+ () => agent.getBalances(state, chainId)
5017
+ );
5018
+ return [id, balanceForApyScope(b, chainId, tokenSymbol)];
6020
5019
  })
6021
5020
  )
6022
5021
  ]);
@@ -6075,7 +5074,12 @@ var OwneySDK = class {
6075
5074
  const { agentId, filters } = options ?? {};
6076
5075
  if (agentId) {
6077
5076
  const agent = this.getAgent(agentId);
6078
- return this.readAgent(agent, "history", () => agent.getHistory(state, chainId, filters), filters);
5077
+ return this.readAgent(
5078
+ agent,
5079
+ "history",
5080
+ () => agent.getHistory(state, chainId, filters),
5081
+ filters
5082
+ );
6079
5083
  }
6080
5084
  const activeAgents = [...this.getActiveAgents().values()];
6081
5085
  const cursorMap = filters?.cursor ? decodeMultiAgentCursor(filters.cursor) : {};
@@ -6132,13 +5136,21 @@ var OwneySDK = class {
6132
5136
  const chainId = this.requireChainId();
6133
5137
  if (agentId) {
6134
5138
  const agent = this.getAgent(agentId);
6135
- return this.readAgent(agent, "profile", () => agent.getUserProfile(state, chainId));
5139
+ return this.readAgent(
5140
+ agent,
5141
+ "profile",
5142
+ () => agent.getUserProfile(state, chainId)
5143
+ );
6136
5144
  }
6137
5145
  const results = {};
6138
5146
  const entries = [...this.getActiveAgents().entries()];
6139
5147
  const profileResults = await Promise.all(
6140
5148
  entries.map(async ([id, agent]) => {
6141
- const p = await this.readAgent(agent, "profile", () => agent.getUserProfile(state, chainId));
5149
+ const p = await this.readAgent(
5150
+ agent,
5151
+ "profile",
5152
+ () => agent.getUserProfile(state, chainId)
5153
+ );
6142
5154
  return [id, p];
6143
5155
  })
6144
5156
  );
@@ -6177,47 +5189,43 @@ var OwneySDK = class {
6177
5189
  return pending;
6178
5190
  }
6179
5191
  /**
6180
- * User-paid approval of Permit2 on the selected token for the active chain.
6181
- * Grants the maximum ERC20 allowance so later deposits do not require another
6182
- * approval. Resolves after one confirmation so the subsequent deposit attempt
6183
- * sees the new allowance. Deposit retries pass their captured chain id so a
6184
- * concurrent activation cannot redirect the approval to another network.
6185
- *
6186
- * @param requiredAmount Raw base-unit amount the pending deposit must cover.
6187
- * @param expectedChainId Chain captured by the deposit that requested approval.
5192
+ * One-time, user-paid approval of Permit2 on the sponsored WETH token for
5193
+ * the active chain. Required once per wallet per chain before gasless WETH
5194
+ * deposits; afterwards deposit() is signature-only. Resolves only after the
5195
+ * approval transaction is mined (1 confirmation), so a subsequent deposit()
5196
+ * will see the new allowance; throws if the transaction reverted.
6188
5197
  * @returns the approval transaction hash.
6189
5198
  */
6190
- async approvePermit2(asset = "WETH", requiredAmount = 0n, expectedChainId) {
5199
+ async approvePermit2(asset = "WETH") {
5200
+ void asset;
6191
5201
  const state = this.requireState();
6192
- const chainId = expectedChainId ?? this.requireChainId();
6193
- this.getEligibleAgents(chainId, asset, { excludeDisabled: true });
6194
- const token = sponsoredTokensFor(asset)[chainId];
5202
+ const chainId = this.requireChainId();
5203
+ this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
5204
+ const token = SPONSORED_WETH_BY_CHAIN[chainId];
6195
5205
  if (!token) {
6196
5206
  throw new OwneyError(
6197
5207
  "CHAIN_UNSUPPORTED",
6198
- `No sponsored token on chain ${chainId}`
5208
+ `No sponsored WETH on chain ${chainId}`
6199
5209
  );
6200
5210
  }
6201
5211
  const provider = this.requireConnectedProvider();
6202
- const publicClient = (0, import_viem11.createPublicClient)({
6203
- chain: VIEM_CHAIN2[chainId],
6204
- transport: (0, import_viem11.custom)(provider)
6205
- });
6206
- const approvalAmount = permit2ApprovalAmount(requiredAmount);
6207
- const wallet = (0, import_viem11.createWalletClient)({
5212
+ const wallet = (0, import_viem8.createWalletClient)({
6208
5213
  account: state.walletAddress,
6209
5214
  chain: VIEM_CHAIN2[chainId],
6210
- transport: (0, import_viem11.custom)(provider)
5215
+ transport: (0, import_viem8.custom)(provider)
6211
5216
  });
6212
- await ensureWalletOnChain(publicClient, wallet, chainId);
6213
5217
  const hash = await wallet.writeContract({
6214
5218
  address: token,
6215
5219
  abi: ERC20_ALLOWANCE_ABI,
6216
5220
  functionName: "approve",
6217
- args: [PERMIT2_ADDRESS, approvalAmount],
5221
+ args: [PERMIT2_ADDRESS, MAX_UINT256],
6218
5222
  account: state.walletAddress,
6219
5223
  chain: VIEM_CHAIN2[chainId]
6220
5224
  });
5225
+ const publicClient = (0, import_viem8.createPublicClient)({
5226
+ chain: VIEM_CHAIN2[chainId],
5227
+ transport: (0, import_viem8.custom)(provider)
5228
+ });
6221
5229
  const receipt = await publicClient.waitForTransactionReceipt({
6222
5230
  hash,
6223
5231
  confirmations: 1
@@ -6225,42 +5233,7 @@ var OwneySDK = class {
6225
5233
  if (receipt.status !== "success") {
6226
5234
  throw new Error(`Permit2 approval reverted (tx ${hash})`);
6227
5235
  }
6228
- let observedAllowance = 0n;
6229
- let verificationError;
6230
- for (let attempt = 0; attempt < PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS; attempt += 1) {
6231
- try {
6232
- observedAllowance = await readPermit2Allowance(
6233
- publicClient,
6234
- token,
6235
- state.walletAddress,
6236
- attempt === 0 ? receipt.blockNumber : void 0
6237
- );
6238
- verificationError = void 0;
6239
- if (observedAllowance >= requiredAmount) return hash;
6240
- } catch (error) {
6241
- verificationError = error;
6242
- }
6243
- if (attempt + 1 < PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS) {
6244
- await new Promise(
6245
- (resolve) => setTimeout(resolve, PERMIT2_ALLOWANCE_VERIFY_DELAY_MS)
6246
- );
6247
- }
6248
- }
6249
- throw new OwneyError(
6250
- "PERMIT2_APPROVAL_REQUIRED",
6251
- "Permit2 approval was confirmed, but the required token allowance was not observable.",
6252
- {
6253
- approvalConfirmed: true,
6254
- approvalTxHash: hash,
6255
- owner: state.walletAddress,
6256
- token,
6257
- spender: PERMIT2_ADDRESS,
6258
- chainId,
6259
- requiredAmount: requiredAmount.toString(),
6260
- observedAllowance: observedAllowance.toString(),
6261
- ...verificationError instanceof Error ? { verificationError: verificationError.message } : {}
6262
- }
6263
- );
5236
+ return hash;
6264
5237
  }
6265
5238
  // --- Discovery (no wallet required) ---
6266
5239
  /**
@@ -6285,15 +5258,23 @@ var OwneySDK = class {
6285
5258
  const agentOptions = { tokenSymbol, chainId };
6286
5259
  if (agentId) {
6287
5260
  const agent = this.getAgent(agentId);
6288
- return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
5261
+ return this.readAgent(
5262
+ agent,
5263
+ "agentApy",
5264
+ () => agent.getAgentApy(days, agentOptions),
5265
+ { days, ...agentOptions }
5266
+ );
6289
5267
  }
6290
5268
  const results = {};
6291
- const agentEntries = [...this.agents.entries()].filter(
6292
- ([id]) => !this.isAgentDisabled(id)
6293
- );
5269
+ const agentEntries = [...this.agents.entries()];
6294
5270
  const apyResults = await Promise.all(
6295
5271
  agentEntries.map(async ([id, agent]) => {
6296
- const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
5272
+ const apy = await this.readAgent(
5273
+ agent,
5274
+ "agentApy",
5275
+ () => agent.getAgentApy(days, agentOptions),
5276
+ { days, ...agentOptions }
5277
+ );
6297
5278
  return [id, apy];
6298
5279
  })
6299
5280
  );
@@ -6320,7 +5301,11 @@ var OwneySDK = class {
6320
5301
  const entries = [...activeAgents.entries()];
6321
5302
  const balanceResults = await Promise.allSettled(
6322
5303
  entries.map(async ([id, agent]) => {
6323
- const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5304
+ const b = await this.readAgent(
5305
+ agent,
5306
+ "balances",
5307
+ () => agent.getBalances(state, chainId)
5308
+ );
6324
5309
  return [id, b.positions ?? []];
6325
5310
  })
6326
5311
  );
@@ -6365,13 +5350,13 @@ var OwneySDK = class {
6365
5350
  };
6366
5351
 
6367
5352
  // src/agents/zyfai/zyfai.siwx.ts
6368
- var import_viem12 = require("viem");
6369
- var import_siwe2 = require("siwe");
5353
+ var import_viem9 = require("viem");
5354
+ var import_siwe = require("siwe");
6370
5355
  var import_sdk2 = require("@zyfai/sdk");
6371
5356
 
6372
5357
  // src/agents/zyfai/zyfai.siwx-cache.ts
6373
- var KEY_PREFIX4 = "owney.siwx.session";
6374
- var storage4 = () => {
5358
+ var KEY_PREFIX3 = "owney.siwx.session";
5359
+ var storage3 = () => {
6375
5360
  if (typeof window === "undefined") return null;
6376
5361
  try {
6377
5362
  return window.localStorage;
@@ -6379,8 +5364,8 @@ var storage4 = () => {
6379
5364
  return null;
6380
5365
  }
6381
5366
  };
6382
- var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
6383
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
5367
+ var buildKey2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
5368
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
6384
5369
  var memorySiwxSessions = /* @__PURE__ */ new Map();
6385
5370
  var readLegacySiwxSession = (store, address) => {
6386
5371
  if (!store) return null;
@@ -6411,17 +5396,17 @@ var readLegacySiwxSession = (store, address) => {
6411
5396
  };
6412
5397
  var readSiwxSession = (address, chainId) => {
6413
5398
  if (typeof window === "undefined") return null;
6414
- const key2 = buildKey3(address);
6415
- const store = storage4();
6416
- let raw2 = null;
5399
+ const key2 = buildKey2(address);
5400
+ const store = storage3();
5401
+ let raw = null;
6417
5402
  try {
6418
- raw2 = store?.getItem(key2) ?? null;
5403
+ raw = store?.getItem(key2) ?? null;
6419
5404
  } catch {
6420
- raw2 = null;
5405
+ raw = null;
6421
5406
  }
6422
- if (raw2) {
5407
+ if (raw) {
6423
5408
  try {
6424
- return JSON.parse(raw2);
5409
+ return JSON.parse(raw);
6425
5410
  } catch {
6426
5411
  memorySiwxSessions.delete(key2);
6427
5412
  try {
@@ -6440,18 +5425,18 @@ var readSiwxSession = (address, chainId) => {
6440
5425
  };
6441
5426
  var writeSiwxSession = (address, _chainId, session) => {
6442
5427
  if (typeof window === "undefined") return;
6443
- const key2 = buildKey3(address);
5428
+ const key2 = buildKey2(address);
6444
5429
  memorySiwxSessions.set(key2, session);
6445
- const store = storage4();
5430
+ const store = storage3();
6446
5431
  try {
6447
5432
  store?.setItem(key2, JSON.stringify(session));
6448
5433
  } catch {
6449
5434
  }
6450
5435
  };
6451
5436
  var clearSiwxSession = (address, _chainId) => {
6452
- const key2 = buildKey3(address);
5437
+ const key2 = buildKey2(address);
6453
5438
  memorySiwxSessions.delete(key2);
6454
- const store = storage4();
5439
+ const store = storage3();
6455
5440
  try {
6456
5441
  store?.removeItem(key2);
6457
5442
  } catch {
@@ -6491,8 +5476,8 @@ function buildSIWXConfig(deps) {
6491
5476
  statement: STATEMENT,
6492
5477
  issuedAt,
6493
5478
  toString() {
6494
- return new import_siwe2.SiweMessage({
6495
- address: (0, import_viem12.getAddress)(accountAddress),
5479
+ return new import_siwe.SiweMessage({
5480
+ address: (0, import_viem9.getAddress)(accountAddress),
6496
5481
  chainId: numericChainId(chainId),
6497
5482
  domain,
6498
5483
  uri,
@@ -6534,7 +5519,7 @@ function buildSIWXConfig(deps) {
6534
5519
  const persistSession = async (session) => {
6535
5520
  const address = session.data.accountAddress;
6536
5521
  const id = numericChainId(session.data.chainId);
6537
- const message = new import_siwe2.SiweMessage(session.message);
5522
+ const message = new import_siwe.SiweMessage(session.message);
6538
5523
  const login = await post("/auth/login", {
6539
5524
  message,
6540
5525
  signature: session.signature,
@@ -6570,9 +5555,9 @@ function buildSIWXConfig(deps) {
6570
5555
  }
6571
5556
  function createOwneySIWX(config) {
6572
5557
  const zyfai = new import_sdk2.ZyfaiSDK({ apiKey: config.apiKey });
6573
- const http2 = zyfai.httpClient;
5558
+ const http4 = zyfai.httpClient;
6574
5559
  return buildSIWXConfig({
6575
- post: (url, data) => http2.post(url, data),
5560
+ post: (url, data) => http4.post(url, data),
6576
5561
  referralSource: config.referralSource
6577
5562
  });
6578
5563
  }
@@ -6584,7 +5569,7 @@ function createOwneySIWX(config) {
6584
5569
  NotConnectedError,
6585
5570
  OwneyError,
6586
5571
  OwneySDK,
6587
- YieldseekerAgent,
6588
5572
  createOwneySIWX,
5573
+ listPendingSwaps,
6589
5574
  setOwneyDebug
6590
5575
  });