@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.js CHANGED
@@ -1,23 +1,3 @@
1
- // src/lib/deposit-batch-callback.ts
2
- var batchCallbacks = /* @__PURE__ */ new WeakMap();
3
- var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
4
- function toBatchTransfer(to, amount, verification) {
5
- return {
6
- to,
7
- amount,
8
- ...verification ? {
9
- yieldseeker: {
10
- signature: verification.signature,
11
- userId: verification.userId,
12
- agentId: verification.yieldseekerAgentId
13
- }
14
- } : {}
15
- };
16
- }
17
- function registerDepositBatch(callback, transfer) {
18
- batchCallbacks.set(callback, transfer);
19
- }
20
-
21
1
  // src/errors.ts
22
2
  var OwneyError = class extends Error {
23
3
  code;
@@ -297,18 +277,18 @@ function tokenDecimals(symbol, explicit) {
297
277
  return explicit;
298
278
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
299
279
  }
300
- function mapDeposit(raw2) {
280
+ function mapDeposit(raw) {
301
281
  return {
302
- txHash: raw2.txHash,
303
- smartWallet: raw2.smartWallet,
304
- amount: raw2.amount
282
+ txHash: raw.txHash,
283
+ smartWallet: raw.smartWallet,
284
+ amount: raw.amount
305
285
  };
306
286
  }
307
- function mapWithdraw(raw2) {
287
+ function mapWithdraw(raw) {
308
288
  return {
309
- txHash: raw2.txHash,
310
- type: raw2.type,
311
- amount: raw2.amount
289
+ txHash: raw.txHash,
290
+ type: raw.type,
291
+ amount: raw.amount
312
292
  };
313
293
  }
314
294
  var CHAIN_ID_TO_NAME = {
@@ -327,10 +307,10 @@ function resolveChainId(chain) {
327
307
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
328
308
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
329
309
  }
330
- function mapPendingAllocations(raw2) {
331
- if (!Array.isArray(raw2)) return void 0;
310
+ function mapPendingAllocations(raw) {
311
+ if (!Array.isArray(raw)) return void 0;
332
312
  const pending = [];
333
- for (const entry of raw2) {
313
+ for (const entry of raw) {
334
314
  if (typeof entry !== "object" || entry === null) continue;
335
315
  const e = entry;
336
316
  if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
@@ -353,8 +333,8 @@ function mapPendingAllocations(raw2) {
353
333
  }
354
334
  return pending.length > 0 ? pending : void 0;
355
335
  }
356
- function mapBalances(raw2, _chainId, smartWallet) {
357
- const portfolio = raw2.portfolio;
336
+ function mapBalances(raw, _chainId, smartWallet) {
337
+ const portfolio = raw.portfolio;
358
338
  const portfolioByChain = portfolio.portfolioByChain ?? {};
359
339
  let totalBalance = 0;
360
340
  const tokens = [];
@@ -423,8 +403,8 @@ function sumTokenValues(tokens) {
423
403
  function sumTokenEarnings(tokens) {
424
404
  return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
425
405
  }
426
- function mapEarnings(raw2, smartWallet) {
427
- const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
406
+ function mapEarnings(raw, smartWallet) {
407
+ const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
428
408
  const tokens = [];
429
409
  for (const [chainIdKey, tokensBySymbol] of Object.entries(
430
410
  totalEarningsByChain
@@ -443,15 +423,15 @@ function mapEarnings(raw2, smartWallet) {
443
423
  return {
444
424
  smartWallet,
445
425
  lifetimeEarnings: sumTokenEarnings(
446
- raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
426
+ raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
447
427
  ),
448
428
  tokens
449
429
  };
450
430
  }
451
- function mapWeightedApyByChain(raw2) {
452
- if (!raw2) return void 0;
431
+ function mapWeightedApyByChain(raw) {
432
+ if (!raw) return void 0;
453
433
  const out = {};
454
- for (const [chainKey, tokenApy] of Object.entries(raw2)) {
434
+ for (const [chainKey, tokenApy] of Object.entries(raw)) {
455
435
  const chainId = Number(chainKey);
456
436
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
457
437
  const perAsset = {};
@@ -491,8 +471,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
491
471
  }
492
472
  return totalBalance > 0 ? weightedSum / totalBalance : null;
493
473
  }
494
- function mapApyHistory(raw2, chainId, tokenSymbol) {
495
- const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
474
+ function mapApyHistory(raw, chainId, tokenSymbol) {
475
+ const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
496
476
  date,
497
477
  apy: rawPoolApyForChain(entry, chainId, tokenSymbol),
498
478
  // Provider position balances are treated as decimal amounts of the
@@ -508,9 +488,9 @@ function mapApyHistory(raw2, chainId, tokenSymbol) {
508
488
  } : {}
509
489
  })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
510
490
  return {
511
- walletAddress: raw2.walletAddress,
512
- weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
513
- apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
491
+ walletAddress: raw.walletAddress,
492
+ weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
493
+ apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
514
494
  history
515
495
  };
516
496
  }
@@ -606,23 +586,23 @@ function mapEntries(rawEntries, chainId) {
606
586
  };
607
587
  });
608
588
  }
609
- function mapUserProfile(raw2, userAddress) {
589
+ function mapUserProfile(raw, userAddress) {
610
590
  return {
611
591
  address: userAddress,
612
- smartWallet: raw2.smartWallet || "",
613
- chains: raw2.chains || [],
614
- strategy: raw2.strategy,
615
- hasActiveSessionKey: raw2.hasActiveSessionKey || false,
616
- protocols: raw2.protocols || [],
617
- splitting: raw2.splitting,
618
- minSplits: raw2.minSplits
592
+ smartWallet: raw.smartWallet || "",
593
+ chains: raw.chains || [],
594
+ strategy: raw.strategy,
595
+ hasActiveSessionKey: raw.hasActiveSessionKey || false,
596
+ protocols: raw.protocols || [],
597
+ splitting: raw.splitting,
598
+ minSplits: raw.minSplits
619
599
  };
620
600
  }
621
- function mapApyByStrategy(raw2) {
601
+ function mapApyByStrategy(raw) {
622
602
  const apyPerAsset = {};
623
603
  let apySum = 0;
624
604
  let apyCount = 0;
625
- for (const entry of raw2.data) {
605
+ for (const entry of raw.data) {
626
606
  const supported = SupportedAssets.find(
627
607
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
628
608
  );
@@ -734,9 +714,9 @@ function netDeltaForSnapshot(entry, chainId, asset) {
734
714
  debugLog("zyfai:earnings", "gross fallback for daily earnings", { gross });
735
715
  return gross;
736
716
  }
737
- function mapDailyEarnings(raw2, chainId, tokenSymbol) {
717
+ function mapDailyEarnings(raw, chainId, tokenSymbol) {
738
718
  const wanted = tokenSymbol?.toUpperCase();
739
- const snapshots = [...raw2.data ?? []].sort(
719
+ const snapshots = [...raw.data ?? []].sort(
740
720
  (a, b) => a.snapshot_date.localeCompare(b.snapshot_date)
741
721
  );
742
722
  const byAsset = /* @__PURE__ */ new Map();
@@ -751,7 +731,7 @@ function mapDailyEarnings(raw2, chainId, tokenSymbol) {
751
731
  }
752
732
  }
753
733
  const assets = [...byAsset.entries()].map(([asset, series]) => ({ asset, points: series.points })).sort((a, b) => a.asset.localeCompare(b.asset));
754
- return { walletAddress: raw2.walletAddress, chainId, assets };
734
+ return { walletAddress: raw.walletAddress, chainId, assets };
755
735
  }
756
736
 
757
737
  // src/agents/zyfai/zyfai.withdraw-amount.ts
@@ -862,15 +842,15 @@ var readSession = (address, _chainId) => {
862
842
  if (typeof window === "undefined") return null;
863
843
  const key2 = buildKey(address);
864
844
  const store = storage();
865
- let raw2 = null;
845
+ let raw = null;
866
846
  try {
867
- raw2 = store?.getItem(key2) ?? null;
847
+ raw = store?.getItem(key2) ?? null;
868
848
  } catch {
869
- raw2 = null;
849
+ raw = null;
870
850
  }
871
- if (raw2) {
851
+ if (raw) {
872
852
  try {
873
- const parsed = JSON.parse(raw2);
853
+ const parsed = JSON.parse(raw);
874
854
  if (isFreshSession(parsed)) return parsed;
875
855
  } catch {
876
856
  }
@@ -1038,8 +1018,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
1038
1018
  }
1039
1019
  return result;
1040
1020
  }
1041
- function flattenAvailablePools(raw2) {
1042
- const byChain = raw2 ?? {};
1021
+ function flattenAvailablePools(raw) {
1022
+ const byChain = raw ?? {};
1043
1023
  const names = [];
1044
1024
  for (const byToken of Object.values(byChain ?? {})) {
1045
1025
  for (const entry of Object.values(byToken ?? {})) {
@@ -1542,8 +1522,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1542
1522
  const poolResults = await Promise.all(
1543
1523
  universe.map(async (protocol) => {
1544
1524
  try {
1545
- const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
1546
- return [protocol.id, flattenAvailablePools(raw2)];
1525
+ const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1526
+ return [protocol.id, flattenAvailablePools(raw)];
1547
1527
  } catch (error) {
1548
1528
  console.warn(
1549
1529
  `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
@@ -1609,14 +1589,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1609
1589
  async readWalletState(ownerAddress) {
1610
1590
  try {
1611
1591
  const { portfolio } = await this.sdk.getPositions(ownerAddress);
1612
- const raw2 = portfolio;
1592
+ const raw = portfolio;
1613
1593
  debugLog("zyfai:onboard", "wallet state from getPositions", {
1614
- predeployed: raw2?.predeployed,
1615
- hasActiveSessionKey: raw2?.hasActiveSessionKey
1594
+ predeployed: raw?.predeployed,
1595
+ hasActiveSessionKey: raw?.hasActiveSessionKey
1616
1596
  });
1617
1597
  return {
1618
- predeployed: raw2?.predeployed,
1619
- hasActiveSessionKey: raw2?.hasActiveSessionKey
1598
+ predeployed: raw?.predeployed,
1599
+ hasActiveSessionKey: raw?.hasActiveSessionKey
1620
1600
  };
1621
1601
  } catch (error) {
1622
1602
  console.warn(
@@ -1902,14 +1882,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1902
1882
  return { txHash, smartWallet, amount };
1903
1883
  }
1904
1884
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
1905
- const raw2 = await this.sdk.depositFunds(
1885
+ const raw = await this.sdk.depositFunds(
1906
1886
  this.getAddress(),
1907
1887
  validChainId,
1908
1888
  amount,
1909
1889
  asset,
1910
1890
  "aggressive"
1911
1891
  );
1912
- return mapDeposit(raw2);
1892
+ return mapDeposit(raw);
1913
1893
  } catch (error) {
1914
1894
  throw error;
1915
1895
  }
@@ -1918,27 +1898,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
1918
1898
  async withdraw(state, chainId, token, amount) {
1919
1899
  const validChainId = isValidChainId(chainId);
1920
1900
  await this.ensureConnected(state, validChainId);
1921
- const raw2 = await this.sdk.withdrawFunds(
1901
+ const raw = await this.sdk.withdrawFunds(
1922
1902
  this.getAddress(),
1923
1903
  validChainId,
1924
1904
  amount,
1925
1905
  token
1926
1906
  );
1927
- if (!raw2.success) {
1907
+ if (!raw.success) {
1928
1908
  throw new OwneyError(
1929
1909
  "WITHDRAW_FAILED",
1930
- raw2.message || "Zyfai withdraw failed.",
1931
- { chainId: validChainId, token, amount, response: raw2 },
1910
+ raw.message || "Zyfai withdraw failed.",
1911
+ { chainId: validChainId, token, amount, response: raw },
1932
1912
  this.id
1933
1913
  );
1934
1914
  }
1935
- return mapWithdraw(raw2);
1915
+ return mapWithdraw(raw);
1936
1916
  }
1937
1917
  // --- IAgent: Portfolio reads ---
1938
1918
  async getBalances(state, chainId) {
1939
1919
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
1940
- const raw2 = await this.sdk.getPortfolio(this.getAddress());
1941
- return mapBalances(raw2, validChainId, smartWallet);
1920
+ const raw = await this.sdk.getPortfolio(this.getAddress());
1921
+ return mapBalances(raw, validChainId, smartWallet);
1942
1922
  }
1943
1923
  earningsKey(state, chainId, smartWallet) {
1944
1924
  return JSON.stringify([
@@ -1951,11 +1931,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
1951
1931
  const existing = this.earningsReads.get(key2);
1952
1932
  if (existing) return existing;
1953
1933
  const generation = this.earningsGeneration;
1954
- const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw2) => {
1934
+ const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw) => {
1955
1935
  if (generation === this.earningsGeneration) {
1956
- this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
1936
+ this.earningsSnapshot = { key: key2, raw, at: Date.now() };
1957
1937
  }
1958
- return raw2;
1938
+ return raw;
1959
1939
  }).finally(() => {
1960
1940
  if (this.earningsReads.get(key2) === pending)
1961
1941
  this.earningsReads.delete(key2);
@@ -1965,11 +1945,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
1965
1945
  }
1966
1946
  async getEarnings(state, chainId) {
1967
1947
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1968
- const raw2 = await this.readEarnings(
1948
+ const raw = await this.readEarnings(
1969
1949
  this.earningsKey(state, chainId, smartWallet),
1970
1950
  smartWallet
1971
1951
  );
1972
- return mapEarnings(raw2, smartWallet);
1952
+ return mapEarnings(raw, smartWallet);
1973
1953
  }
1974
1954
  async refreshEarnings(state, chainId) {
1975
1955
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -1996,17 +1976,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
1996
1976
  }
1997
1977
  async getAccountApy(state, chainId, days, tokenSymbol) {
1998
1978
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1999
- const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
2000
- return mapApyHistory(raw2, chainId, tokenSymbol);
1979
+ const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
1980
+ return mapApyHistory(raw, chainId, tokenSymbol);
2001
1981
  }
2002
1982
  async getDailyEarnings(state, chainId, days, tokenSymbol) {
2003
1983
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
2004
1984
  const start = new Date(Date.now() - (DayFilterMapping[days] + 1) * 864e5);
2005
- const raw2 = await this.sdk.getDailyEarnings(
1985
+ const raw = await this.sdk.getDailyEarnings(
2006
1986
  smartWallet,
2007
1987
  start.toISOString().slice(0, 10)
2008
1988
  );
2009
- return mapDailyEarnings(raw2, chainId, tokenSymbol);
1989
+ return mapDailyEarnings(raw, chainId, tokenSymbol);
2010
1990
  }
2011
1991
  /**
2012
1992
  * Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
@@ -2041,7 +2021,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2041
2021
  const matched = [];
2042
2022
  let backendExhausted = false;
2043
2023
  for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
2044
- const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
2024
+ const raw = await this.sdk.getHistory(smartWallet, validChainId, {
2045
2025
  limit: backendPageSize,
2046
2026
  offset,
2047
2027
  fromDate: options?.fromDate,
@@ -2052,13 +2032,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
2052
2032
  // asset's rows and handing back a page that filters to nothing.
2053
2033
  assetType
2054
2034
  });
2055
- raw2.data.forEach((entry, idx) => {
2035
+ raw.data.forEach((entry, idx) => {
2056
2036
  if (entry.chainId === validChainId) {
2057
2037
  matched.push({ entry, rawIdx: offset + idx });
2058
2038
  }
2059
2039
  });
2060
- offset += raw2.data.length;
2061
- if (raw2.data.length < backendPageSize) {
2040
+ offset += raw.data.length;
2041
+ if (raw.data.length < backendPageSize) {
2062
2042
  backendExhausted = true;
2063
2043
  break;
2064
2044
  }
@@ -2080,18 +2060,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
2080
2060
  }
2081
2061
  async getUserProfile(state, chainId) {
2082
2062
  await this.connectAuth(state, chainId);
2083
- const raw2 = await this.sdk.getUserDetails();
2063
+ const raw = await this.sdk.getUserDetails();
2084
2064
  debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
2085
2065
  asset: "USDC (default \u2014 no asset passed)",
2086
- splitting: raw2.splitting,
2087
- minSplits: raw2.minSplits,
2088
- strategy: raw2.strategy,
2089
- chains: raw2.chains,
2090
- protocolCount: raw2.protocols?.length,
2091
- hasActiveSessionKey: raw2.hasActiveSessionKey,
2092
- smartWallet: raw2.smartWallet
2066
+ splitting: raw.splitting,
2067
+ minSplits: raw.minSplits,
2068
+ strategy: raw.strategy,
2069
+ chains: raw.chains,
2070
+ protocolCount: raw.protocols?.length,
2071
+ hasActiveSessionKey: raw.hasActiveSessionKey,
2072
+ smartWallet: raw.smartWallet
2093
2073
  });
2094
- return mapUserProfile(raw2, this.connectedAddress);
2074
+ return mapUserProfile(raw, this.connectedAddress);
2095
2075
  }
2096
2076
  async ensureAutoSelectProtocols(state, chainId, asset) {
2097
2077
  await this.connectAuth(state, chainId);
@@ -2110,28 +2090,80 @@ var ZyfaiAgent = class _ZyfaiAgent {
2110
2090
  }
2111
2091
  // --- IAgent: Discovery (no wallet required) ---
2112
2092
  async getAgentApy(days, options) {
2113
- const raw2 = await this.sdk.getAPYPerStrategy(
2093
+ const raw = await this.sdk.getAPYPerStrategy(
2114
2094
  false,
2115
2095
  DayFilterMapping[days],
2116
2096
  "aggressive",
2117
2097
  options?.chainId,
2118
2098
  options?.tokenSymbol
2119
2099
  );
2120
- return mapApyByStrategy(raw2);
2100
+ return mapApyByStrategy(raw);
2121
2101
  }
2122
2102
  };
2123
2103
 
2124
- // src/agents/yieldseeker/yieldseeker.agent.ts
2125
- import {
2126
- createPublicClient as createPublicClient3,
2127
- createWalletClient as createWalletClient2,
2128
- custom as custom2,
2129
- encodeFunctionData,
2130
- erc20Abi,
2131
- getAddress as getAddress2,
2132
- isAddress as isAddress2
2133
- } from "viem";
2134
- import { base as base3 } from "viem/chains";
2104
+ // src/lib/routing-api.ts
2105
+ var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2106
+ async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL) {
2107
+ const url = `${baseUrl}/api/v1/agent/org-config`;
2108
+ try {
2109
+ const res = await fetch(url, {
2110
+ method: "GET",
2111
+ headers: {
2112
+ "Content-Type": "application/json",
2113
+ "x-owney-api-key": `${apiKey}`
2114
+ }
2115
+ });
2116
+ if (!res.ok) {
2117
+ if (res.status !== 404) {
2118
+ console.warn(
2119
+ `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
2120
+ );
2121
+ }
2122
+ return null;
2123
+ }
2124
+ const json = await res.json();
2125
+ const policy = json.success ? json.data ?? null : null;
2126
+ debugLog(
2127
+ "owney-sdk",
2128
+ policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
2129
+ policy ?? void 0
2130
+ );
2131
+ return policy;
2132
+ } catch (error) {
2133
+ console.warn(
2134
+ "[owney-sdk] Could not read org agent config (non-fatal):",
2135
+ error instanceof Error ? error.message : String(error)
2136
+ );
2137
+ return null;
2138
+ }
2139
+ }
2140
+ async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
2141
+ const url = `${baseUrl}/api/v1/agent/keys`;
2142
+ const res = await fetch(url, {
2143
+ method: "GET",
2144
+ headers: {
2145
+ "Content-Type": "application/json",
2146
+ "x-owney-api-key": `${apiKey}`
2147
+ }
2148
+ });
2149
+ if (!res.ok) {
2150
+ const text = await res.text().catch(() => "");
2151
+ throw new OwneyError(
2152
+ "API_ROUTING_ERROR",
2153
+ `Routing API error ${res.status}: ${text}`,
2154
+ { statusCode: res.status, responseBody: text }
2155
+ );
2156
+ }
2157
+ const json = await res.json();
2158
+ if (!json.success) {
2159
+ throw new OwneyError(
2160
+ "API_ROUTING_FAILED",
2161
+ `Routing API request failed: ${json.message}`,
2162
+ { message: json.message }
2163
+ );
2164
+ }
2165
+ return json.data;
2166
+ }
2135
2167
 
2136
2168
  // src/lib/chain-guard.ts
2137
2169
  var CHAIN_NAMES = {
@@ -2168,132 +2200,167 @@ async function ensureWalletOnChain(pub, wallet, expected) {
2168
2200
  }
2169
2201
  }
2170
2202
 
2171
- // src/lib/transfer-auth.ts
2172
- import { bytesToHex } from "viem";
2173
-
2174
- // src/lib/sponsor-client.ts
2175
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2176
- async function postPaymasterIntent(input) {
2177
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2178
- let res;
2179
- try {
2180
- res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
2181
- method: "POST",
2182
- headers: {
2183
- "content-type": "application/json",
2184
- "x-owney-api-key": input.apiKey,
2185
- Authorization: `Signature ${input.yieldseekerSignature}`
2186
- },
2187
- body: JSON.stringify(input.body)
2188
- });
2189
- } catch (networkError) {
2203
+ // src/lib/swap/swap-api.ts
2204
+ async function request(baseUrl, apiKey, path, init) {
2205
+ const url = `${baseUrl}/api/v1/swap${path}`;
2206
+ const res = await fetch(url, {
2207
+ method: init?.method ?? "GET",
2208
+ headers: {
2209
+ "Content-Type": "application/json",
2210
+ "x-owney-api-key": apiKey
2211
+ },
2212
+ ...init ? { body: JSON.stringify(init.body) } : {}
2213
+ });
2214
+ if (!res.ok) {
2215
+ const text = await res.text().catch(() => "");
2216
+ if (res.status === 429) {
2217
+ throw new OwneyError(
2218
+ "SWAP_RATE_LIMITED",
2219
+ "Swap provider is rate limiting, retry shortly",
2220
+ { statusCode: res.status }
2221
+ );
2222
+ }
2223
+ if (res.status === 403) {
2224
+ throw new OwneyError(
2225
+ "SWAP_DISABLED",
2226
+ "Swap is not enabled for this organization",
2227
+ { statusCode: res.status }
2228
+ );
2229
+ }
2190
2230
  throw new OwneyError(
2191
- "SPONSOR_REQUEST_FAILED",
2192
- `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2193
- { cause: String(networkError), safeToFallback: true }
2231
+ "SWAP_REQUEST_FAILED",
2232
+ `Swap API error ${res.status}: ${text}`,
2233
+ { statusCode: res.status, responseBody: text }
2194
2234
  );
2195
2235
  }
2196
- const text = await res.text();
2197
- let parsed = null;
2198
- try {
2199
- parsed = JSON.parse(text);
2200
- } catch {
2201
- }
2202
- if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
2236
+ const json = await res.json();
2237
+ if (!json.success) {
2203
2238
  throw new OwneyError(
2204
- "SPONSOR_REQUEST_FAILED",
2205
- `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2206
- {
2207
- statusCode: res.status,
2208
- responseBody: text.slice(0, 500),
2209
- safeToFallback: true
2210
- }
2239
+ "SWAP_REQUEST_FAILED",
2240
+ `Swap API request failed: ${json.message ?? "unknown error"}`,
2241
+ { message: json.message }
2211
2242
  );
2212
2243
  }
2213
- return parsed.data;
2244
+ return json.data;
2214
2245
  }
2215
- async function getSponsorRelayerAddress(input) {
2216
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2217
- let res;
2218
- try {
2219
- res = await fetch(
2220
- `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2221
- {
2222
- headers: { "x-owney-api-key": input.apiKey }
2223
- }
2224
- );
2225
- } catch (networkError) {
2226
- throw new OwneyError(
2227
- "SPONSOR_REQUEST_FAILED",
2228
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2229
- { cause: String(networkError), safeToFallback: true }
2230
- );
2231
- }
2232
- const text = await res.text();
2233
- let parsed = null;
2234
- try {
2235
- parsed = JSON.parse(text);
2236
- } catch {
2237
- }
2238
- if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
2239
- throw new OwneyError(
2240
- "SPONSOR_REQUEST_FAILED",
2241
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2242
- {
2243
- statusCode: res.status,
2244
- responseBody: text.slice(0, 500),
2245
- safeToFallback: true
2246
+ function createSwapApi(baseUrl, apiKey) {
2247
+ return {
2248
+ /** Source assets the user may pay with, and each chain's deposit targets. */
2249
+ listTokens: () => request(baseUrl, apiKey, "/tokens"),
2250
+ /**
2251
+ * Search one chain's payable-with assets by name, symbol or address.
2252
+ *
2253
+ * Separate from `listTokens` on purpose: the full set is thousands of
2254
+ * tokens per chain, far too much to ship to a picker, so the short list
2255
+ * renders immediately and this reaches everything else on demand.
2256
+ *
2257
+ * Already filtered server-side to what a quote will accept, so anything
2258
+ * returned here can be paid with.
2259
+ */
2260
+ searchTokens: (params) => request(
2261
+ baseUrl,
2262
+ apiKey,
2263
+ `/tokens/search?${new URLSearchParams({
2264
+ chainId: String(params.chainId),
2265
+ query: params.query,
2266
+ ...params.limit === void 0 ? {} : { limit: String(params.limit) }
2267
+ }).toString()}`
2268
+ ),
2269
+ /**
2270
+ * `walletAddress` is required even though the routing API could not infer
2271
+ * it: the Fusion+ quoter binds a quote to whoever will sign the order and
2272
+ * rejects the request without it.
2273
+ */
2274
+ quote: (params) => request(baseUrl, apiKey, "/quote", {
2275
+ method: "POST",
2276
+ body: {
2277
+ srcChainId: params.from.chainId,
2278
+ srcSymbol: params.from.symbol,
2279
+ dstChainId: params.to.chainId,
2280
+ dstSymbol: params.to.symbol,
2281
+ amount: params.from.amount,
2282
+ walletAddress: params.walletAddress,
2283
+ ...params.direction ? { direction: params.direction } : {}
2246
2284
  }
2247
- );
2248
- }
2249
- return parsed.data.relayer;
2250
- }
2251
- async function postSponsorBatchTransfer(input) {
2252
- let res;
2253
- try {
2254
- res = await fetch(
2255
- `${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
2256
- {
2257
- method: "POST",
2258
- headers: {
2259
- "content-type": "application/json",
2260
- "x-owney-api-key": input.apiKey
2261
- },
2262
- body: JSON.stringify(input.body)
2285
+ }),
2286
+ /** Ready-to-send calldata for a same-chain swap. */
2287
+ swapTx: (params) => request(baseUrl, apiKey, "/tx", {
2288
+ method: "POST",
2289
+ body: {
2290
+ srcChainId: params.from.chainId,
2291
+ srcSymbol: params.from.symbol,
2292
+ dstChainId: params.to.chainId,
2293
+ dstSymbol: params.to.symbol,
2294
+ amount: params.from.amount,
2295
+ walletAddress: params.walletAddress,
2296
+ slippage: params.slippage,
2297
+ ...params.direction ? { direction: params.direction } : {}
2263
2298
  }
2264
- );
2265
- } catch {
2266
- throw new OwneyError(
2267
- "SPONSOR_REQUEST_FAILED",
2268
- "Deposit status is unknown. Retry the same amount to check it.",
2269
- { safeToFallback: false }
2270
- );
2271
- }
2272
- const parsed = await res.json().catch(() => null);
2273
- if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
2274
- throw new OwneyError(
2275
- "SPONSOR_REQUEST_FAILED",
2276
- "Deposit could not be confirmed. Retry the same amount to check its status.",
2277
- {
2278
- statusCode: res.status,
2279
- safeToFallback: false,
2280
- notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
2299
+ }),
2300
+ /**
2301
+ * Builds a Fusion+ order server-side and returns EIP-712 typed data.
2302
+ *
2303
+ * Only HASHES go over the wire. The preimages never leave the browser —
2304
+ * see swap.secrets.
2305
+ */
2306
+ buildOrder: (params) => request(baseUrl, apiKey, "/order/build", {
2307
+ method: "POST",
2308
+ body: {
2309
+ srcChainId: params.from.chainId,
2310
+ srcSymbol: params.from.symbol,
2311
+ dstChainId: params.to.chainId,
2312
+ dstSymbol: params.to.symbol,
2313
+ amount: params.from.amount,
2314
+ walletAddress: params.walletAddress,
2315
+ secretHashes: params.secretHashes,
2316
+ ...params.direction ? { direction: params.direction } : {},
2317
+ ...params.receiver ? { receiver: params.receiver } : {}
2281
2318
  }
2282
- );
2283
- }
2284
- return parsed.data;
2319
+ }),
2320
+ submitOrder: (body) => request(baseUrl, apiKey, "/order", { method: "POST", body }),
2321
+ /**
2322
+ * Only call once `readyForSecrets` reports the escrow deployed. Publishing
2323
+ * earlier hands a resolver the preimage while the user's funds are locked
2324
+ * and nothing has been posted on the destination chain.
2325
+ */
2326
+ submitSecret: (orderHash, secret) => request(baseUrl, apiKey, "/order/secret", {
2327
+ method: "POST",
2328
+ body: { orderHash, secret }
2329
+ }),
2330
+ orderStatus: (orderHash) => request(baseUrl, apiKey, `/order/${orderHash}`),
2331
+ readyForSecrets: (orderHash) => request(
2332
+ baseUrl,
2333
+ apiKey,
2334
+ `/order/${orderHash}/ready-for-secrets`
2335
+ )
2336
+ };
2337
+ }
2338
+
2339
+ // src/lib/swap/swap.rpc.ts
2340
+ import { fallback, http as http2 } from "viem";
2341
+ var DEFAULT_RPC_URLS = {
2342
+ 1: ["https://cloudflare-eth.com", "https://ethereum-rpc.publicnode.com"],
2343
+ 8453: ["https://mainnet.base.org", "https://base-rpc.publicnode.com"],
2344
+ 42161: [
2345
+ "https://arb1.arbitrum.io/rpc",
2346
+ "https://arbitrum-one-rpc.publicnode.com"
2347
+ ]
2348
+ };
2349
+ function swapReadTransport(chainId, overrides) {
2350
+ const override = overrides?.[chainId];
2351
+ if (override) return http2(override);
2352
+ const urls = DEFAULT_RPC_URLS[chainId];
2353
+ if (!urls || urls.length === 0) return http2();
2354
+ return fallback(urls.map((url) => http2(url)));
2355
+ }
2356
+ function receiptTimeoutMs(chainId) {
2357
+ return chainId === 1 ? 6e5 : 18e4;
2285
2358
  }
2286
2359
 
2287
2360
  // src/lib/permit2.ts
2288
- import { bytesToHex as bytesToHex2 } from "viem";
2361
+ import { bytesToHex } from "viem";
2289
2362
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2290
2363
  var MAX_UINT256 = 2n ** 256n - 1n;
2291
- function permit2ApprovalAmount(requiredAmount) {
2292
- if (requiredAmount <= 0n) {
2293
- throw new Error("Permit2 approval requires a positive deposit amount");
2294
- }
2295
- return MAX_UINT256;
2296
- }
2297
2364
  var ERC20_ALLOWANCE_ABI = [
2298
2365
  {
2299
2366
  type: "function",
@@ -2323,18 +2390,40 @@ var ERC20_ALLOWANCE_ABI = [
2323
2390
  outputs: [{ name: "", type: "uint256" }]
2324
2391
  }
2325
2392
  ];
2393
+ function buildPermitTransferFromTypedData(input) {
2394
+ return {
2395
+ domain: {
2396
+ name: "Permit2",
2397
+ chainId: input.chainId,
2398
+ verifyingContract: PERMIT2_ADDRESS
2399
+ },
2400
+ types: {
2401
+ PermitTransferFrom: [
2402
+ { name: "permitted", type: "TokenPermissions" },
2403
+ { name: "spender", type: "address" },
2404
+ { name: "nonce", type: "uint256" },
2405
+ { name: "deadline", type: "uint256" }
2406
+ ],
2407
+ TokenPermissions: [
2408
+ { name: "token", type: "address" },
2409
+ { name: "amount", type: "uint256" }
2410
+ ]
2411
+ },
2412
+ primaryType: "PermitTransferFrom",
2413
+ message: input.message
2414
+ };
2415
+ }
2326
2416
  function randomPermit2Nonce() {
2327
2417
  const bytes = new Uint8Array(32);
2328
2418
  globalThis.crypto.getRandomValues(bytes);
2329
- return BigInt(bytesToHex2(bytes));
2419
+ return BigInt(bytesToHex(bytes));
2330
2420
  }
2331
- async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
2421
+ async function readPermit2Allowance(publicClient, token, owner) {
2332
2422
  return publicClient.readContract({
2333
2423
  address: token,
2334
2424
  abi: ERC20_ALLOWANCE_ABI,
2335
2425
  functionName: "allowance",
2336
- args: [owner, PERMIT2_ADDRESS],
2337
- ...blockNumber === void 0 ? {} : { blockNumber }
2426
+ args: [owner, PERMIT2_ADDRESS]
2338
2427
  });
2339
2428
  }
2340
2429
  async function readErc20Balance(publicClient, token, owner) {
@@ -2346,44 +2435,122 @@ async function readErc20Balance(publicClient, token, owner) {
2346
2435
  });
2347
2436
  }
2348
2437
 
2349
- // src/lib/sponsored-deposit.ts
2350
- var AUTH_WINDOW_SECONDS = 15 * 60;
2351
- var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
2352
- function provideDepositVerificationContext(callback, context) {
2353
- callback[verificationSetter]?.(context);
2438
+ // src/lib/swap/swap.secrets.ts
2439
+ import { keccak256, toHex } from "viem";
2440
+ var SECRET_BYTES = 32;
2441
+ function randomBytes(length) {
2442
+ const bytes = new Uint8Array(length);
2443
+ const cryptoObj = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
2444
+ if (!cryptoObj?.getRandomValues) {
2445
+ throw new Error(
2446
+ "[owney-sdk] Secure randomness is unavailable, so a swap secret cannot be generated safely."
2447
+ );
2448
+ }
2449
+ cryptoObj.getRandomValues(bytes);
2450
+ return bytes;
2354
2451
  }
2355
- function makeVerificationAwareDepositCallback(implementation) {
2356
- let nextVerification;
2357
- const callback = async (smartWallet, chainId, amount) => {
2358
- const verification = nextVerification;
2359
- nextVerification = void 0;
2360
- return implementation(smartWallet, chainId, amount, verification);
2361
- };
2362
- Object.defineProperty(callback, verificationSetter, {
2363
- value: (context) => {
2364
- nextVerification = context;
2365
- }
2366
- });
2367
- return callback;
2452
+ function mintSecrets(count) {
2453
+ if (!Number.isInteger(count) || count < 1) {
2454
+ throw new Error(
2455
+ `[owney-sdk] A swap needs at least one secret, got ${String(count)}.`
2456
+ );
2457
+ }
2458
+ const secrets = [];
2459
+ const secretHashes = [];
2460
+ for (let i = 0; i < count; i++) {
2461
+ const secret = toHex(randomBytes(SECRET_BYTES));
2462
+ secrets.push(secret);
2463
+ secretHashes.push(keccak256(secret));
2464
+ }
2465
+ return { secrets, secretHashes };
2368
2466
  }
2369
2467
 
2370
- // src/agents/yieldseeker/yieldseeker.auth.ts
2371
- import { SiweMessage, generateNonce } from "siwe";
2372
- import {
2373
- createPublicClient as createPublicClient2,
2374
- createWalletClient,
2375
- custom,
2376
- getAddress
2377
- } from "viem";
2378
- import { base as base2 } from "viem/chains";
2379
-
2380
- // src/agents/yieldseeker/yieldseeker.auth-cache.ts
2381
- var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
2382
- var INVALIDATED_KEY_PREFIXES = [
2383
- "owney.yieldseeker.session",
2384
- "owney.yieldseeker.session.v3",
2385
- "owney.yieldseeker.session.v4"
2468
+ // src/lib/swap/swap.types.ts
2469
+ var SWAP_TERMINAL_STATUSES = [
2470
+ "executed",
2471
+ "expired",
2472
+ "cancelled",
2473
+ "refunded"
2386
2474
  ];
2475
+ var isSwapTerminal = (status) => SWAP_TERMINAL_STATUSES.includes(status);
2476
+
2477
+ // src/lib/swap/swap.order-runner.ts
2478
+ var DEFAULT_POLL_MS = 5e3;
2479
+ var MAX_BACKOFF_MS = 3e4;
2480
+ var backoffFor = (failures, base3) => Math.min(base3 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS);
2481
+ var DEFAULT_TIMEOUT_MS = 15 * 60 * 1e3;
2482
+ async function runFusionOrder(deps, options) {
2483
+ const {
2484
+ orderHash,
2485
+ secrets,
2486
+ onStage,
2487
+ pollIntervalMs = DEFAULT_POLL_MS,
2488
+ timeoutMs = DEFAULT_TIMEOUT_MS
2489
+ } = options;
2490
+ const deadline = deps.now() + timeoutMs;
2491
+ let failures = 0;
2492
+ const published = /* @__PURE__ */ new Set();
2493
+ onStage?.("swapping");
2494
+ for (; ; ) {
2495
+ if (deps.now() >= deadline) {
2496
+ throw new OwneyError(
2497
+ "SWAP_REQUEST_FAILED",
2498
+ "Timed out waiting for the swap to settle. It may still complete \u2014 check the order status before retrying.",
2499
+ { orderHash }
2500
+ );
2501
+ }
2502
+ let ready;
2503
+ try {
2504
+ ready = await deps.readyForSecrets(orderHash);
2505
+ } catch {
2506
+ ready = {};
2507
+ }
2508
+ for (const fill of ready.fills ?? []) {
2509
+ if (published.has(fill.idx)) continue;
2510
+ const secret = secrets[fill.idx];
2511
+ if (secret === void 0) {
2512
+ throw new OwneyError(
2513
+ "SWAP_REQUEST_FAILED",
2514
+ `Swap needs a secret for fill ${fill.idx} that this session does not have. The order will refund once its timelock expires.`,
2515
+ { orderHash, fillIndex: fill.idx }
2516
+ );
2517
+ }
2518
+ try {
2519
+ await deps.submitSecret(orderHash, secret);
2520
+ published.add(fill.idx);
2521
+ } catch {
2522
+ failures += 1;
2523
+ }
2524
+ }
2525
+ let status;
2526
+ try {
2527
+ ({ status } = await deps.orderStatus(orderHash));
2528
+ failures = 0;
2529
+ } catch {
2530
+ failures += 1;
2531
+ await deps.sleep(backoffFor(failures, pollIntervalMs));
2532
+ continue;
2533
+ }
2534
+ if (status === "refunding") onStage?.("refunding");
2535
+ if (isSwapTerminal(status)) {
2536
+ if (status === "executed") {
2537
+ onStage?.("swapped");
2538
+ return { status, filled: true };
2539
+ }
2540
+ if (status === "refunded") onStage?.("refunded");
2541
+ throw new OwneyError(
2542
+ status === "refunded" ? "SWAP_ORDER_REFUNDED" : status === "cancelled" ? "SWAP_ORDER_CANCELLED" : "SWAP_ORDER_EXPIRED",
2543
+ 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.",
2544
+ { orderHash, status }
2545
+ );
2546
+ }
2547
+ await deps.sleep(pollIntervalMs);
2548
+ }
2549
+ }
2550
+
2551
+ // src/lib/swap/swap.secret-store.ts
2552
+ var KEY_PREFIX2 = "owney.swap.order";
2553
+ var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
2387
2554
  var storage2 = () => {
2388
2555
  if (typeof window === "undefined") return null;
2389
2556
  try {
@@ -2392,1649 +2559,287 @@ var storage2 = () => {
2392
2559
  return null;
2393
2560
  }
2394
2561
  };
2395
- var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2396
- var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
2397
- (prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
2398
- );
2399
- var clearInvalidatedSessions = (store, address, chainId) => {
2400
- for (const key2 of invalidatedKeys(address, chainId)) {
2401
- memorySessions2.delete(key2);
2402
- try {
2403
- store?.removeItem(key2);
2404
- } catch {
2405
- }
2406
- }
2407
- };
2408
- var memorySessions2 = /* @__PURE__ */ new Map();
2409
- var isValidSession = (session) => {
2410
- if (!session?.token) return false;
2562
+ var keyFor = (orderHash) => `${KEY_PREFIX2}.${orderHash}`;
2563
+ function saveOrder(order) {
2564
+ const store = storage2();
2565
+ if (!store) return;
2411
2566
  try {
2412
- const parsed = JSON.parse(atob(session.token));
2413
- return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
2567
+ store.setItem(keyFor(order.orderHash), JSON.stringify(order));
2414
2568
  } catch {
2415
- return false;
2416
2569
  }
2417
- };
2418
- var readYieldseekerSession = (address, chainId) => {
2419
- if (typeof window === "undefined") return null;
2420
- const key2 = buildKey2(address, chainId);
2570
+ }
2571
+ function clearOrder(orderHash) {
2421
2572
  const store = storage2();
2422
- clearInvalidatedSessions(store, address, chainId);
2423
- let raw2 = null;
2573
+ if (!store) return;
2424
2574
  try {
2425
- raw2 = store?.getItem(key2) ?? null;
2575
+ store.removeItem(keyFor(orderHash));
2426
2576
  } catch {
2427
- raw2 = null;
2428
2577
  }
2429
- if (raw2) {
2430
- try {
2431
- const parsed = JSON.parse(raw2);
2432
- if (isValidSession(parsed)) return parsed.token;
2433
- } catch {
2578
+ }
2579
+ function listOrders(now = Date.now()) {
2580
+ const store = storage2();
2581
+ if (!store) return [];
2582
+ const out = [];
2583
+ try {
2584
+ const keys = [];
2585
+ for (let i = 0; i < store.length; i++) {
2586
+ const key2 = store.key(i);
2587
+ if (key2?.startsWith(`${KEY_PREFIX2}.`)) keys.push(key2);
2434
2588
  }
2435
- memorySessions2.delete(key2);
2436
- try {
2437
- store?.removeItem(key2);
2438
- } catch {
2439
- }
2440
- return null;
2441
- }
2442
- const cached = memorySessions2.get(key2);
2443
- if (isValidSession(cached)) return cached.token;
2444
- if (cached) memorySessions2.delete(key2);
2445
- return null;
2446
- };
2447
- var writeYieldseekerSession = (address, chainId, token) => {
2448
- if (typeof window === "undefined") return;
2449
- const session = { token };
2450
- if (!isValidSession(session)) return;
2451
- const key2 = buildKey2(address, chainId);
2452
- memorySessions2.set(key2, session);
2453
- const store = storage2();
2454
- try {
2455
- store?.setItem(key2, JSON.stringify(session));
2456
- } catch {
2457
- }
2458
- };
2459
- var clearYieldseekerSession = (address, chainId) => {
2460
- const key2 = buildKey2(address, chainId);
2461
- memorySessions2.delete(key2);
2462
- const store = storage2();
2463
- clearInvalidatedSessions(store, address, chainId);
2464
- try {
2465
- store?.removeItem(key2);
2466
- } catch {
2467
- }
2468
- };
2469
-
2470
- // src/agents/yieldseeker/yieldseeker.auth.ts
2471
- function resolveSiweOrigin(override) {
2472
- const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
2473
- if (!origin || origin === "null") {
2474
- throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
2475
- }
2476
- const url = new URL(origin);
2477
- if (url.protocol !== "https:" && url.protocol !== "http:") {
2478
- throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
2479
- }
2480
- if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
2481
- throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
2482
- }
2483
- return url;
2484
- }
2485
- function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2486
- const url = resolveSiweOrigin(dependencies.origin);
2487
- return new SiweMessage({
2488
- scheme: url.protocol.slice(0, -1),
2489
- domain: url.host,
2490
- address: getAddress(address),
2491
- uri: url.origin,
2492
- version: "1",
2493
- chainId,
2494
- nonce: (dependencies.nonce ?? generateNonce)(),
2495
- issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
2496
- }).prepareMessage();
2497
- }
2498
- function encodeYieldseekerAuthToken(token) {
2499
- const bytes = new TextEncoder().encode(JSON.stringify(token));
2500
- let binary = "";
2501
- for (const byte of bytes) binary += String.fromCharCode(byte);
2502
- return btoa(binary);
2503
- }
2504
- var YieldseekerAuth = class {
2505
- constructor(dependencies = {}) {
2506
- this.dependencies = dependencies;
2507
- }
2508
- dependencies;
2509
- tokens = /* @__PURE__ */ new Map();
2510
- pending = /* @__PURE__ */ new Map();
2511
- scopes = /* @__PURE__ */ new Map();
2512
- key(state, chainId) {
2513
- return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
2514
- }
2515
- async getToken(state, chainId) {
2516
- const key2 = this.key(state, chainId);
2517
- const scope = { address: state.walletAddress, chainId };
2518
- this.scopes.set(key2, scope);
2519
- const cached = this.tokens.get(key2);
2520
- if (cached) return cached;
2521
- const persisted = readYieldseekerSession(scope.address, scope.chainId);
2522
- if (persisted && this.matchesOrigin(persisted)) {
2523
- this.tokens.set(key2, persisted);
2524
- return persisted;
2525
- }
2526
- if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
2527
- const inFlight = this.pending.get(key2);
2528
- if (inFlight) return inFlight;
2529
- const request = this.sign(state, chainId).then((token) => {
2530
- if (this.pending.get(key2) !== request) throw new Error("Wallet sign-in session changed. Please try again.");
2531
- this.tokens.set(key2, token);
2532
- writeYieldseekerSession(scope.address, scope.chainId, token);
2533
- return token;
2534
- });
2535
- this.pending.set(key2, request);
2536
- try {
2537
- return await request;
2538
- } finally {
2539
- if (this.pending.get(key2) === request) this.pending.delete(key2);
2540
- }
2541
- }
2542
- async refreshToken(state, chainId, rejectedToken) {
2543
- const key2 = this.key(state, chainId);
2544
- if (this.tokens.get(key2) === rejectedToken) {
2545
- this.tokens.delete(key2);
2546
- clearYieldseekerSession(state.walletAddress, chainId);
2547
- }
2548
- return this.getToken(state, chainId);
2549
- }
2550
- matchesOrigin(token) {
2551
- try {
2552
- const message = new SiweMessage(JSON.parse(atob(token)).message);
2553
- const url = resolveSiweOrigin(this.dependencies.origin);
2554
- return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
2555
- } catch {
2556
- return false;
2557
- }
2558
- }
2559
- clear(state, chainId) {
2560
- if (!state || chainId === void 0) {
2561
- for (const scope of this.scopes.values()) {
2562
- clearYieldseekerSession(scope.address, scope.chainId);
2589
+ for (const key2 of keys) {
2590
+ const raw = store.getItem(key2);
2591
+ if (!raw) continue;
2592
+ try {
2593
+ const parsed = JSON.parse(raw);
2594
+ if (now - parsed.createdAt > MAX_AGE_MS) {
2595
+ store.removeItem(key2);
2596
+ continue;
2597
+ }
2598
+ if (Array.isArray(parsed.secrets) && parsed.secrets.length > 0) {
2599
+ out.push(parsed);
2600
+ }
2601
+ } catch {
2602
+ store.removeItem(key2);
2563
2603
  }
2564
- this.tokens.clear();
2565
- this.pending.clear();
2566
- this.scopes.clear();
2567
- return;
2568
2604
  }
2569
- const key2 = this.key(state, chainId);
2570
- this.tokens.delete(key2);
2571
- this.pending.delete(key2);
2572
- this.scopes.delete(key2);
2573
- clearYieldseekerSession(state.walletAddress, chainId);
2574
- }
2575
- async sign(state, chainId) {
2576
- const account = getAddress(state.walletAddress);
2577
- const publicClient = createPublicClient2({
2578
- chain: base2,
2579
- transport: custom(state.provider)
2580
- });
2581
- const walletClient = createWalletClient({
2582
- account,
2583
- chain: base2,
2584
- transport: custom(state.provider)
2585
- });
2586
- await ensureWalletOnChain(
2587
- publicClient,
2588
- walletClient,
2589
- 8453
2590
- );
2591
- const message = createYieldseekerSiweMessage(
2592
- account,
2593
- chainId,
2594
- this.dependencies
2595
- );
2596
- const signature = await walletClient.signMessage({ account, message });
2597
- return encodeYieldseekerAuthToken({ message, signature });
2598
- }
2599
- };
2600
-
2601
- // src/agents/yieldseeker/yieldseeker.identity-cache.ts
2602
- var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
2603
- var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
2604
- var memoryIdentities = /* @__PURE__ */ new Map();
2605
- var storage3 = () => {
2606
- if (typeof window === "undefined") return null;
2607
- try {
2608
- return window.localStorage;
2609
- } catch {
2610
- return null;
2611
- }
2612
- };
2613
- var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
2614
- function valid(value, walletAddress, chainId, now) {
2615
- return Boolean(
2616
- 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
2617
- );
2618
- }
2619
- function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
2620
- if (typeof window === "undefined") return null;
2621
- const key2 = keyFor(walletAddress, chainId);
2622
- const store = storage3();
2623
- let parsed = null;
2624
- try {
2625
- const raw2 = store?.getItem(key2);
2626
- parsed = raw2 ? JSON.parse(raw2) : null;
2627
- } catch {
2628
- parsed = null;
2629
- }
2630
- const candidate = parsed ?? memoryIdentities.get(key2);
2631
- if (valid(candidate, walletAddress, chainId, now)) {
2632
- memoryIdentities.set(key2, candidate);
2633
- return { userId: candidate.userId };
2634
- }
2635
- memoryIdentities.delete(key2);
2636
- try {
2637
- store?.removeItem(key2);
2638
- } catch {
2639
- }
2640
- return null;
2641
- }
2642
- function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
2643
- if (typeof window === "undefined") return;
2644
- const identity = {
2645
- userId,
2646
- walletAddress,
2647
- chainId,
2648
- expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
2649
- };
2650
- if (!valid(identity, walletAddress, chainId, now)) return;
2651
- const key2 = keyFor(walletAddress, chainId);
2652
- memoryIdentities.set(key2, identity);
2653
- try {
2654
- storage3()?.setItem(key2, JSON.stringify(identity));
2655
- } catch {
2656
- }
2657
- }
2658
- function clearYieldseekerIdentity(walletAddress, chainId) {
2659
- const key2 = keyFor(walletAddress, chainId);
2660
- memoryIdentities.delete(key2);
2661
- try {
2662
- storage3()?.removeItem(key2);
2663
2605
  } catch {
2606
+ return out;
2664
2607
  }
2608
+ return out.sort((a, b) => b.createdAt - a.createdAt);
2665
2609
  }
2666
2610
 
2667
- // src/agents/yieldseeker/yieldseeker.client.ts
2668
- var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2669
- function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
2670
- return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
2671
- }
2672
- var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
2673
- var YieldseekerApiError = class extends Error {
2674
- constructor(status, providerCode, responseFields) {
2675
- super(`Yieldseeker request failed (${status}): ${providerCode}`);
2676
- this.status = status;
2677
- this.providerCode = providerCode;
2678
- this.responseFields = responseFields;
2679
- this.name = "YieldseekerApiError";
2680
- }
2681
- status;
2682
- providerCode;
2683
- responseFields;
2684
- get isAuthenticationError() {
2685
- return this.status === 401 || this.status === 403;
2686
- }
2687
- };
2688
- function providerError(body, fallback) {
2689
- if (!body || typeof body !== "object") return { code: fallback };
2690
- const record = body;
2691
- return {
2692
- code: typeof record.message === "string" ? record.message : fallback,
2693
- fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
2694
- };
2695
- }
2696
- var YieldseekerApiClient = class {
2697
- constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch.bind(globalThis)) {
2698
- this.owneyApiKey = owneyApiKey;
2699
- this.baseUrl = baseUrl;
2700
- this.fetchFn = fetchFn;
2701
- }
2702
- owneyApiKey;
2703
- baseUrl;
2704
- fetchFn;
2705
- async request(path, options = {}) {
2706
- const controller = new AbortController();
2707
- const timer = setTimeout(
2708
- () => controller.abort(),
2709
- options.timeoutMs ?? 15e3
2710
- );
2711
- try {
2712
- const response = await this.fetchFn(`${this.baseUrl}${path}`, {
2713
- method: options.method ?? "GET",
2714
- headers: {
2715
- "Content-Type": "application/json",
2716
- "x-owney-api-key": this.owneyApiKey,
2717
- ...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
2718
- },
2719
- body: options.body ? JSON.stringify(options.body) : void 0,
2720
- signal: controller.signal
2721
- });
2722
- const payload = await response.json().catch(() => null);
2723
- if (!response.ok) {
2724
- const error = providerError(payload, `HTTP_${response.status}`);
2725
- throw new YieldseekerApiError(
2726
- response.status,
2727
- error.code,
2728
- error.fields
2729
- );
2730
- }
2731
- if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
2732
- return payload.data;
2733
- }
2734
- return payload;
2735
- } catch (error) {
2736
- if (error instanceof YieldseekerApiError) throw error;
2737
- if (error instanceof DOMException && error.name === "AbortError") {
2738
- throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
2739
- }
2740
- throw new YieldseekerApiError(0, "NETWORK_ERROR", {
2741
- cause: error instanceof Error ? error.message : String(error)
2742
- });
2743
- } finally {
2744
- clearTimeout(timer);
2745
- }
2746
- }
2747
- };
2748
-
2749
- // src/agents/yieldseeker/yieldseeker.mapper.ts
2750
- import { formatUnits, isAddress } from "viem";
2751
-
2752
- // src/lib/helpers/snapshot-apy.ts
2753
- var DAY_MS = 864e5;
2754
- function snapshotTime(date) {
2755
- if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return void 0;
2756
- const time = Date.parse(date);
2757
- return Number.isFinite(time) && new Date(time).toISOString().slice(0, 10) === date ? time : void 0;
2758
- }
2759
- function returnFactor(value) {
2760
- if (typeof value !== "number" && typeof value !== "string") return void 0;
2761
- if (typeof value === "string" && value.trim() === "") return void 0;
2762
- const factor = Number(value);
2763
- return Number.isFinite(factor) && factor >= 0 ? factor : void 0;
2764
- }
2765
- function calculateSnapshotApy(snapshots, lookbackDays, now = Date.now()) {
2766
- if (!Number.isFinite(lookbackDays) || lookbackDays <= 0 || !Number.isFinite(now)) {
2767
- return void 0;
2768
- }
2769
- const points = snapshots.flatMap((snapshot) => {
2770
- const time = snapshotTime(snapshot.date);
2771
- return time !== void 0 && time <= now ? [{ snapshot, time }] : [];
2772
- }).sort((a, b) => a.time - b.time);
2773
- const end = points.at(-1);
2774
- if (!end) return void 0;
2775
- const cutoff = end.time - lookbackDays * DAY_MS;
2776
- const start = points.find((point) => point.time >= cutoff);
2777
- const actualDays = (end.time - start.time) / DAY_MS;
2778
- if (actualDays <= 0) return void 0;
2779
- const startFactor = returnFactor(start.snapshot.cumulativeReturnFactor);
2780
- const endFactor = returnFactor(end.snapshot.cumulativeReturnFactor);
2781
- if (startFactor === void 0 || startFactor === 0 || endFactor === void 0) {
2782
- return void 0;
2783
- }
2784
- const periodReturn = endFactor / startFactor - 1;
2785
- const apy = Math.pow(1 + periodReturn, 365 / actualDays) - 1;
2786
- return Number.isFinite(apy) ? apy : void 0;
2787
- }
2788
-
2789
- // src/agents/yieldseeker/yieldseeker.types.ts
2790
- var YIELDSEEKER_ASSET_METADATA = {
2791
- USDC: {
2792
- address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2793
- decimals: 6
2794
- },
2795
- WETH: {
2796
- address: "0x4200000000000000000000000000000000000006",
2797
- decimals: 18
2798
- }
2799
- };
2800
-
2801
- // src/agents/yieldseeker/yieldseeker.mapper.ts
2802
- function invalid(endpoint, detail) {
2803
- throw new OwneyError(
2804
- "AGENT_INVALID_RESPONSE",
2805
- `Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
2806
- { endpoint, detail },
2807
- "yieldseeker"
2808
- );
2809
- }
2810
- function raw(value, endpoint) {
2811
- if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
2812
- return invalid(endpoint, "expected a base-10 integer string");
2813
- }
2814
- return BigInt(value);
2815
- }
2816
- function decimal(value, decimals, endpoint) {
2817
- return formatUnits(raw(value, endpoint), decimals);
2818
- }
2819
- function usd(rawAmount, decimals, price) {
2820
- return Number(formatUnits(rawAmount, decimals)) * price;
2821
- }
2822
- function percent(value) {
2823
- const result = Number(value);
2824
- return Number.isFinite(result) ? result * 100 : 0;
2825
- }
2826
- var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
2827
- function publicApyAfterYieldseekerFee(value) {
2828
- const grossPercent = percent(value);
2829
- if (grossPercent <= 0) return grossPercent;
2830
- const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
2831
- return Math.round(netPercent * 1e12) / 1e12;
2832
- }
2833
- function riskAdjustedApyForDays(option, days) {
2834
- if (days === "7D") return option.riskAdjustedApy7dAverage;
2835
- if (days === "30D") return option.riskAdjustedApy30dAverage;
2836
- return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
2837
- }
2838
- function assetAddressValue(record, address) {
2839
- const entry = Object.entries(record).find(
2840
- ([key2]) => key2.toLowerCase() === address.toLowerCase()
2841
- );
2842
- return entry?.[1] ?? "0";
2843
- }
2844
- function position(value, asset, baseAssetDecimals) {
2845
- const option = value?.yieldOption;
2846
- if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress(option.address)) {
2847
- return invalid("yield positions", "missing vault metadata");
2848
- }
2849
- return {
2850
- chain: "BASE",
2851
- protocol: option.provider,
2852
- protocolId: option.address,
2853
- pool: option.name,
2854
- asset,
2855
- // `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
2856
- // differ from the underlying asset. Yieldseeker already converts it to
2857
- // underlying base-asset units in `assetsBase`; pair that value with the
2858
- // snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
2859
- // share quantity separately because withdraw-from-position expects it.
2860
- amount: decimal(
2861
- value.assetsBase,
2862
- baseAssetDecimals,
2863
- "yield positions"
2864
- ),
2865
- amountRaw: String(value.assetsRaw),
2866
- apy: percent(option.riskAdjustedApy),
2867
- tvl: Number(option.totalDepositsUsd),
2868
- liquidity: Number(option.withdrawableDepositsUsd)
2869
- };
2870
- }
2871
- function mapYieldseekerBalances(contexts) {
2872
- const tokens = [];
2873
- const assetBalances = [];
2874
- const positions = [];
2875
- let totalUsd = 0;
2876
- for (const context of contexts) {
2877
- const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
2878
- assetBalances.push({
2879
- chain: "BASE",
2880
- chainId: 8453,
2881
- asset: context.asset,
2882
- amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
2883
- });
2884
- const idle = assetAddressValue(
2885
- context.snapshot.tokenBalances,
2886
- metadata.address
2887
- );
2888
- tokens.push({
2889
- chain: "BASE",
2890
- chainId: 8453,
2891
- asset: context.asset,
2892
- amount: decimal(idle, metadata.decimals, "snapshot")
2893
- });
2894
- positions.push(
2895
- ...context.positions.map(
2896
- (entry) => position(
2897
- entry,
2898
- context.asset,
2899
- context.snapshot.baseAssetDecimals
2900
- )
2901
- )
2902
- );
2903
- totalUsd += usd(
2904
- raw(context.snapshot.totalValueBase, "snapshot"),
2905
- context.snapshot.baseAssetDecimals,
2906
- context.snapshot.baseAssetPriceUsd
2907
- );
2908
- }
2909
- return {
2910
- ...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
2911
- totalBalance: String(totalUsd),
2912
- totalBalanceAsset: "usdc",
2913
- assetBalances,
2914
- tokens,
2915
- positions
2916
- };
2611
+ // src/lib/swap/swap.executor.ts
2612
+ var DEFAULT_SLIPPAGE = 1;
2613
+ async function affordableAmount(deps, quoted) {
2614
+ const balance = await deps.readSourceBalance();
2615
+ if (balance >= quoted) return quoted;
2616
+ debugLog("owney-sdk", "swap: trimming to the current source balance", {
2617
+ quoted: quoted.toString(),
2618
+ balance: balance.toString(),
2619
+ short: (quoted - balance).toString()
2620
+ });
2621
+ return balance;
2917
2622
  }
2918
- function mapYieldseekerEarnings(contexts) {
2919
- const tokens = [];
2920
- let lifetimeEarnings = 0;
2921
- for (const context of contexts) {
2922
- const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
2923
- tokens.push({
2924
- chain: "BASE",
2925
- chainId: 8453,
2926
- asset: context.asset,
2927
- amount: formatUnits(amount, context.snapshot.baseAssetDecimals)
2928
- });
2929
- lifetimeEarnings += usd(
2930
- amount,
2931
- context.snapshot.baseAssetDecimals,
2932
- context.snapshot.baseAssetPriceUsd
2623
+ async function executeSwap(deps, options) {
2624
+ const { quote, walletAddress, onStage } = options;
2625
+ debugLog("owney-sdk", "swap: start", {
2626
+ rail: quote.rail,
2627
+ from: `${quote.src.amount} ${quote.src.symbol} on ${quote.src.chainId}`,
2628
+ to: `${quote.dst.symbol} on ${quote.dst.chainId}`,
2629
+ expected: quote.dst.amount,
2630
+ floor: quote.dstAmountMin
2631
+ });
2632
+ const before = await deps.readTargetBalance();
2633
+ debugLog("owney-sdk", "swap: target balance before", before.toString());
2634
+ const result = quote.rail === "classic" ? await runClassic(deps, options) : await runFusion(deps, options, walletAddress);
2635
+ const after = await deps.readTargetBalance();
2636
+ const received = after - before;
2637
+ debugLog("owney-sdk", "swap: target balance after", {
2638
+ after: after.toString(),
2639
+ received: received.toString()
2640
+ });
2641
+ if (received <= 0n) {
2642
+ throw new OwneyError(
2643
+ "SWAP_REQUEST_FAILED",
2644
+ "The swap completed but no funds arrived in the wallet. Check the transaction before retrying.",
2645
+ { rail: quote.rail, ...result }
2933
2646
  );
2934
2647
  }
2935
- return {
2936
- smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2937
- lifetimeEarnings,
2938
- tokens
2939
- };
2940
- }
2941
- function apyForDays(context, days, now) {
2942
- if (days === "7D") return percent(context.snapshot.apy7d);
2943
- if (days === "30D") return percent(context.snapshot.apy30d);
2944
- const apy = calculateSnapshotApy(context.historic?.dailyYieldSnapshots ?? [], 14, now);
2945
- const apyPercent = apy === void 0 ? void 0 : apy * 100;
2946
- return apyPercent !== void 0 && Number.isFinite(apyPercent) ? apyPercent : void 0;
2947
- }
2948
- function dailyApy(point) {
2949
- const total = raw(point.totalValueBase, "historic position");
2950
- const earned = raw(point.dailyYieldBase, "historic position");
2951
- const principal = total - earned;
2952
- if (principal <= 0n || earned === 0n) return 0;
2953
- return Number(earned) / Number(principal) * 365 * 100;
2954
- }
2955
- function aggregateHistory(contexts, dayCount, now) {
2956
- const today = new Date(now).toISOString().slice(0, 10);
2957
- const cutoff = new Date(Date.parse(today) - (dayCount - 1) * 864e5).toISOString().slice(0, 10);
2958
- const assets = new Set(contexts.map((context) => context.asset.toUpperCase()));
2959
- const unit = assets.size === 1 ? [...assets][0] : "USD";
2960
- const byDate = /* @__PURE__ */ new Map();
2961
- for (const context of contexts) {
2962
- const points = context.historic?.dailyYieldSnapshots ?? [];
2963
- for (const point of points) {
2964
- if (!/^\d{4}-\d{2}-\d{2}$/.test(point.date) || !Number.isFinite(Date.parse(point.date)) || point.date < cutoff || point.date > today) continue;
2965
- const tokens = Number(decimal(point.totalValueBase, point.baseAssetDecimals, "historic position"));
2966
- const amount = assets.size === 1 ? tokens : tokens * point.baseAssetPriceUsd;
2967
- if (!Number.isFinite(amount) || amount < 0) {
2968
- invalid("historic position", "expected a finite non-negative balance");
2969
- }
2970
- const current = byDate.get(point.date) ?? { weighted: 0, amount: 0 };
2971
- current.weighted += dailyApy(point) * amount;
2972
- current.amount += amount;
2973
- byDate.set(point.date, current);
2974
- }
2975
- }
2976
- return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
2977
- date,
2978
- apy: value.amount > 0 ? value.weighted / value.amount : 0,
2979
- historicalBalance: { amount: value.amount, unit }
2980
- }));
2648
+ return { received: received.toString(), ...result };
2981
2649
  }
2982
- function mapYieldseekerApy(walletAddress, contexts, days, now = Date.now()) {
2983
- let weighted = 0;
2984
- let totalUsd = 0;
2985
- const byAsset = {};
2986
- for (const context of contexts) {
2987
- const valueUsd = usd(
2988
- raw(context.snapshot.totalValueBase, "snapshot"),
2989
- context.snapshot.baseAssetDecimals,
2990
- context.snapshot.baseAssetPriceUsd
2991
- );
2992
- const apy = apyForDays(context, days, now);
2993
- if (apy === void 0) continue;
2994
- weighted += apy * valueUsd;
2995
- totalUsd += valueUsd;
2996
- byAsset[context.asset] = apy;
2997
- }
2998
- const dayCount = Number(days.slice(0, -1));
2999
- return {
2650
+ async function runClassic(deps, options) {
2651
+ const {
2652
+ quote,
3000
2653
  walletAddress,
3001
- ...days !== "14D" || Object.keys(byAsset).length > 0 ? { weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0 } : {},
3002
- apyByChainAndAsset: { 8453: byAsset },
3003
- history: aggregateHistory(contexts, dayCount, now)
3004
- };
3005
- }
3006
- function actionType(value) {
3007
- const normalized = value.toLowerCase();
3008
- if (normalized.includes("deposit")) return "Deposit";
3009
- if (normalized.includes("withdraw")) return "Withdraw";
3010
- if (normalized.includes("yield") || normalized.includes("earn"))
3011
- return "Earned";
3012
- return "Rebalance";
3013
- }
3014
- function transactionHashes(details) {
3015
- if (!details) return [];
3016
- const values = [
3017
- details.transactionHash,
3018
- details.txHash,
3019
- ...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
3020
- ...Array.isArray(details.txHashes) ? details.txHashes : []
3021
- ];
3022
- return values.filter(
3023
- (value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
3024
- ).filter((value, index, all) => all.indexOf(value) === index);
3025
- }
3026
- function actionEntry(action) {
3027
- if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
3028
- return {
3029
- agent: "yieldseeker",
3030
- action: actionType(action.actionType),
3031
- date: action.createdDate,
3032
- oldApy: null,
3033
- newApy: null,
3034
- transactions: [
3035
- {
3036
- txHashes: transactionHashes(action.details),
3037
- chainId: 8453
3038
- }
3039
- ],
3040
- rebalanceLog: []
3041
- };
3042
- }
3043
- function depositDestination(context, movement) {
3044
- const to = movement.toAddress.toLowerCase();
3045
- const option = context.positions.find(({ yieldOption }) => yieldOption.chainId === movement.chainId && yieldOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && yieldOption.address.toLowerCase() === to)?.yieldOption;
3046
- if (option) return { protocol: option.provider || "Vault", pool: option.name, tokenSymbol: context.asset };
3047
- const receipt = context.historic?.assets.find((asset) => asset.chainId === movement.chainId && !asset.isSpam && asset.address.toLowerCase() === to && asset.address.toLowerCase() !== context.agent.assetAddress.toLowerCase());
3048
- 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);
3049
- if (receipt && minted) return { protocol: "Vault", pool: receipt.name || receipt.symbol, tokenSymbol: context.asset };
3050
- return void 0;
3051
- }
3052
- function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses, destination) {
3053
- const from = movement.fromAddress.toLowerCase();
3054
- const to = movement.toAddress.toLowerCase();
3055
- const owner = ownerAddress.toLowerCase();
3056
- const agentWallet = wallet.walletAddress.toLowerCase();
3057
- const baseAsset = agent.assetAddress.toLowerCase();
3058
- if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
3059
- return void 0;
3060
- }
3061
- let action;
3062
- if (to === agentWallet && !vaultAddresses.has(from)) {
3063
- action = "Top up";
3064
- } else if (from === agentWallet && to === owner) {
3065
- action = "Withdraw";
3066
- } else if (from === agentWallet && destination) {
3067
- action = "Deposit";
3068
- }
3069
- if (!action) return void 0;
3070
- return {
3071
- agent: "yieldseeker",
3072
- action,
3073
- ...action === "Deposit" && destination ? { positions: [{
3074
- ...destination,
3075
- amount: decimal(movement.assetAmount, YIELDSEEKER_ASSET_METADATA[asset].decimals, "historic position")
3076
- }] } : {},
3077
- date: movement.blockDate,
3078
- oldApy: null,
3079
- newApy: null,
3080
- transactions: [
3081
- {
3082
- txHashes: [movement.transactionHash],
3083
- chainId: agent.chainId,
3084
- tokenSymbol: asset,
3085
- amount: decimal(
3086
- movement.assetAmount,
3087
- YIELDSEEKER_ASSET_METADATA[asset].decimals,
3088
- "historic position"
3089
- )
3090
- }
3091
- ],
3092
- rebalanceLog: []
3093
- };
3094
- }
3095
- function mapYieldseekerHistory(contexts, options) {
3096
- const entries = contexts.flatMap((context) => {
3097
- const seenMovements = /* @__PURE__ */ new Set();
3098
- const movements = (context.historic?.movements ?? []).filter((movement) => {
3099
- const key2 = `${movement.chainId}:${movement.transactionHash.toLowerCase()}:${movement.logIndex}`;
3100
- if (seenMovements.has(key2)) return false;
3101
- seenMovements.add(key2);
3102
- return true;
3103
- });
3104
- return [
3105
- ...movements.map(
3106
- (movement) => movementEntry(
3107
- movement,
3108
- context.wallet,
3109
- context.agent,
3110
- context.asset,
3111
- options.ownerAddress,
3112
- options.vaultAddresses,
3113
- depositDestination(context, movement)
3114
- )
3115
- ),
3116
- ...(context.actions ?? []).map(actionEntry)
3117
- ].filter((entry) => entry !== void 0);
3118
- });
3119
- const grouped = /* @__PURE__ */ new Map();
3120
- const ungrouped = [];
3121
- for (const entry of entries) {
3122
- const tx = entry.transactions[0];
3123
- const hash = tx?.txHashes[0];
3124
- if (!hash) {
3125
- ungrouped.push(entry);
3126
- continue;
3127
- }
3128
- const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}`;
3129
- const previous = grouped.get(key2);
3130
- if (!previous) {
3131
- grouped.set(key2, entry);
3132
- continue;
3133
- }
3134
- if (entry.action === "Deposit" && entry.positions?.length) {
3135
- if (!previous.positions?.length) {
3136
- grouped.set(key2, entry);
3137
- continue;
3138
- }
3139
- previous.positions.push(...entry.positions);
3140
- previous.transactions.push(...entry.transactions);
3141
- }
3142
- }
3143
- 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));
3144
- return {
3145
- data: filtered.slice(0, options.limit),
3146
- // v1 returns the whole action/movement collection and defines no cursor.
3147
- // Report a terminal page so callers never loop over the same prefix.
3148
- hasMore: false
3149
- };
3150
- }
3151
- function mapYieldseekerProfile(address, contexts) {
3152
- const protocols = /* @__PURE__ */ new Set();
3153
- for (const context of contexts) {
3154
- for (const current of context.positions) {
3155
- if (current.yieldOption?.provider) {
3156
- protocols.add(String(current.yieldOption.provider));
3157
- }
3158
- }
3159
- }
3160
- return {
3161
- address,
3162
- smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
3163
- chains: contexts.length > 0 ? [8453] : [],
3164
- hasActiveSessionKey: contexts.some(
3165
- (context) => context.wallet.initializedDate != null
3166
- ),
3167
- protocols: [...protocols]
3168
- };
3169
- }
3170
- function mapYieldseekerAgentApy(options, days) {
3171
- const perAsset = {};
3172
- const all = [];
3173
- for (const entry of options) {
3174
- const apys = entry.yieldOptions.map(
3175
- (option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
3176
- ).filter(Number.isFinite);
3177
- if (apys.length === 0) continue;
3178
- const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
3179
- perAsset[entry.asset] = average;
3180
- all.push(average);
3181
- }
3182
- return {
3183
- averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
3184
- detailedApys: { apyPerAsset: { 8453: perAsset } }
2654
+ slippage = DEFAULT_SLIPPAGE,
2655
+ direction,
2656
+ onStage
2657
+ } = options;
2658
+ const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
2659
+ const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
2660
+ const swapTxRequest = {
2661
+ from: {
2662
+ chainId: quote.src.chainId,
2663
+ symbol: quote.src.symbol,
2664
+ amount: amount.toString()
2665
+ },
2666
+ to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
2667
+ walletAddress,
2668
+ slippage,
2669
+ ...direction ? { direction } : {}
3185
2670
  };
3186
- }
3187
-
3188
- // src/agents/yieldseeker/yieldseeker.agent.ts
3189
- var OWNEY_AGENT_NAME = "owney";
3190
- var YIELDSEEKER_USERNAME_PREFIX = "owney_";
3191
- var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
3192
- var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
3193
- var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
3194
- function generateYieldseekerUsername() {
3195
- const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
3196
- return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
3197
- }
3198
- function isUsernameConflict(error) {
3199
- if (!(error instanceof YieldseekerApiError)) return false;
3200
- const code = error.providerCode.toUpperCase();
3201
- return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
3202
- }
3203
- var YIELDSEEKER_AGENT_WALLET_ABI = [
3204
- {
3205
- type: "function",
3206
- name: "withdrawAssetToUser",
3207
- stateMutability: "nonpayable",
3208
- inputs: [
3209
- { name: "recipient", type: "address" },
3210
- { name: "asset", type: "address" },
3211
- { name: "amount", type: "uint256" }
3212
- ],
3213
- outputs: []
3214
- },
3215
- {
3216
- type: "function",
3217
- name: "withdrawAllAssetToUser",
3218
- stateMutability: "nonpayable",
3219
- inputs: [
3220
- { name: "recipient", type: "address" },
3221
- { name: "asset", type: "address" }
3222
- ],
3223
- outputs: []
3224
- }
3225
- ];
3226
- function query(params) {
3227
- const search = new URLSearchParams();
3228
- for (const [key2, value] of Object.entries(params)) {
3229
- if (value !== void 0) search.set(key2, String(value));
3230
- }
3231
- const encoded = search.toString();
3232
- return encoded ? `?${encoded}` : "";
3233
- }
3234
- var YieldseekerAgent = class {
3235
- id = "yieldseeker";
3236
- balanceComposition = "tokens-plus-positions";
3237
- supportedChainIds = [8453];
3238
- supportedAssets = [
3239
- {
3240
- chainId: 8453,
3241
- chain: "BASE",
3242
- assets: [
3243
- { symbol: "USDC", minDepositAmount: "10000000" },
3244
- { symbol: "WETH", minDepositAmount: "1" }
3245
- ]
3246
- }
3247
- ];
3248
- api;
3249
- auth;
3250
- transactionExecutor;
3251
- unwindReceiptWaiter;
3252
- agentContexts = /* @__PURE__ */ new Map();
3253
- users = /* @__PURE__ */ new Map();
3254
- pendingAgents = /* @__PURE__ */ new Map();
3255
- yieldOptions = /* @__PURE__ */ new Map();
3256
- pendingYieldOptions = /* @__PURE__ */ new Map();
3257
- constructor(owneyApiKey, options = {}) {
3258
- this.api = new YieldseekerApiClient(
3259
- owneyApiKey,
3260
- options.baseUrl ?? getYieldseekerProxyBaseUrl(),
3261
- options.fetchFn
3262
- );
3263
- this.auth = new YieldseekerAuth(options.auth);
3264
- this.transactionExecutor = options.transactionExecutor;
3265
- this.unwindReceiptWaiter = options.unwindReceiptWaiter;
3266
- }
3267
- async disconnect() {
3268
- this.auth.clear();
3269
- for (const key2 of this.users.keys()) {
3270
- const [walletAddress, chainId] = key2.split(":");
3271
- clearYieldseekerIdentity(walletAddress, Number(chainId));
3272
- }
3273
- this.users.clear();
3274
- this.agentContexts.clear();
3275
- this.pendingAgents.clear();
3276
- }
3277
- async activateAgent(state, chainId, asset) {
3278
- this.assertChain(chainId);
3279
- const targetAsset = asset ?? "USDC";
3280
- this.assertAsset(targetAsset);
3281
- await this.ensureAgent(state, chainId, targetAsset);
3282
- }
3283
- async deposit(state, chainId, amount, asset, depositCallback) {
3284
- this.assertChain(chainId);
3285
- this.assertAsset(asset);
3286
- if (BigInt(amount) <= 0n) {
3287
- throw new OwneyError(
3288
- "DEPOSIT_AMOUNT_BELOW_MINIMUM",
3289
- "Yieldseeker deposits must be greater than zero.",
3290
- { amount, minDepositAmount: "1" },
3291
- this.id
3292
- );
3293
- }
3294
- const context = await this.ensureAgent(state, chainId, asset);
3295
- let txHash;
3296
- try {
3297
- if (depositCallback) {
3298
- provideDepositVerificationContext(depositCallback, {
3299
- agentId: "yieldseeker",
3300
- signature: await this.auth.getToken(state, chainId),
3301
- userId: context.user.userId,
3302
- yieldseekerAgentId: context.agent.agentId
3303
- });
3304
- txHash = await depositCallback(
3305
- context.wallet.walletAddress,
3306
- chainId,
3307
- amount
3308
- );
3309
- await this.waitForReceipt(state, chainId, txHash);
3310
- } else {
3311
- txHash = await this.submitTransaction(state, chainId, {
3312
- from: getAddress2(state.walletAddress),
3313
- to: YIELDSEEKER_ASSET_METADATA[asset].address,
3314
- data: encodeFunctionData({
3315
- abi: erc20Abi,
3316
- functionName: "transfer",
3317
- args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
3318
- }),
3319
- value: "0",
3320
- chainId
3321
- });
3322
- }
3323
- } finally {
3324
- await this.refreshSnapshotAfterMovement(
3325
- state,
3326
- chainId,
3327
- context,
3328
- "deposit"
3329
- );
3330
- }
3331
- return {
3332
- txHash,
3333
- smartWallet: context.wallet.walletAddress,
3334
- amount
3335
- };
3336
- }
3337
- async withdraw(state, chainId, asset, amount) {
3338
- this.assertChain(chainId);
3339
- this.assertAsset(asset);
3340
- if (amount !== void 0 && BigInt(amount) <= 0n) {
3341
- throw new OwneyError(
3342
- "WITHDRAW_FAILED",
3343
- "Yieldseeker withdrawals must be greater than zero.",
3344
- { amount },
3345
- this.id
3346
- );
3347
- }
3348
- const context = await this.findAgent(state, chainId, asset);
3349
- if (!context) {
3350
- throw new OwneyError(
3351
- "WITHDRAW_INSUFFICIENT_BALANCE",
3352
- `No Yieldseeker ${asset} agent exists for this wallet.`,
3353
- { asset, available: "0" },
3354
- this.id
3355
- );
3356
- }
3357
- try {
3358
- const portfolio = await this.loadPortfolioContext(
3359
- state,
3360
- chainId,
3361
- context
3362
- );
3363
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3364
- const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
3365
- ([address]) => address.toLowerCase() === metadata.address.toLowerCase()
3366
- );
3367
- const idle = BigInt(idleEntry?.[1] ?? "0");
3368
- const deployed = portfolio.positions.reduce(
3369
- (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
3370
- 0n
3371
- );
3372
- const totalAvailable = idle + deployed;
3373
- const requested = amount === void 0 ? totalAvailable : BigInt(amount);
3374
- if (requested > totalAvailable) {
3375
- throw new OwneyError(
3376
- "WITHDRAW_INSUFFICIENT_BALANCE",
3377
- `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
3378
- {
3379
- asset,
3380
- requested: requested.toString(),
3381
- available: totalAvailable.toString()
3382
- },
3383
- this.id
3384
- );
3385
- }
3386
- let remaining = requested > idle ? requested - idle : 0n;
3387
- for (const position2 of portfolio.positions) {
3388
- if (remaining === 0n) break;
3389
- const available = BigInt(position2.withdrawableAssetsRaw);
3390
- if (available <= 0n) continue;
3391
- const assetsRaw = available < remaining ? available : remaining;
3392
- const response = await this.walletRequest(
3393
- state,
3394
- chainId,
3395
- this.agentPath(context, "withdraw-from-position"),
3396
- {
3397
- method: "POST",
3398
- body: {
3399
- chainId,
3400
- vaultAddress: position2.yieldOption.address,
3401
- assetsRaw: assetsRaw.toString()
3402
- }
3403
- }
3404
- );
3405
- if (!this.isTransactionHash(response?.transactionHash)) {
3406
- throw this.invalidResponse("position withdrawal");
3407
- }
3408
- await this.waitForReceipt(state, chainId, response.transactionHash);
3409
- remaining -= assetsRaw;
3410
- }
3411
- if (remaining > 0n) {
3412
- throw this.invalidResponse("yield positions", {
3413
- reason: "Withdrawable positions could not cover the request.",
3414
- remaining: remaining.toString()
3415
- });
3416
- }
3417
- const account = getAddress2(state.walletAddress);
3418
- const txHash = await this.submitTransaction(state, chainId, {
3419
- from: account,
3420
- to: getAddress2(context.wallet.walletAddress),
3421
- data: amount === void 0 ? encodeFunctionData({
3422
- abi: YIELDSEEKER_AGENT_WALLET_ABI,
3423
- functionName: "withdrawAllAssetToUser",
3424
- args: [account, metadata.address]
3425
- }) : encodeFunctionData({
3426
- abi: YIELDSEEKER_AGENT_WALLET_ABI,
3427
- functionName: "withdrawAssetToUser",
3428
- args: [account, metadata.address, requested]
3429
- }),
3430
- value: "0",
3431
- chainId
3432
- });
3433
- return {
3434
- txHash,
3435
- type: amount === void 0 ? "full" : "partial",
3436
- amount: requested.toString()
3437
- };
3438
- } finally {
3439
- await this.refreshSnapshotAfterMovement(
3440
- state,
3441
- chainId,
3442
- context,
3443
- "withdrawal"
3444
- );
3445
- }
3446
- }
3447
- async getBalances(state, chainId) {
3448
- this.assertChain(chainId);
3449
- return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
3450
- }
3451
- async getEarnings(state, chainId) {
3452
- this.assertChain(chainId);
3453
- return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
3454
- }
3455
- async getAccountApy(state, chainId, days, tokenSymbol) {
3456
- this.assertChain(chainId);
3457
- const asset = tokenSymbol?.toUpperCase();
3458
- if (asset !== void 0) this.assertAsset(asset);
3459
- const contexts = await this.loadPortfolio(state, chainId, {
3460
- ...asset ? { asset } : {},
3461
- historic: true
3462
- });
3463
- return mapYieldseekerApy(state.walletAddress, contexts, days);
3464
- }
3465
- async getHistory(state, chainId, options) {
3466
- this.assertChain(chainId);
3467
- const asset = options?.tokenSymbol?.toUpperCase();
3468
- if (asset !== void 0) this.assertAsset(asset);
3469
- const contexts = await this.loadPortfolio(state, chainId, {
3470
- ...asset ? { asset } : {},
3471
- historic: true,
3472
- actions: true
3473
- });
3474
- const catalog = await Promise.all(
3475
- [...new Set(contexts.map((context) => context.asset))].map(
3476
- (contextAsset) => this.loadYieldOptions(contextAsset)
3477
- )
3478
- );
3479
- const vaultAddresses = new Set(
3480
- catalog.flat().filter(
3481
- (yieldOption) => yieldOption.chainId === chainId && isAddress2(yieldOption.address)
3482
- ).map((yieldOption) => yieldOption.address.toLowerCase())
3483
- );
3484
- return mapYieldseekerHistory(contexts, {
3485
- limit: options?.limit ?? 10,
3486
- ownerAddress: state.walletAddress,
3487
- vaultAddresses,
3488
- ...options?.fromDate ? { fromDate: options.fromDate } : {},
3489
- ...options?.toDate ? { toDate: options.toDate } : {}
3490
- });
3491
- }
3492
- async getUserProfile(state, chainId) {
3493
- this.assertChain(chainId);
3494
- return mapYieldseekerProfile(
3495
- state.walletAddress,
3496
- await this.loadPortfolio(state, chainId, {})
3497
- );
3498
- }
3499
- async getAgentApy(days, options) {
3500
- this.assertOptionalChain(options?.chainId);
3501
- const requested = options?.tokenSymbol?.toUpperCase();
3502
- if (requested !== void 0) this.assertAsset(requested);
3503
- const assets = requested ? [requested] : ["USDC", "WETH"];
3504
- const values = await Promise.all(
3505
- assets.map(async (asset) => {
3506
- return { asset, yieldOptions: await this.loadYieldOptions(asset) };
3507
- })
3508
- );
3509
- return mapYieldseekerAgentApy(values, days);
3510
- }
3511
- async loadYieldOptions(asset) {
3512
- const cached = this.yieldOptions.get(asset);
3513
- if (cached && cached.expiresAt > Date.now()) return cached.value;
3514
- const pending = this.pendingYieldOptions.get(asset);
3515
- if (pending) return pending;
3516
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3517
- const request = this.api.request(
3518
- `/chains/8453/assets/${metadata.address}/yield-options`
3519
- ).then((response) => {
3520
- if (!Array.isArray(response?.yieldOptions)) {
3521
- throw this.invalidResponse("yield options");
3522
- }
3523
- this.yieldOptions.set(asset, {
3524
- expiresAt: Date.now() + YIELDSEEKER_YIELD_OPTIONS_CACHE_MS,
3525
- value: response.yieldOptions
3526
- });
3527
- return response.yieldOptions;
3528
- }).finally(() => this.pendingYieldOptions.delete(asset));
3529
- this.pendingYieldOptions.set(asset, request);
3530
- return request;
3531
- }
3532
- userKey(state, chainId) {
3533
- return `${state.walletAddress.toLowerCase()}:${chainId}`;
3534
- }
3535
- contextKey(state, chainId, asset) {
3536
- return `${this.userKey(state, chainId)}:${asset}`;
3537
- }
3538
- async resolveUser(state, chainId) {
3539
- const key2 = this.userKey(state, chainId);
3540
- const inMemory = this.users.get(key2);
3541
- if (inMemory) return inMemory;
3542
- const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
3543
- if (persisted) {
3544
- this.users.set(key2, persisted);
3545
- return persisted;
3546
- }
3547
- const walletAddress = getAddress2(state.walletAddress);
3548
- let user = null;
3549
- try {
3550
- const login = await this.providerRequest(
3551
- state,
3552
- chainId,
3553
- "/users/login-with-wallet",
3554
- { method: "POST", body: { walletAddress } }
3555
- );
3556
- user = login?.user ?? null;
3557
- if (!user) {
3558
- throw this.invalidResponse("wallet login", {
3559
- reason: "A successful login returned no user."
3560
- });
3561
- }
3562
- } catch (error) {
3563
- if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
3564
- if (error instanceof OwneyError) throw error;
3565
- throw this.mapApiError(error);
3566
- }
3567
- let created;
3568
- for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
3569
- try {
3570
- created = await this.providerRequest(
3571
- state,
3572
- chainId,
3573
- "/users",
3574
- {
3575
- method: "POST",
3576
- body: {
3577
- walletAddress,
3578
- username: generateYieldseekerUsername()
3579
- }
3580
- }
3581
- );
3582
- break;
3583
- } catch (createError) {
3584
- const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
3585
- if (canRetry) continue;
3586
- throw this.mapApiError(createError);
3587
- }
3588
- }
3589
- user = created?.user ?? null;
3590
- }
3591
- if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
3592
- throw this.invalidResponse("wallet identity");
3593
- }
3594
- const resolved = { userId: user.userId };
3595
- this.users.set(key2, resolved);
3596
- writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
3597
- return resolved;
3598
- }
3599
- forgetUser(state, chainId) {
3600
- this.users.delete(this.userKey(state, chainId));
3601
- clearYieldseekerIdentity(state.walletAddress, chainId);
3602
- }
3603
- async ensureAgent(state, chainId, asset) {
3604
- const key2 = this.contextKey(state, chainId, asset);
3605
- const cached = this.agentContexts.get(key2);
3606
- if (cached) return cached;
3607
- const pending = this.pendingAgents.get(key2);
3608
- if (pending) return pending;
3609
- const request = this.resolveAgent(state, chainId, asset, true).then(
3610
- async (context) => {
3611
- if (!context) throw this.invalidResponse("agent creation");
3612
- await this.deployAgent(state, chainId, context);
3613
- this.agentContexts.set(key2, context);
3614
- return context;
3615
- }
3616
- );
3617
- this.pendingAgents.set(key2, request);
3618
- try {
3619
- return await request;
3620
- } finally {
3621
- this.pendingAgents.delete(key2);
3622
- }
3623
- }
3624
- async findAgent(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 context = await this.resolveAgent(state, chainId, asset, false);
3629
- if (context) this.agentContexts.set(key2, context);
3630
- return context;
3631
- }
3632
- async resolveAgent(state, chainId, asset, createIfMissing) {
3633
- const user = await this.resolveUser(state, chainId);
3634
- const response = await this.walletRequest(
3635
- state,
3636
- chainId,
3637
- `/users/${user.userId}/agents`
3638
- );
3639
- if (!Array.isArray(response?.agents)) {
3640
- throw this.invalidResponse("agent list");
3641
- }
3642
- const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3643
- let agent = response.agents.find(
3644
- (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3645
- );
3646
- if (!agent && createIfMissing) {
3647
- const created = await this.walletRequest(
3648
- state,
3649
- chainId,
3650
- `/users/${user.userId}/agents`,
3651
- {
3652
- method: "POST",
3653
- body: {
3654
- name: OWNEY_AGENT_NAME,
3655
- emoji: "\u{1F989}",
3656
- chainId,
3657
- assetAddress: metadata.address,
3658
- type: "vault",
3659
- rulePreset: null
3660
- }
3661
- }
3662
- );
3663
- agent = created?.agent;
3664
- }
3665
- if (!agent) return null;
3666
- this.assertAgent(agent);
3667
- const walletResponse = await this.walletRequest(
3668
- state,
3669
- chainId,
3670
- `/users/${user.userId}/agents/${agent.agentId}/wallet`
3671
- );
3672
- if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
3673
- throw this.invalidResponse("agent wallet");
3674
- }
3675
- return { user, agent, wallet: walletResponse.agentWallet, asset };
3676
- }
3677
- async loadPortfolio(state, chainId, options) {
3678
- const user = await this.resolveUser(state, chainId);
3679
- const response = await this.walletRequest(
3680
- state,
3681
- chainId,
3682
- `/users/${user.userId}/agents`
3683
- );
3684
- if (!Array.isArray(response?.agents)) {
3685
- throw this.invalidResponse("agent list");
3686
- }
3687
- const contexts = [];
3688
- for (const agent of response.agents) {
3689
- const asset = this.assetForAgent(agent);
3690
- if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3691
- continue;
3692
- }
3693
- this.assertAgent(agent);
3694
- const walletResponse = await this.walletRequest(
3695
- state,
3696
- chainId,
3697
- `/users/${user.userId}/agents/${agent.agentId}/wallet`
3698
- );
3699
- if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
3700
- throw this.invalidResponse("agent wallet");
3701
- }
3702
- const context = {
3703
- user,
3704
- agent,
3705
- wallet: walletResponse.agentWallet,
3706
- asset
3707
- };
3708
- this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3709
- contexts.push(context);
3710
- }
3711
- return Promise.all(
3712
- contexts.map(
3713
- (context) => this.loadPortfolioContext(state, chainId, context, options)
3714
- )
3715
- );
3716
- }
3717
- async loadPortfolioContext(state, chainId, context, options = {}) {
3718
- const [snapshot, positions, historic, actions] = await Promise.all([
3719
- this.walletRequest(
3720
- state,
3721
- chainId,
3722
- `${this.agentPath(context, "snapshot")}${query({
3723
- shouldOnlyUseRecentValue: true,
3724
- shouldAllowStaleOnError: true
3725
- })}`
3726
- ),
3727
- this.walletRequest(
3728
- state,
3729
- chainId,
3730
- this.agentPath(context, "yield-positions")
3731
- ),
3732
- options.historic ? this.walletRequest(
3733
- state,
3734
- chainId,
3735
- this.agentPath(context, "wallet/historic-position")
3736
- ) : Promise.resolve(void 0),
3737
- options.actions ? this.walletRequest(
3738
- state,
3739
- chainId,
3740
- this.agentPath(context, "actions")
3741
- ) : Promise.resolve(void 0)
3742
- ]);
3743
- if (!snapshot?.agentSnapshot) {
3744
- throw this.invalidResponse("agent snapshot");
3745
- }
3746
- if (!Array.isArray(positions?.yieldPositions)) {
3747
- throw this.invalidResponse("yield positions");
3748
- }
3749
- return {
3750
- ...context,
3751
- snapshot: snapshot.agentSnapshot,
3752
- positions: positions.yieldPositions,
3753
- ...historic?.position ? { historic: historic.position } : {},
3754
- ...actions?.actions ? { actions: actions.actions } : {}
3755
- };
3756
- }
3757
- async deployAgent(state, chainId, context) {
3758
- if (context.wallet.initializedDate != null) return;
3759
- const walletAddress = context.wallet.walletAddress.toLowerCase();
3760
- const deployed = await this.walletRequest(
3761
- state,
3762
- chainId,
3763
- this.agentPath(context, "deploy"),
3764
- { method: "POST", body: {} }
3765
- );
3766
- if (!deployed?.agentWallet || !isAddress2(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
3767
- throw this.invalidResponse("agent deployment", {
3768
- reason: "Deploy did not return the expected Agent Wallet."
3769
- });
3770
- }
3771
- context.wallet = deployed.agentWallet;
3772
- }
3773
- async refreshSnapshotAfterMovement(state, chainId, context, movement) {
3774
- try {
3775
- const response = await this.walletRequest(
3776
- state,
3777
- chainId,
3778
- `${this.agentPath(context, "snapshot")}${query({
3779
- shouldForceRefresh: true
3780
- })}`
3781
- );
3782
- if (!response?.agentSnapshot) {
3783
- throw this.invalidResponse("agent snapshot refresh");
3784
- }
3785
- } catch (error) {
3786
- console.warn(
3787
- `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3788
- error
3789
- );
3790
- }
3791
- }
3792
- agentPath(context, suffix) {
3793
- return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
3794
- }
3795
- async walletRequest(state, chainId, path, options = {}) {
3796
- try {
3797
- return await this.providerRequest(state, chainId, path, options);
3798
- } catch (error) {
3799
- throw this.mapApiError(error);
3800
- }
3801
- }
3802
- async providerRequest(state, chainId, path, options = {}) {
3803
- this.assertChain(chainId);
3804
- const request = (signature2) => this.api.request(path, {
3805
- ...options,
3806
- signature: signature2
3807
- });
3808
- let signature = await this.auth.getToken(state, chainId);
3809
- try {
3810
- return await request(signature);
3811
- } catch (error) {
3812
- if (!(error instanceof YieldseekerApiError)) throw error;
3813
- if (error.providerCode === "NO_USER") throw error;
3814
- if (!error.isAuthenticationError) throw error;
3815
- signature = await this.auth.refreshToken(state, chainId, signature);
3816
- try {
3817
- return await request(signature);
3818
- } catch (retryError) {
3819
- if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
3820
- this.forgetUser(state, chainId);
3821
- }
3822
- throw retryError;
3823
- }
3824
- }
3825
- }
3826
- mapApiError(error) {
3827
- if (!(error instanceof YieldseekerApiError)) {
3828
- return new OwneyError(
3829
- "AGENT_API_ERROR",
3830
- "Yieldseeker request failed.",
3831
- { cause: error instanceof Error ? error.message : String(error) },
3832
- this.id
3833
- );
3834
- }
3835
- const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
3836
- return new OwneyError(
3837
- code,
3838
- `Yieldseeker request failed: ${error.providerCode}.`,
3839
- {
3840
- statusCode: error.status,
3841
- providerCode: error.providerCode,
3842
- ...error.responseFields ? { fields: error.responseFields } : {}
3843
- },
3844
- this.id
3845
- );
3846
- }
3847
- async submitTransaction(state, chainId, transaction) {
3848
- if (this.transactionExecutor) {
3849
- return this.transactionExecutor(state, chainId, transaction);
3850
- }
3851
- this.assertTransaction(transaction, state, chainId);
3852
- const account = getAddress2(state.walletAddress);
3853
- const walletClient = createWalletClient2({
3854
- account,
3855
- chain: base3,
3856
- transport: custom2(state.provider)
3857
- });
3858
- const publicClient = createPublicClient3({
3859
- chain: base3,
3860
- transport: custom2(state.provider)
3861
- });
3862
- await ensureWalletOnChain(
3863
- publicClient,
3864
- walletClient,
3865
- 8453
3866
- );
3867
- const hash = await walletClient.sendTransaction({
3868
- account,
3869
- chain: base3,
3870
- to: getAddress2(transaction.to),
3871
- data: transaction.data,
3872
- value: BigInt(transaction.value)
3873
- });
3874
- const receipt = await publicClient.waitForTransactionReceipt({
3875
- hash,
3876
- confirmations: 1
3877
- });
3878
- if (receipt.status !== "success") {
3879
- throw new OwneyError(
3880
- "AGENT_TRANSACTION_REVERTED",
3881
- `Yieldseeker transaction reverted (${hash}).`,
3882
- { transactionHash: hash },
3883
- this.id
3884
- );
3885
- }
3886
- return hash;
3887
- }
3888
- async waitForReceipt(state, chainId, transactionHash) {
3889
- if (this.unwindReceiptWaiter) {
3890
- await this.unwindReceiptWaiter(state, chainId, transactionHash);
3891
- return;
3892
- }
3893
- const publicClient = createPublicClient3({
3894
- chain: base3,
3895
- transport: custom2(state.provider)
3896
- });
3897
- const receipt = await publicClient.waitForTransactionReceipt({
3898
- hash: transactionHash,
3899
- confirmations: 1
3900
- });
3901
- if (receipt.status !== "success") {
3902
- throw new OwneyError(
3903
- "AGENT_TRANSACTION_REVERTED",
3904
- `Yieldseeker transaction reverted (${transactionHash}).`,
3905
- { transactionHash },
3906
- this.id
3907
- );
3908
- }
3909
- }
3910
- assertTransaction(transaction, state, chainId) {
3911
- if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(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 || getAddress2(transaction.from) !== getAddress2(state.walletAddress)) {
3912
- throw this.invalidResponse("transaction");
3913
- }
3914
- }
3915
- assertAgent(agent) {
3916
- if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
3917
- throw this.invalidResponse("agent");
3918
- }
3919
- }
3920
- isOwneyAgent(agent) {
3921
- return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
3922
- }
3923
- assetForAgent(agent) {
3924
- for (const asset of ["USDC", "WETH"]) {
3925
- if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
3926
- return asset;
3927
- }
3928
- }
3929
- return null;
3930
- }
3931
- isTransactionHash(value) {
3932
- return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
3933
- }
3934
- assertChain(chainId) {
3935
- if (chainId !== 8453) {
3936
- throw new OwneyError(
3937
- "CHAIN_UNSUPPORTED",
3938
- `Yieldseeker does not support chain ${chainId}.`,
3939
- { chainId, supportedChainIds: [8453] },
3940
- this.id
3941
- );
3942
- }
3943
- }
3944
- assertOptionalChain(chainId) {
3945
- if (chainId !== void 0) this.assertChain(chainId);
3946
- }
3947
- assertAsset(asset) {
3948
- if (asset !== "USDC" && asset !== "WETH") {
3949
- throw new OwneyError(
3950
- "ASSET_UNSUPPORTED",
3951
- `Yieldseeker does not support asset ${asset} in the Owney rollout.`,
3952
- {
3953
- asset,
3954
- supportedAssets: ["USDC", "WETH"],
3955
- providerAlsoAdvertises: ["cbBTC"]
3956
- },
3957
- this.id
3958
- );
3959
- }
3960
- }
3961
- invalidResponse(operation, details = {}) {
3962
- return new OwneyError(
3963
- "AGENT_INVALID_RESPONSE",
3964
- `Yieldseeker returned an invalid ${operation} response.`,
3965
- details,
3966
- this.id
3967
- );
3968
- }
3969
- };
3970
-
3971
- // src/lib/routing-api.ts
3972
- var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3973
- async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3974
- const url = `${baseUrl}/api/v1/agent/org-config`;
3975
- try {
3976
- const res = await fetch(url, {
3977
- method: "GET",
3978
- headers: {
3979
- "Content-Type": "application/json",
3980
- "x-owney-api-key": `${apiKey}`
3981
- }
2671
+ onStage?.("quoting");
2672
+ debugLog("owney-sdk", "swap: fetching classic calldata");
2673
+ let { tx } = await deps.api.swapTx(swapTxRequest);
2674
+ const isNative = BigInt(tx.value ?? "0") > 0n;
2675
+ if (!isNative) {
2676
+ const needed = amount;
2677
+ const current = await deps.readAllowance(tx.to);
2678
+ debugLog("owney-sdk", "swap: allowance", {
2679
+ spender: tx.to,
2680
+ current: current.toString(),
2681
+ needed: needed.toString()
3982
2682
  });
3983
- if (!res.ok) {
3984
- if (res.status !== 404) {
3985
- console.warn(
3986
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
3987
- );
3988
- }
3989
- return null;
2683
+ if (current < needed) {
2684
+ onStage?.("approving");
2685
+ await deps.ensureChain(quote.src.chainId);
2686
+ await deps.approve(tx.to, MAX_UINT256);
2687
+ debugLog(
2688
+ "owney-sdk",
2689
+ "swap: re-fetching classic calldata after approval"
2690
+ );
2691
+ ({ tx } = await deps.api.swapTx(swapTxRequest));
3990
2692
  }
3991
- const json = await res.json();
3992
- const policy = json.success ? json.data ?? null : null;
3993
- debugLog(
3994
- "owney-sdk",
3995
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
3996
- policy ?? void 0
3997
- );
3998
- return policy;
3999
- } catch (error) {
4000
- console.warn(
4001
- "[owney-sdk] Could not read org agent config (non-fatal):",
4002
- error instanceof Error ? error.message : String(error)
4003
- );
4004
- return null;
4005
2693
  }
2694
+ onStage?.("signing");
2695
+ await deps.ensureChain(quote.src.chainId);
2696
+ debugLog("owney-sdk", "swap: sending classic swap tx", { to: tx.to });
2697
+ const txHash = await deps.sendTransaction({
2698
+ to: tx.to,
2699
+ data: tx.data,
2700
+ value: tx.value ?? "0"
2701
+ });
2702
+ onStage?.("swapped");
2703
+ return { txHash };
4006
2704
  }
4007
- async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
4008
- const url = `${baseUrl}/api/v1/agent/keys`;
4009
- const res = await fetch(url, {
4010
- method: "GET",
4011
- headers: {
4012
- "Content-Type": "application/json",
4013
- "x-owney-api-key": `${apiKey}`
2705
+ async function runFusion(deps, options, walletAddress) {
2706
+ const { quote, direction, onStage } = options;
2707
+ const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
2708
+ const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
2709
+ if (quote.spender && !isNativeSource) {
2710
+ const needed = amount;
2711
+ const current = await deps.readAllowance(quote.spender);
2712
+ debugLog("owney-sdk", "swap: fusion allowance", {
2713
+ spender: quote.spender,
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(quote.spender, MAX_UINT256);
2721
+ debugLog("owney-sdk", "swap: approved limit order protocol");
4014
2722
  }
2723
+ }
2724
+ const { secrets, secretHashes } = mintSecrets(quote.secretsCount ?? 1);
2725
+ onStage?.("quoting");
2726
+ debugLog("owney-sdk", "swap: building fusion order", {
2727
+ secrets: secretHashes.length
4015
2728
  });
4016
- if (!res.ok) {
4017
- const text = await res.text().catch(() => "");
4018
- throw new OwneyError(
4019
- "API_ROUTING_ERROR",
4020
- `Routing API error ${res.status}: ${text}`,
4021
- { statusCode: res.status, responseBody: text }
4022
- );
2729
+ const built = await deps.api.buildOrder({
2730
+ from: {
2731
+ chainId: quote.src.chainId,
2732
+ symbol: quote.src.symbol,
2733
+ // The trimmed amount — the order is re-quoted at this size server-side.
2734
+ amount: amount.toString()
2735
+ },
2736
+ to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
2737
+ walletAddress,
2738
+ secretHashes,
2739
+ ...direction ? { direction } : {}
2740
+ });
2741
+ saveOrder({
2742
+ orderHash: built.orderHash,
2743
+ secrets,
2744
+ srcChainId: quote.src.chainId,
2745
+ srcSymbol: quote.src.symbol,
2746
+ dstChainId: quote.dst.chainId,
2747
+ dstSymbol: quote.dst.symbol,
2748
+ amount: amount.toString(),
2749
+ createdAt: Date.now()
2750
+ });
2751
+ debugLog("owney-sdk", "swap: order built", { orderHash: built.orderHash });
2752
+ onStage?.("signing");
2753
+ await deps.ensureChain(quote.src.chainId);
2754
+ debugLog("owney-sdk", "swap: awaiting signature in wallet", {
2755
+ signingOnChain: quote.src.chainId
2756
+ });
2757
+ const signature = await deps.signTypedData(built.typedData);
2758
+ debugLog("owney-sdk", "swap: signed, submitting to relayer");
2759
+ await deps.api.submitOrder({
2760
+ srcChainId: quote.src.chainId,
2761
+ // The ORDER STRUCT, not the typed-data envelope we just signed. Sending
2762
+ // the envelope here gets a bare 500 from the relayer.
2763
+ order: built.order,
2764
+ signature,
2765
+ quoteId: built.quoteId,
2766
+ // Single-fill orders must NOT carry secretHashes — the relayer rejects
2767
+ // them with SECRET_HASHES_NOT_REQUIRED. The one hash is already inside the
2768
+ // order's hashlock, so repeating it here is redundant, and only a
2769
+ // multi-fill order (a Merkle tree of hashes) needs them listed.
2770
+ ...secretHashes.length > 1 ? { secretHashes } : {},
2771
+ ...built.extension ? { extension: built.extension } : {}
2772
+ });
2773
+ debugLog("owney-sdk", "swap: order submitted, polling escrows");
2774
+ try {
2775
+ await runFusionOrder(deps.runner, {
2776
+ orderHash: built.orderHash,
2777
+ secrets,
2778
+ ...onStage ? { onStage } : {}
2779
+ });
2780
+ } catch (error) {
2781
+ if (error instanceof OwneyError && (error.code === "SWAP_ORDER_REFUNDED" || error.code === "SWAP_ORDER_CANCELLED")) {
2782
+ clearOrder(built.orderHash);
2783
+ }
2784
+ throw error;
4023
2785
  }
4024
- const json = await res.json();
4025
- if (!json.success) {
4026
- throw new OwneyError(
4027
- "API_ROUTING_FAILED",
4028
- `Routing API request failed: ${json.message}`,
4029
- { message: json.message }
4030
- );
2786
+ clearOrder(built.orderHash);
2787
+ return { orderHash: built.orderHash };
2788
+ }
2789
+
2790
+ // src/lib/swap/swap.arrival.ts
2791
+ var DEFAULT_TIMEOUT_MS2 = 18e4;
2792
+ var DEFAULT_POLL_MS2 = 4e3;
2793
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
2794
+ async function awaitWithdrawalArrival(options) {
2795
+ const {
2796
+ readBalance,
2797
+ baseline,
2798
+ timeoutMs = DEFAULT_TIMEOUT_MS2,
2799
+ pollMs = DEFAULT_POLL_MS2
2800
+ } = options;
2801
+ const deadline = Date.now() + timeoutMs;
2802
+ debugLog("owney-sdk", "withdraw: waiting for funds to land", {
2803
+ baseline: baseline.toString(),
2804
+ timeoutMs
2805
+ });
2806
+ let lastError;
2807
+ for (; ; ) {
2808
+ try {
2809
+ const balance = await readBalance();
2810
+ if (balance > baseline) {
2811
+ const arrived = balance - baseline;
2812
+ debugLog("owney-sdk", "withdraw: funds landed", {
2813
+ arrived: arrived.toString()
2814
+ });
2815
+ return arrived;
2816
+ }
2817
+ } catch (error) {
2818
+ lastError = error;
2819
+ debugLog("owney-sdk", "withdraw: balance read failed, retrying", {
2820
+ message: error instanceof Error ? error.message : String(error)
2821
+ });
2822
+ }
2823
+ if (Date.now() >= deadline) {
2824
+ throw new OwneyError(
2825
+ "WITHDRAW_ARRIVAL_TIMEOUT",
2826
+ "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.",
2827
+ {
2828
+ baseline: baseline.toString(),
2829
+ waitedMs: timeoutMs,
2830
+ ...lastError ? {
2831
+ lastReadError: lastError instanceof Error ? lastError.message : String(lastError)
2832
+ } : {}
2833
+ }
2834
+ );
2835
+ }
2836
+ await sleep(pollMs);
4031
2837
  }
4032
- return json.data;
4033
2838
  }
4034
2839
 
4035
2840
  // src/lib/health-report.ts
4036
- var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
4037
- async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
2841
+ var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2842
+ async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
4038
2843
  try {
4039
2844
  await fetch(`${baseUrl}/api/v1/agent/health-report`, {
4040
2845
  method: "POST",
@@ -4074,29 +2879,8 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
4074
2879
  const tokenBalance = agentBalance?.tokens.find(
4075
2880
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
4076
2881
  );
4077
- let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
4078
- if (agent.balanceComposition === "tokens-plus-positions") {
4079
- const chainNameById = {
4080
- 1: "ETHEREUM",
4081
- 8453: "BASE",
4082
- 42161: "ARBITRUM"
4083
- };
4084
- const targetChain = chainNameById[chainId];
4085
- for (const position2 of agentBalance?.positions ?? []) {
4086
- const positionChain = position2.chain.trim().toUpperCase();
4087
- const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
4088
- if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
4089
- if (position2.amountRaw !== void 0) {
4090
- try {
4091
- balance += BigInt(position2.amountRaw);
4092
- continue;
4093
- } catch {
4094
- }
4095
- }
4096
- balance += parseUnits(position2.amount, decimals);
4097
- }
4098
- }
4099
- return { agent, balance };
2882
+ if (!tokenBalance) return { agent, balance: 0n };
2883
+ return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
4100
2884
  });
4101
2885
  }
4102
2886
  function planProportionalShares(balances, requested, totalAvailable) {
@@ -4122,9 +2906,7 @@ function planProportionalShares(balances, requested, totalAvailable) {
4122
2906
  return plans;
4123
2907
  }
4124
2908
  function planDisabledDrain(disabled, requested) {
4125
- const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
4126
- (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
4127
- );
2909
+ const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
4128
2910
  const plans = [];
4129
2911
  let remaining = requested;
4130
2912
  for (const { agent, balance } of sorted) {
@@ -4175,13 +2957,6 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
4175
2957
  return Number.isFinite(total) && total > 0 ? total : 0;
4176
2958
  }
4177
2959
  const normalizedToken = tokenSymbol.toUpperCase();
4178
- const snapshots = balance.assetBalances?.filter(
4179
- (token) => Number(token.chainId) === chainId && String(token.asset).toUpperCase() === normalizedToken
4180
- );
4181
- if (snapshots?.length) {
4182
- const amount = snapshots.reduce((sum, token) => sum + Number(token.amount), 0);
4183
- if (Number.isFinite(amount)) return Math.max(0, amount);
4184
- }
4185
2960
  return balance.tokens.reduce((total, token) => {
4186
2961
  if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
4187
2962
  return total;
@@ -4253,312 +3028,329 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
4253
3028
 
4254
3029
  // src/client.ts
4255
3030
  import {
4256
- createPublicClient as createPublicClient4,
4257
- createWalletClient as createWalletClient3,
4258
- custom as custom3
4259
- } from "viem";
4260
- import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
4261
-
4262
- // src/lib/sponsored-token-batch.ts
4263
- import {
4264
- isAddressEqual,
4265
- keccak256,
4266
- toBytes
3031
+ createPublicClient as createPublicClient2,
3032
+ createWalletClient,
3033
+ custom,
3034
+ erc20Abi as erc20Abi2
4267
3035
  } from "viem";
3036
+ import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
4268
3037
 
4269
- // src/lib/permit2-batch.ts
4270
- import { parseAbi as parseAbi2, hashStruct } from "viem";
4271
- var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
4272
- var PERMIT_BATCH_TYPES = {
4273
- PermitBatchWitnessTransferFrom: [
4274
- { name: "permitted", type: "TokenPermissions[]" },
4275
- { name: "spender", type: "address" },
4276
- { name: "nonce", type: "uint256" },
4277
- { name: "deadline", type: "uint256" },
4278
- { name: "witness", type: "Deposit" }
4279
- ],
4280
- Deposit: [{ name: "recipients", type: "address[]" }],
4281
- TokenPermissions: [
4282
- { name: "token", type: "address" },
4283
- { name: "amount", type: "uint256" }
4284
- ]
4285
- };
4286
- var PERMIT2_BATCH_ABI = parseAbi2([
4287
- "struct TokenPermissions { address token; uint256 amount; }",
4288
- "struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
4289
- "struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
4290
- "function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
4291
- "function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
4292
- ]);
4293
- function batchPermit(b) {
4294
- return {
4295
- permitted: b.transfers.map((t) => ({
4296
- token: b.token,
4297
- amount: BigInt(t.amount)
4298
- })),
4299
- nonce: BigInt(b.nonce),
4300
- deadline: BigInt(b.deadline)
4301
- };
4302
- }
4303
- function batchTypedData(b, spender) {
3038
+ // src/lib/transfer-auth.ts
3039
+ import { bytesToHex as bytesToHex2 } from "viem";
3040
+ var ERC20_META_ABI = [
3041
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
3042
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
3043
+ ];
3044
+ function buildTransferWithAuthorizationTypedData(input) {
4304
3045
  return {
4305
- domain: {
4306
- name: "Permit2",
4307
- chainId: b.chainId,
4308
- verifyingContract: BATCH_PERMIT2_ADDRESS
3046
+ domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
3047
+ types: {
3048
+ TransferWithAuthorization: [
3049
+ { name: "from", type: "address" },
3050
+ { name: "to", type: "address" },
3051
+ { name: "value", type: "uint256" },
3052
+ { name: "validAfter", type: "uint256" },
3053
+ { name: "validBefore", type: "uint256" },
3054
+ { name: "nonce", type: "bytes32" }
3055
+ ]
4309
3056
  },
4310
- types: PERMIT_BATCH_TYPES,
4311
- primaryType: "PermitBatchWitnessTransferFrom",
4312
- message: {
4313
- ...batchPermit(b),
4314
- spender,
4315
- witness: { recipients: b.transfers.map((t) => t.to) }
4316
- }
3057
+ primaryType: "TransferWithAuthorization",
3058
+ message: input.message
4317
3059
  };
4318
3060
  }
4319
-
4320
- // src/lib/sponsored-token-batch.ts
4321
- var memory = /* @__PURE__ */ new Map();
4322
- var inflight = /* @__PURE__ */ new Map();
4323
- var planOf = (transfers) => JSON.stringify(
4324
- transfers.map((t) => ({
4325
- to: t.to.toLowerCase(),
4326
- amount: BigInt(t.amount).toString()
4327
- }))
4328
- );
4329
- function read(key2) {
4330
- return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
4331
- }
4332
- function save(key2, body) {
4333
- const value = JSON.stringify({
4334
- ...body,
4335
- transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
4336
- });
4337
- if (typeof window === "undefined") memory.set(key2, value);
4338
- else window.localStorage.setItem(key2, value);
3061
+ async function readTokenMeta(publicClient, token) {
3062
+ const [tokenName, tokenVersion] = await Promise.all([
3063
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
3064
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
3065
+ ]);
3066
+ return { tokenName, tokenVersion };
4339
3067
  }
4340
- function clear(key2) {
4341
- if (typeof window === "undefined") memory.delete(key2);
4342
- else window.localStorage.removeItem(key2);
3068
+ function randomAuthNonce() {
3069
+ const bytes = new Uint8Array(32);
3070
+ globalThis.crypto.getRandomValues(bytes);
3071
+ return bytesToHex2(bytes);
4343
3072
  }
4344
- function sponsorTokenBatch(i) {
4345
- const key2 = `owney.token-batch.v1:${keccak256(toBytes(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
4346
- const plan = planOf(i.transfers);
4347
- const active = inflight.get(key2);
4348
- if (active) {
4349
- if (active.plan !== plan)
4350
- return Promise.reject(
4351
- new Error(
4352
- "A token deposit is already in progress. Wait for its result before depositing again."
4353
- )
4354
- );
4355
- return active.promise;
3073
+
3074
+ // src/lib/sponsor-client.ts
3075
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3076
+ async function postSponsorTransferAuth(input) {
3077
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
3078
+ let res;
3079
+ try {
3080
+ res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
3081
+ method: "POST",
3082
+ headers: {
3083
+ "content-type": "application/json",
3084
+ "x-owney-api-key": input.apiKey
3085
+ },
3086
+ body: JSON.stringify(input.body)
3087
+ });
3088
+ } catch (networkError) {
3089
+ throw new OwneyError(
3090
+ "SPONSOR_REQUEST_FAILED",
3091
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
3092
+ { cause: String(networkError) }
3093
+ );
3094
+ }
3095
+ const text = await res.text();
3096
+ let parsed = null;
3097
+ try {
3098
+ parsed = JSON.parse(text);
3099
+ } catch {
3100
+ }
3101
+ if (!res.ok || !parsed?.success || !parsed.data) {
3102
+ throw new OwneyError(
3103
+ "SPONSOR_REQUEST_FAILED",
3104
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
3105
+ {
3106
+ statusCode: res.status,
3107
+ responseBody: text.slice(0, 500),
3108
+ // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
3109
+ // before broadcast, so it is safe to fall back to a user-paid deposit.
3110
+ safeToFallback: res.status === 503
3111
+ }
3112
+ );
4356
3113
  }
4357
- const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
4358
- inflight.set(key2, { plan, promise });
4359
- return promise;
3114
+ return parsed.data;
4360
3115
  }
4361
- async function execute(i, key2, plan) {
4362
- if (!i.transfers.length || i.transfers.length > 16 || i.transfers.some(
4363
- (t) => BigInt(t.amount) <= 0n || BigInt(t.amount) >= 1n << 256n
4364
- ) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
4365
- throw new Error("Invalid token deposit shares.");
4366
- const send = async (initial) => {
4367
- let body = initial;
4368
- save(key2, body);
4369
- try {
4370
- if (!body.serializedTransaction) {
4371
- const prepared = await postSponsorBatchTransfer({
4372
- apiKey: i.apiKey,
4373
- baseUrl: i.baseUrl,
4374
- body
4375
- });
4376
- if (!prepared.serializedTransaction || keccak256(prepared.serializedTransaction) !== prepared.txHash)
4377
- throw new Error(
4378
- "Sponsorship API did not return a valid prepared transaction."
4379
- );
4380
- body = {
4381
- ...body,
4382
- serializedTransaction: prepared.serializedTransaction
4383
- };
4384
- save(key2, body);
3116
+ async function postSponsorPermit2Transfer(input) {
3117
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
3118
+ let res;
3119
+ try {
3120
+ res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
3121
+ method: "POST",
3122
+ headers: {
3123
+ "content-type": "application/json",
3124
+ "x-owney-api-key": input.apiKey
3125
+ },
3126
+ body: JSON.stringify(input.body)
3127
+ });
3128
+ } catch (networkError) {
3129
+ throw new OwneyError(
3130
+ "SPONSOR_REQUEST_FAILED",
3131
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
3132
+ { cause: String(networkError), safeToFallback: false }
3133
+ );
3134
+ }
3135
+ const text = await res.text();
3136
+ let parsed = null;
3137
+ try {
3138
+ parsed = JSON.parse(text);
3139
+ } catch {
3140
+ }
3141
+ if (!res.ok || !parsed?.success || !parsed.data) {
3142
+ throw new OwneyError(
3143
+ "SPONSOR_REQUEST_FAILED",
3144
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
3145
+ {
3146
+ statusCode: res.status,
3147
+ responseBody: text.slice(0, 500),
3148
+ safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
4385
3149
  }
4386
- const result = await postSponsorBatchTransfer({
4387
- apiKey: i.apiKey,
4388
- baseUrl: i.baseUrl,
4389
- body
4390
- });
4391
- if (result.txHash !== keccak256(body.serializedTransaction))
4392
- throw new Error(
4393
- "Sponsorship receipt does not match the pending transaction."
4394
- );
4395
- clear(key2);
4396
- return result.txHash;
4397
- } catch (error) {
4398
- if (!body.serializedTransaction || error instanceof OwneyError && error.details?.notSubmitted === true)
4399
- clear(key2);
4400
- throw error;
4401
- }
4402
- };
4403
- const saved = read(key2);
4404
- if (saved) {
4405
- const previous = JSON.parse(saved);
4406
- if (previous.chainId !== i.chainId || !isAddressEqual(previous.from, i.owner) || !isAddressEqual(previous.token, i.token) || planOf(previous.transfers) !== plan)
4407
- throw new Error(
4408
- "Retry the previous token deposit and agent split first to reconcile its status."
4409
- );
4410
- i.onApproved?.();
4411
- return send({ ...previous, transfers: i.transfers });
3150
+ );
4412
3151
  }
4413
- const total = i.transfers.reduce((sum, t) => sum + BigInt(t.amount), 0n);
4414
- const [balance, allowance] = await Promise.all([
4415
- readErc20Balance(i.pub, i.token, i.owner),
4416
- readPermit2Allowance(i.pub, i.token, i.owner)
4417
- ]);
4418
- if (balance < total)
3152
+ return parsed.data;
3153
+ }
3154
+ async function getSponsorRelayerAddress(input) {
3155
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
3156
+ let res;
3157
+ try {
3158
+ res = await fetch(
3159
+ `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
3160
+ {
3161
+ headers: { "x-owney-api-key": input.apiKey }
3162
+ }
3163
+ );
3164
+ } catch (networkError) {
4419
3165
  throw new OwneyError(
4420
- "DEPOSIT_INSUFFICIENT_BALANCE",
4421
- "Insufficient token balance for this deposit."
3166
+ "SPONSOR_REQUEST_FAILED",
3167
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
3168
+ { cause: String(networkError), safeToFallback: true }
4422
3169
  );
4423
- if (allowance < total)
3170
+ }
3171
+ const text = await res.text();
3172
+ let parsed = null;
3173
+ try {
3174
+ parsed = JSON.parse(text);
3175
+ } catch {
3176
+ }
3177
+ if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
4424
3178
  throw new OwneyError(
4425
- "PERMIT2_APPROVAL_REQUIRED",
4426
- "token deposits need a one-time Permit2 approval."
3179
+ "SPONSOR_REQUEST_FAILED",
3180
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
3181
+ {
3182
+ statusCode: res.status,
3183
+ responseBody: text.slice(0, 500),
3184
+ safeToFallback: true
3185
+ }
4427
3186
  );
4428
- const relayer = await getSponsorRelayerAddress({
4429
- apiKey: i.apiKey,
4430
- baseUrl: i.baseUrl,
4431
- chainId: i.chainId
4432
- });
4433
- const now = (await i.pub.getBlock()).timestamp;
4434
- const unsigned = {
4435
- chainId: i.chainId,
4436
- token: i.token,
4437
- from: i.owner,
4438
- transfers: i.transfers,
4439
- nonce: randomPermit2Nonce().toString(),
4440
- deadline: (now + 900n).toString()
4441
- };
4442
- const signature = await i.wallet.signTypedData({
4443
- account: i.owner,
4444
- ...batchTypedData(unsigned, relayer)
4445
- });
4446
- i.onApproved?.();
4447
- return send({ ...unsigned, signature });
3187
+ }
3188
+ return parsed.data.relayer;
4448
3189
  }
4449
3190
 
4450
- // src/lib/sponsored-token-deposit.ts
4451
- function makeSponsoredTokenCallback(deps) {
4452
- const batch = async (chainId, transfers) => {
4453
- if (chainId !== 8453 && chainId !== 42161 && chainId !== 1)
3191
+ // src/lib/sponsored-deposit.ts
3192
+ var AUTH_WINDOW_SECONDS = 15 * 60;
3193
+ function makeSponsoredDepositCallback(deps) {
3194
+ const post = deps.httpPost ?? postSponsorTransferAuth;
3195
+ return async (smartWallet, chainId, amount) => {
3196
+ const cid = chainId;
3197
+ const token = deps.tokenAddressByChain[cid];
3198
+ if (!token) {
4454
3199
  throw new OwneyError(
4455
3200
  "CHAIN_UNSUPPORTED",
4456
3201
  `No sponsored token configured for chain ${chainId}`
4457
3202
  );
4458
- const token = deps.tokenAddressByChain[chainId];
4459
- if (!token)
4460
- throw new OwneyError(
4461
- "CHAIN_UNSUPPORTED",
4462
- `No sponsored token configured for chain ${chainId}`
3203
+ }
3204
+ const pub = deps.getPublicClient(cid);
3205
+ const wallet = deps.getWalletClient(cid);
3206
+ await ensureWalletOnChain(pub, wallet, cid);
3207
+ try {
3208
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3209
+ if (balance < BigInt(amount)) {
3210
+ throw new OwneyError(
3211
+ "DEPOSIT_INSUFFICIENT_BALANCE",
3212
+ "Insufficient balance for this deposit.",
3213
+ { token, chainId: cid, balance: balance.toString(), amount }
3214
+ );
3215
+ }
3216
+ } catch (err) {
3217
+ if (err instanceof OwneyError) throw err;
3218
+ console.warn(
3219
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
3220
+ err instanceof Error ? err.message : String(err)
4463
3221
  );
4464
- const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
4465
- await ensureWalletOnChain(pub, wallet, chainId);
4466
- return sponsorTokenBatch({
4467
- apiKey: deps.apiKey,
4468
- baseUrl: deps.baseUrl,
4469
- owner: deps.ownerAddress,
3222
+ }
3223
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
3224
+ const validAfter = 0n;
3225
+ const validBefore = BigInt(
3226
+ Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
3227
+ );
3228
+ const nonce = randomAuthNonce();
3229
+ const typedData = buildTransferWithAuthorizationTypedData({
4470
3230
  token,
4471
- chainId,
4472
- transfers,
4473
- pub,
4474
- wallet,
4475
- onApproved: deps.onApproved
3231
+ chainId: cid,
3232
+ tokenName,
3233
+ tokenVersion,
3234
+ message: {
3235
+ from: deps.ownerAddress,
3236
+ to: smartWallet,
3237
+ value: BigInt(amount),
3238
+ validAfter,
3239
+ validBefore,
3240
+ nonce
3241
+ }
4476
3242
  });
4477
- };
4478
- const callback = makeVerificationAwareDepositCallback(
4479
- (to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
4480
- );
4481
- registerDepositBatch(callback, batch);
4482
- return callback;
4483
- }
4484
-
4485
- // src/lib/agent-deposit-batch.ts
4486
- function deferred() {
4487
- let resolve, reject;
4488
- const promise = new Promise((yes, no) => {
4489
- resolve = yes;
4490
- reject = no;
4491
- });
4492
- void promise.catch(() => {
4493
- });
4494
- return { promise, resolve, reject };
4495
- }
4496
- async function runAgentDepositBatch(chainId, legs, transfer) {
4497
- const funding = deferred();
4498
- const tasks = [];
4499
- const transfers = [];
4500
- try {
4501
- for (const leg of legs) {
4502
- const ready = deferred();
4503
- let entered = false;
4504
- const callback = makeVerificationAwareDepositCallback(
4505
- (to, cid, amount, verification) => {
4506
- if (entered || cid !== chainId || BigInt(amount) !== BigInt(leg.amount)) {
4507
- const error = new Error(
4508
- "Agent changed its prepared deposit share."
4509
- );
4510
- ready.reject(error);
4511
- throw error;
4512
- }
4513
- entered = true;
4514
- ready.resolve(toBatchTransfer(to, amount, verification));
4515
- return funding.promise;
4516
- }
4517
- );
4518
- const task = Promise.resolve().then(() => leg.run(callback));
4519
- tasks.push(task);
4520
- void task.then(
4521
- () => {
4522
- if (!entered)
4523
- ready.reject(
4524
- new Error("Agent did not prepare a deposit transfer.")
4525
- );
4526
- },
4527
- (error) => ready.reject(error)
3243
+ const authSignature = await wallet.signTypedData({
3244
+ account: deps.ownerAddress,
3245
+ ...typedData
3246
+ });
3247
+ deps.onApproved?.();
3248
+ const result = await post({
3249
+ baseUrl: deps.baseUrl,
3250
+ apiKey: deps.apiKey,
3251
+ body: {
3252
+ chainId: cid,
3253
+ token,
3254
+ from: deps.ownerAddress,
3255
+ to: smartWallet,
3256
+ value: amount,
3257
+ validAfter: validAfter.toString(),
3258
+ validBefore: validBefore.toString(),
3259
+ nonce,
3260
+ authSignature,
3261
+ tokenName,
3262
+ tokenVersion
3263
+ }
3264
+ });
3265
+ return result.txHash;
3266
+ };
3267
+ }
3268
+
3269
+ // src/lib/sponsored-weth-deposit.ts
3270
+ var PERMIT_WINDOW_SECONDS = 15 * 60;
3271
+ function makeSponsoredWethCallback(deps) {
3272
+ const get = deps.httpGet ?? getSponsorRelayerAddress;
3273
+ const post = deps.httpPost ?? postSponsorPermit2Transfer;
3274
+ return async (smartWallet, chainId, amount) => {
3275
+ const cid = chainId;
3276
+ const token = deps.tokenAddressByChain[cid];
3277
+ if (!token) {
3278
+ throw new OwneyError(
3279
+ "CHAIN_UNSUPPORTED",
3280
+ `No sponsored WETH configured for chain ${chainId}`
4528
3281
  );
4529
- transfers.push(await ready.promise);
4530
3282
  }
4531
- const txHash = await transfer(chainId, transfers);
4532
- funding.resolve(txHash);
4533
- const settled = await Promise.allSettled(tasks);
4534
- const agentResults = {};
4535
- const failures = [];
4536
- for (const [index, result] of settled.entries()) {
4537
- if (result.status === "fulfilled")
4538
- agentResults[legs[index].id] = result.value;
4539
- else failures.push(legs[index].id);
3283
+ const amountWei = BigInt(amount);
3284
+ const pub = deps.getPublicClient(cid);
3285
+ const wallet = deps.getWalletClient(cid);
3286
+ await ensureWalletOnChain(pub, wallet, cid);
3287
+ try {
3288
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3289
+ if (balance < amountWei) {
3290
+ throw new OwneyError(
3291
+ "DEPOSIT_INSUFFICIENT_BALANCE",
3292
+ "Insufficient WETH balance for this deposit.",
3293
+ { token, chainId: cid, balance: balance.toString(), amount }
3294
+ );
3295
+ }
3296
+ } catch (err) {
3297
+ if (err instanceof OwneyError) throw err;
3298
+ console.warn(
3299
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
3300
+ err instanceof Error ? err.message : String(err)
3301
+ );
4540
3302
  }
4541
- if (failures.length)
3303
+ const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
3304
+ if (allowance < amountWei) {
4542
3305
  throw new OwneyError(
4543
- "DEPOSIT_PARTIAL_FAILURE",
4544
- "The deposit was sent to all agents, but some agent updates could not be confirmed. Check activity before depositing again.",
4545
- {
4546
- txHash,
4547
- fundsSubmitted: true,
4548
- agentResults,
4549
- failedAgentIds: failures
4550
- }
3306
+ "PERMIT2_APPROVAL_REQUIRED",
3307
+ "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3308
+ { token, chainId: cid, allowance: allowance.toString(), amount }
4551
3309
  );
4552
- return { agentResults };
4553
- } catch (error) {
4554
- funding.reject(error);
4555
- await Promise.allSettled(tasks);
4556
- throw error;
4557
- }
3310
+ }
3311
+ const relayer = await get({
3312
+ baseUrl: deps.baseUrl,
3313
+ apiKey: deps.apiKey,
3314
+ chainId: cid
3315
+ });
3316
+ const nonce = randomPermit2Nonce();
3317
+ const deadline = BigInt(
3318
+ Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
3319
+ );
3320
+ const typedData = buildPermitTransferFromTypedData({
3321
+ chainId: cid,
3322
+ message: {
3323
+ permitted: { token, amount: amountWei },
3324
+ spender: relayer,
3325
+ nonce,
3326
+ deadline
3327
+ }
3328
+ });
3329
+ const signature = await wallet.signTypedData({
3330
+ account: deps.ownerAddress,
3331
+ ...typedData
3332
+ });
3333
+ deps.onApproved?.();
3334
+ const result = await post({
3335
+ baseUrl: deps.baseUrl,
3336
+ apiKey: deps.apiKey,
3337
+ body: {
3338
+ chainId: cid,
3339
+ token,
3340
+ from: deps.ownerAddress,
3341
+ to: smartWallet,
3342
+ amount,
3343
+ nonce: nonce.toString(),
3344
+ deadline: deadline.toString(),
3345
+ signature
3346
+ }
3347
+ });
3348
+ return result.txHash;
3349
+ };
4558
3350
  }
4559
3351
 
4560
3352
  // src/lib/sponsored-calls-deposit.ts
4561
- import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi2, toHex } from "viem";
3353
+ import { encodeFunctionData, erc20Abi, toHex as toHex2 } from "viem";
4562
3354
  var DEFAULT_POLL_INTERVAL_MS = 1500;
4563
3355
  var DEFAULT_MAX_POLLS = 30;
4564
3356
  async function paymasterSupported(provider, owner, chainId) {
@@ -4566,7 +3358,7 @@ async function paymasterSupported(provider, owner, chainId) {
4566
3358
  method: "wallet_getCapabilities",
4567
3359
  params: [owner]
4568
3360
  });
4569
- const forChain = caps?.[toHex(chainId)] ?? caps?.[String(chainId)];
3361
+ const forChain = caps?.[toHex2(chainId)] ?? caps?.[String(chainId)];
4570
3362
  return Boolean(forChain?.paymasterService?.supported);
4571
3363
  }
4572
3364
  function makeSponsoredCallsCallback(deps) {
@@ -4584,7 +3376,7 @@ function makeSponsoredCallsCallback(deps) {
4584
3376
  }
4585
3377
  return new URL(configured, origin).toString();
4586
3378
  };
4587
- const batch = async (chainId, transfers) => {
3379
+ return async (smartWallet, chainId, amount) => {
4588
3380
  const cid = chainId;
4589
3381
  const token = deps.tokenAddressByChain[cid];
4590
3382
  if (!token) {
@@ -4600,53 +3392,22 @@ function makeSponsoredCallsCallback(deps) {
4600
3392
  { chainId }
4601
3393
  );
4602
3394
  }
4603
- const calls = transfers.map((transfer) => ({
4604
- to: token,
4605
- value: "0x0",
4606
- data: encodeFunctionData2({
4607
- abi: erc20Abi2,
4608
- functionName: "transfer",
4609
- args: [transfer.to, BigInt(transfer.amount)]
4610
- })
4611
- }));
4612
- let paymasterUrl = absolutePaymasterUrl();
4613
- for (const transfer of transfers) {
4614
- const verification = transfer.yieldseeker;
4615
- if (!verification) continue;
4616
- if (chainId !== 8453)
4617
- throw new OwneyError(
4618
- "CHAIN_UNSUPPORTED",
4619
- `Yieldseeker sponsorship is not available on chain ${chainId}.`
4620
- );
4621
- const { intent } = await postPaymasterIntent({
4622
- baseUrl: deps.routingApiBaseUrl,
4623
- apiKey: deps.apiKey,
4624
- yieldseekerSignature: verification.signature,
4625
- body: {
4626
- chainId,
4627
- token,
4628
- from: deps.ownerAddress,
4629
- to: transfer.to,
4630
- amount: transfer.amount,
4631
- yieldseekerUserId: verification.userId,
4632
- yieldseekerAgentId: verification.agentId
4633
- }
4634
- });
4635
- const url = new URL(paymasterUrl);
4636
- url.searchParams.append("owneyIntent", intent);
4637
- paymasterUrl = url.toString();
4638
- }
3395
+ const data = encodeFunctionData({
3396
+ abi: erc20Abi,
3397
+ functionName: "transfer",
3398
+ args: [smartWallet, BigInt(amount)]
3399
+ });
4639
3400
  const sendResult = await deps.provider.request({
4640
3401
  method: "wallet_sendCalls",
4641
3402
  params: [
4642
3403
  {
4643
3404
  version: "2.0.0",
4644
3405
  from: deps.ownerAddress,
4645
- chainId: toHex(chainId),
4646
- atomicRequired: transfers.length > 1,
4647
- calls,
3406
+ chainId: toHex2(chainId),
3407
+ atomicRequired: false,
3408
+ calls: [{ to: token, value: "0x0", data }],
4648
3409
  capabilities: {
4649
- paymasterService: { url: paymasterUrl }
3410
+ paymasterService: { url: absolutePaymasterUrl() }
4650
3411
  }
4651
3412
  }
4652
3413
  ]
@@ -4666,24 +3427,7 @@ function makeSponsoredCallsCallback(deps) {
4666
3427
  params: [callsId]
4667
3428
  });
4668
3429
  const txHash = status?.receipts?.[0]?.transactionHash;
4669
- if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
4670
- throw new OwneyError(
4671
- "SPONSOR_REQUEST_FAILED",
4672
- "The sponsored deposit did not complete successfully.",
4673
- { chainId, callsId, safeToFallback: false }
4674
- );
4675
- }
4676
- if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
4677
- if (status?.receipts?.some(
4678
- (receipt) => receipt.transactionHash !== txHash
4679
- ))
4680
- throw new OwneyError(
4681
- "SPONSORED_CALLS_NO_RECEIPT",
4682
- "The wallet returned multiple transactions for an atomic deposit. Check activity before trying again.",
4683
- { chainId, callsId }
4684
- );
4685
- return txHash;
4686
- }
3430
+ if (txHash) return txHash;
4687
3431
  if (pollIntervalMs > 0) {
4688
3432
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
4689
3433
  }
@@ -4694,16 +3438,9 @@ function makeSponsoredCallsCallback(deps) {
4694
3438
  { chainId, callsId }
4695
3439
  );
4696
3440
  };
4697
- const callback = makeVerificationAwareDepositCallback(
4698
- (to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
4699
- );
4700
- registerDepositBatch(callback, batch);
4701
- return callback;
4702
3441
  }
4703
3442
 
4704
3443
  // src/client.ts
4705
- var PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS = 6;
4706
- var PERMIT2_ALLOWANCE_VERIFY_DELAY_MS = 250;
4707
3444
  function encodeMultiAgentCursor(map) {
4708
3445
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
4709
3446
  }
@@ -4730,7 +3467,7 @@ var SPONSORED_USDC_BY_CHAIN = {
4730
3467
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
4731
3468
  };
4732
3469
  var VIEM_CHAIN2 = {
4733
- 8453: base4,
3470
+ 8453: base2,
4734
3471
  42161: arbitrum2,
4735
3472
  1: mainnet2
4736
3473
  };
@@ -4739,11 +3476,6 @@ var SPONSORED_WETH_BY_CHAIN = {
4739
3476
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
4740
3477
  1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
4741
3478
  };
4742
- var SPONSORED_TOKENS_BY_ASSET = { USDC: SPONSORED_USDC_BY_CHAIN, WETH: SPONSORED_WETH_BY_CHAIN };
4743
- function sponsoredTokensFor(asset) {
4744
- if (!Object.prototype.hasOwnProperty.call(SPONSORED_TOKENS_BY_ASSET, asset)) throw new OwneyError("ASSET_UNSUPPORTED", `No sponsored token configured for ${asset}`);
4745
- return SPONSORED_TOKENS_BY_ASSET[asset];
4746
- }
4747
3479
  function shouldFallbackToUserPaid(error, asset, appCallback) {
4748
3480
  return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
4749
3481
  }
@@ -4765,8 +3497,6 @@ var OwneySDK = class {
4765
3497
  orgAgentConfig;
4766
3498
  orgAgentConfigPromise = null;
4767
3499
  zyfaiRpcUrls;
4768
- yieldseekerApiBaseUrl;
4769
- yieldseekerSiweOrigin;
4770
3500
  routingApiBaseUrl;
4771
3501
  referralSource;
4772
3502
  cachedSponsoredCallback = null;
@@ -4789,8 +3519,6 @@ var OwneySDK = class {
4789
3519
  this.apiKey = config.apiKey;
4790
3520
  if (config.debug) setOwneyDebug(true);
4791
3521
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
4792
- this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
4793
- this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
4794
3522
  this.routingApiBaseUrl = config.routingApiBaseUrl;
4795
3523
  this.paymasterServiceUrl = config.paymasterServiceUrl;
4796
3524
  this.referralSource = config.referralSource;
@@ -4824,7 +3552,6 @@ var OwneySDK = class {
4824
3552
  * After calling this, `connect()` must be called again before using agent methods.
4825
3553
  */
4826
3554
  async disconnect() {
4827
- this.state = null;
4828
3555
  for (const agent of this.agents.values()) {
4829
3556
  await agent.disconnect();
4830
3557
  }
@@ -4878,13 +3605,18 @@ var OwneySDK = class {
4878
3605
  }
4879
3606
  return this.state.provider;
4880
3607
  }
4881
- /** Builds the default USDC batch callback for the connected wallet. */
3608
+ /**
3609
+ * Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
3610
+ * used when the caller omits `depositCallback`. Wraps the connected EIP-1193
3611
+ * provider with viem `custom(provider)` to read token meta and sign the
3612
+ * `TransferWithAuthorization`, then POSTs to the sponsor API.
3613
+ */
4882
3614
  getDefaultSponsoredCallback(onApproved) {
4883
3615
  if (!onApproved && this.cachedSponsoredCallback)
4884
3616
  return this.cachedSponsoredCallback;
4885
3617
  const provider = this.requireConnectedProvider();
4886
3618
  const owner = this.state.walletAddress;
4887
- const callback = makeSponsoredTokenCallback({
3619
+ const callback = makeSponsoredDepositCallback({
4888
3620
  apiKey: this.apiKey,
4889
3621
  baseUrl: this.routingApiBaseUrl,
4890
3622
  ownerAddress: owner,
@@ -4893,32 +3625,35 @@ var OwneySDK = class {
4893
3625
  // Casts work around viem's chain-narrowed Client vs the generic
4894
3626
  // PublicClient/WalletClient param types — structurally identical at
4895
3627
  // runtime, but the two share a name TS treats as unrelated.
4896
- getPublicClient: (cid) => createPublicClient4({
3628
+ getPublicClient: (cid) => createPublicClient2({
4897
3629
  chain: VIEM_CHAIN2[cid],
4898
- transport: custom3(provider)
3630
+ transport: custom(provider)
4899
3631
  }),
4900
- getWalletClient: (cid) => createWalletClient3({
3632
+ getWalletClient: (cid) => createWalletClient({
4901
3633
  account: owner,
4902
3634
  chain: VIEM_CHAIN2[cid],
4903
- transport: custom3(provider)
3635
+ transport: custom(provider)
4904
3636
  })
4905
3637
  });
4906
3638
  if (!onApproved) this.cachedSponsoredCallback = callback;
4907
3639
  return callback;
4908
3640
  }
4909
- /** Builds the wallet-native sponsored calls callback for compatible paymasters. */
3641
+ /**
3642
+ * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
3643
+ * callback used when the caller omits `depositCallback` for a WETH
3644
+ * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
3645
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
3646
+ */
4910
3647
  getDefaultSponsoredCallsCallback(asset, onApproved) {
4911
3648
  const cached = this.cachedSponsoredCallsCallbacks.get(asset);
4912
3649
  if (!onApproved && cached) return cached;
4913
3650
  const provider = this.requireConnectedProvider();
4914
3651
  const callback = makeSponsoredCallsCallback({
4915
- apiKey: this.apiKey,
4916
- routingApiBaseUrl: this.routingApiBaseUrl,
4917
3652
  provider,
4918
3653
  ownerAddress: this.state.walletAddress,
4919
3654
  paymasterServiceUrl: this.paymasterServiceUrl,
4920
3655
  onApproved,
4921
- tokenAddressByChain: sponsoredTokensFor(asset)
3656
+ tokenAddressByChain: asset === "WETH" ? SPONSORED_WETH_BY_CHAIN : SPONSORED_USDC_BY_CHAIN
4922
3657
  });
4923
3658
  if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
4924
3659
  return callback;
@@ -4927,14 +3662,14 @@ var OwneySDK = class {
4927
3662
  * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
4928
3663
  * callback used when the caller omits `depositCallback` for a WETH deposit.
4929
3664
  * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
4930
- * single-use batch authorization instead of an EIP-3009 authorization.
3665
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
4931
3666
  */
4932
3667
  getDefaultWethSponsoredCallback(onApproved) {
4933
3668
  if (!onApproved && this.cachedWethSponsoredCallback)
4934
3669
  return this.cachedWethSponsoredCallback;
4935
3670
  const provider = this.requireConnectedProvider();
4936
3671
  const owner = this.state.walletAddress;
4937
- const callback = makeSponsoredTokenCallback({
3672
+ const callback = makeSponsoredWethCallback({
4938
3673
  apiKey: this.apiKey,
4939
3674
  baseUrl: this.routingApiBaseUrl,
4940
3675
  ownerAddress: owner,
@@ -4943,14 +3678,14 @@ var OwneySDK = class {
4943
3678
  // Casts work around viem's chain-narrowed Client vs the generic
4944
3679
  // PublicClient/WalletClient param types — structurally identical at
4945
3680
  // runtime, but the two share a name TS treats as unrelated.
4946
- getPublicClient: (cid) => createPublicClient4({
3681
+ getPublicClient: (cid) => createPublicClient2({
4947
3682
  chain: VIEM_CHAIN2[cid],
4948
- transport: custom3(provider)
3683
+ transport: custom(provider)
4949
3684
  }),
4950
- getWalletClient: (cid) => createWalletClient3({
3685
+ getWalletClient: (cid) => createWalletClient({
4951
3686
  account: owner,
4952
3687
  chain: VIEM_CHAIN2[cid],
4953
- transport: custom3(provider)
3688
+ transport: custom(provider)
4954
3689
  })
4955
3690
  });
4956
3691
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -5024,14 +3759,7 @@ var OwneySDK = class {
5024
3759
  this.routingApiBaseUrl
5025
3760
  );
5026
3761
  this.disabledAgents.clear();
5027
- for (const {
5028
- key: key2,
5029
- agent_type,
5030
- is_enabled,
5031
- is_configured
5032
- } of agentKeys) {
5033
- const configured = is_configured ?? Boolean(key2);
5034
- if (!configured) continue;
3762
+ for (const { key: key2, agent_type, is_enabled } of agentKeys) {
5035
3763
  const agent = this.createAgent(agent_type, key2);
5036
3764
  if (!agent) continue;
5037
3765
  this.agents.set(agent_type, agent);
@@ -5055,15 +3783,8 @@ var OwneySDK = class {
5055
3783
  }
5056
3784
  createAgent(agentId, key2) {
5057
3785
  if (agentId === "zyfai") {
5058
- if (!key2) return null;
5059
3786
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
5060
3787
  }
5061
- if (agentId === "yieldseeker") {
5062
- return new YieldseekerAgent(this.apiKey, {
5063
- auth: { origin: this.yieldseekerSiweOrigin },
5064
- baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
5065
- });
5066
- }
5067
3788
  return null;
5068
3789
  }
5069
3790
  /**
@@ -5108,10 +3829,9 @@ var OwneySDK = class {
5108
3829
  * If provided, ALL specified agents must support the chainId or the call
5109
3830
  * throws before activating any agent.
5110
3831
  */
5111
- async activateAgent(chainId, agentId, asset) {
3832
+ async activateAgent(chainId, agentId) {
5112
3833
  const state = this.requireState();
5113
3834
  await this.ensureAgentsInitialized();
5114
- this.assertActivationSession(state);
5115
3835
  if (agentId !== void 0) {
5116
3836
  if (agentId.length === 0) {
5117
3837
  throw new OwneyError(
@@ -5145,7 +3865,7 @@ var OwneySDK = class {
5145
3865
  this.activeAgents.add(id);
5146
3866
  }
5147
3867
  state.chainId = chainId;
5148
- await this.activateAgentsInTurn(agents, state, chainId, asset);
3868
+ await this.activateAgentsInTurn(agents, state, chainId);
5149
3869
  return;
5150
3870
  }
5151
3871
  const compatible = [...this.agents.values()].filter(
@@ -5166,12 +3886,7 @@ var OwneySDK = class {
5166
3886
  const enabledCompatible = compatible.filter(
5167
3887
  (agent) => !this.isAgentDisabled(agent.id)
5168
3888
  );
5169
- await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
5170
- }
5171
- assertActivationSession(state) {
5172
- if (this.state !== state) {
5173
- throw new OwneyError("NOT_CONNECTED", "Wallet connection changed. Please try again.");
5174
- }
3889
+ await this.activateAgentsInTurn(enabledCompatible, state, chainId);
5175
3890
  }
5176
3891
  /**
5177
3892
  * Activate agents ONE AT A TIME, each followed by its org policy.
@@ -5186,51 +3901,26 @@ var OwneySDK = class {
5186
3901
  * Serializing costs no real wall-clock: the user can only approve one prompt
5187
3902
  * at a time anyway.
5188
3903
  *
5189
- * Stop at the first failure so a canceled sign-in does not open another
5190
- * agent's wallet prompt. Report any earlier successes for diagnostics; the
5191
- * app discards the session when the complete sign-in does not succeed.
3904
+ * Every agent is attempted even if an earlier one fails, so one declined
3905
+ * signature can't deny the remaining agents their turn. The first failure is
3906
+ * rethrown (matching the previous `Promise.all` rejection) once all agents
3907
+ * have had a chance to activate.
5192
3908
  */
5193
- async activateAgentsInTurn(agents, state, chainId, asset) {
3909
+ async activateAgentsInTurn(agents, state, chainId) {
5194
3910
  let firstError = null;
5195
- const activatedAgentIds = [];
5196
- const failedAgents = [];
5197
3911
  for (const agent of agents) {
5198
- this.assertActivationSession(state);
5199
3912
  try {
5200
- await agent.activateAgent(state, chainId, asset);
5201
- this.assertActivationSession(state);
3913
+ await agent.activateAgent(state, chainId);
5202
3914
  await this.applyOrgPolicyTo(agent, state, chainId);
5203
- this.assertActivationSession(state);
5204
- activatedAgentIds.push(agent.id);
5205
3915
  } catch (error) {
5206
- this.assertActivationSession(state);
5207
- 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.";
5208
- failedAgents.push({
5209
- agentId: agent.id,
5210
- code: error instanceof OwneyError ? error.code : void 0,
5211
- message,
5212
- ...error instanceof OwneyError && error.details ? { details: error.details } : {}
5213
- });
5214
3916
  if (firstError === null) {
5215
3917
  firstError = error;
5216
3918
  } else {
5217
3919
  console.error(`activateAgent(${agent.id}) failed:`, error);
5218
3920
  }
5219
- break;
5220
3921
  }
5221
3922
  }
5222
- if (firstError === null) return;
5223
- if (activatedAgentIds.length === 0) throw firstError;
5224
- const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
5225
- const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
5226
- const failureMessages = failedAgents.map(
5227
- ({ agentId, message }) => `${this.formatAgentName(agentId)} activation failed: ${message}`
5228
- ).join(" ");
5229
- throw new OwneyError(
5230
- "AGENT_ACTIVATION_PARTIAL_FAILURE",
5231
- `${activeNames} activated. ${failureMessages}`,
5232
- { activatedAgentIds, failedAgentIds, failures: failedAgents }
5233
- );
3923
+ if (firstError !== null) throw firstError;
5234
3924
  }
5235
3925
  /**
5236
3926
  * Deposit into a specific agent, or distribute across all eligible agents.
@@ -5240,8 +3930,7 @@ var OwneySDK = class {
5240
3930
  * @param options.asset - Asset symbol to deposit (e.g. "USDC")
5241
3931
  * @param options.depositCallback - Callback that performs the token transfer and returns a tx hash.
5242
3932
  * When agentId is omitted, this callback is invoked once per eligible agent with that agent's
5243
- * split amount and smart wallet address. Default sponsored deposits batch
5244
- * all shares into one signature; custom callbacks still run once per agent.
3933
+ * split amount and smart wallet address — expect multiple wallet prompts.
5245
3934
  * @param options.agentId - Optional explicit target. Otherwise split equally,
5246
3935
  * or fund remaining agents when a recovery deposit cannot meet every minimum.
5247
3936
  * @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
@@ -5326,40 +4015,6 @@ var OwneySDK = class {
5326
4015
  }
5327
4016
  );
5328
4017
  }
5329
- const batchTransfer = getDepositBatchTransfer(effectiveCallback);
5330
- if (!depositCallback && batchTransfer) {
5331
- return runAgentDepositBatch(
5332
- chainId,
5333
- agentAmounts.map(({ agent, amount: amount2 }) => ({
5334
- id: agent.id,
5335
- amount: amount2,
5336
- run: (callback) => withFailureReporting(
5337
- this.apiKey,
5338
- agent.id,
5339
- () => agent.deposit(state, chainId, amount2, asset, callback),
5340
- this.routingApiBaseUrl
5341
- )
5342
- })),
5343
- async (cid, transfers) => {
5344
- try {
5345
- return await batchTransfer(cid, transfers);
5346
- } catch (error) {
5347
- if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
5348
- throw error;
5349
- const requiredAmount = transfers.reduce(
5350
- (sum, transfer) => sum + BigInt(transfer.amount),
5351
- 0n
5352
- );
5353
- await this.approvePermit2(
5354
- asset,
5355
- requiredAmount,
5356
- cid
5357
- );
5358
- return batchTransfer(cid, transfers);
5359
- }
5360
- }
5361
- );
5362
- }
5363
4018
  const agentResults = {};
5364
4019
  for (const [
5365
4020
  index,
@@ -5399,7 +4054,7 @@ var OwneySDK = class {
5399
4054
  *
5400
4055
  * 1. Missing Permit2 allowance: when the app did not supply its own
5401
4056
  * callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
5402
- * token deposit, this is the wallet's first Permit2 deposit for that token. We send
4057
+ * WETH deposit, this is the wallet's first gasless WETH deposit. We send
5403
4058
  * the one-time (user-paid) Permit2 approval via `approvePermit2()` and
5404
4059
  * retry the SAME sponsored attempt once. Bounded to one approval attempt
5405
4060
  * per call so a wallet/agent that keeps reporting the allowance as
@@ -5436,16 +4091,12 @@ var OwneySDK = class {
5436
4091
  try {
5437
4092
  return await attempt(effectiveCallback);
5438
4093
  } catch (error) {
5439
- if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
4094
+ if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
5440
4095
  approvalAttempted = true;
5441
4096
  console.warn(
5442
- "[owney-sdk] First token deposit: sending one-time Permit2 approval..."
5443
- );
5444
- await this.approvePermit2(
5445
- asset,
5446
- BigInt(amount),
5447
- chainId
4097
+ "[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
5448
4098
  );
4099
+ await this.approvePermit2();
5449
4100
  continue;
5450
4101
  }
5451
4102
  if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
@@ -5483,10 +4134,10 @@ var OwneySDK = class {
5483
4134
  agent,
5484
4135
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
5485
4136
  }));
5486
- const valid2 = splits.filter(
4137
+ const valid = splits.filter(
5487
4138
  (s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
5488
4139
  );
5489
- if (valid2.length === agents.length) {
4140
+ if (valid.length === agents.length) {
5490
4141
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
5491
4142
  }
5492
4143
  const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
@@ -5505,11 +4156,6 @@ var OwneySDK = class {
5505
4156
  )
5506
4157
  }));
5507
4158
  }
5508
- formatAgentName(agentId) {
5509
- if (agentId === "zyfai") return "Zyfai";
5510
- if (agentId === "yieldseeker") return "Yieldseeker";
5511
- return agentId;
5512
- }
5513
4159
  async validateMinDepositAmount(agent, state, chainId, asset, amount) {
5514
4160
  const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
5515
4161
  const parsedAmount = BigInt(amount);
@@ -5541,12 +4187,12 @@ var OwneySDK = class {
5541
4187
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
5542
4188
  );
5543
4189
  const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
5544
- const position2 = (balance.positions ?? []).find((p) => {
4190
+ const position = (balance.positions ?? []).find((p) => {
5545
4191
  const positionChain = p.chain.trim().toUpperCase();
5546
4192
  const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
5547
4193
  return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
5548
4194
  });
5549
- return !!token && Number(token.amount) > 0 || !!position2;
4195
+ return !!token && Number(token.amount) > 0 || !!position;
5550
4196
  } catch (error) {
5551
4197
  if (requireReliableRead) {
5552
4198
  throw new OwneyError(
@@ -5592,6 +4238,354 @@ var OwneySDK = class {
5592
4238
  return eligible;
5593
4239
  }
5594
4240
  // --- Fund operations ---
4241
+ // --- Swap to yield (ROUT-242) ---
4242
+ /** Lazily built so an app that never swaps pays nothing for it. */
4243
+ swapApiClient;
4244
+ swapApi() {
4245
+ this.swapApiClient ??= createSwapApi(
4246
+ this.routingApiBaseUrl ?? ROUTING_API_BASE_URL,
4247
+ this.apiKey
4248
+ );
4249
+ return this.swapApiClient;
4250
+ }
4251
+ /**
4252
+ * Put the wallet on `chainId`, or fail with something actionable.
4253
+ *
4254
+ * Reuses the same guard the deposit rail uses, which re-reads the chain after
4255
+ * switching — some wallets resolve wallet_switchEthereumChain before the
4256
+ * network has actually changed.
4257
+ */
4258
+ async ensureSwapChain(chainId) {
4259
+ const provider = this.requireConnectedProvider();
4260
+ const state = this.requireState();
4261
+ const chain = VIEM_CHAIN2[chainId];
4262
+ if (!chain) {
4263
+ throw new OwneyError(
4264
+ "CHAIN_UNSUPPORTED",
4265
+ `Chain ${chainId} is not supported`,
4266
+ { chainId }
4267
+ );
4268
+ }
4269
+ await ensureWalletOnChain(
4270
+ createPublicClient2({ chain, transport: custom(provider) }),
4271
+ createWalletClient({
4272
+ account: state.walletAddress,
4273
+ chain,
4274
+ transport: custom(provider)
4275
+ }),
4276
+ chainId
4277
+ );
4278
+ }
4279
+ /**
4280
+ * Binds the executor's abstract deps to this client's wallet.
4281
+ *
4282
+ * Kept as a builder rather than baked into the executor so the whole swap
4283
+ * flow stays testable without a provider — the executor never imports viem.
4284
+ */
4285
+ buildSwapDeps(quote) {
4286
+ const state = this.requireState();
4287
+ const provider = this.requireConnectedProvider();
4288
+ const srcChain = VIEM_CHAIN2[quote.src.chainId];
4289
+ const dstChain = VIEM_CHAIN2[quote.dst.chainId];
4290
+ const wallet = createWalletClient({
4291
+ account: state.walletAddress,
4292
+ chain: srcChain,
4293
+ transport: custom(provider)
4294
+ });
4295
+ const srcPublic = createPublicClient2({
4296
+ chain: srcChain,
4297
+ transport: swapReadTransport(quote.src.chainId, this.zyfaiRpcUrls)
4298
+ });
4299
+ const dstPublic = createPublicClient2({
4300
+ chain: dstChain,
4301
+ transport: swapReadTransport(quote.dst.chainId, this.zyfaiRpcUrls)
4302
+ });
4303
+ return {
4304
+ api: this.swapApi(),
4305
+ // Native-aware, like readSourceBalance below. Native ETH is never a
4306
+ // DEPOSIT target, so this only ever mattered once withdrawal shipped —
4307
+ // and there it is the headline case. balanceOf() on the 0xEeee sentinel
4308
+ // reverts, which would have read as "the swap landed nothing".
4309
+ readTargetBalance: async () => {
4310
+ const dst = quote.dst.address;
4311
+ if (dst.toLowerCase().startsWith("0xeeee")) {
4312
+ return dstPublic.getBalance({ address: state.walletAddress });
4313
+ }
4314
+ return dstPublic.readContract({
4315
+ address: dst,
4316
+ abi: erc20Abi2,
4317
+ functionName: "balanceOf",
4318
+ args: [state.walletAddress]
4319
+ });
4320
+ },
4321
+ sendTransaction: async (tx) => {
4322
+ const hash = await wallet.sendTransaction({
4323
+ to: tx.to,
4324
+ data: tx.data,
4325
+ value: BigInt(tx.value || "0"),
4326
+ account: state.walletAddress,
4327
+ chain: srcChain
4328
+ });
4329
+ const receipt = await srcPublic.waitForTransactionReceipt({
4330
+ timeout: receiptTimeoutMs(quote.src.chainId),
4331
+ hash,
4332
+ confirmations: 1
4333
+ });
4334
+ if (receipt.status !== "success") {
4335
+ throw new OwneyError(
4336
+ "SWAP_REQUEST_FAILED",
4337
+ `Swap transaction reverted (tx ${hash})`,
4338
+ { hash }
4339
+ );
4340
+ }
4341
+ return hash;
4342
+ },
4343
+ signTypedData: (typedData) => wallet.signTypedData({
4344
+ account: state.walletAddress,
4345
+ ...typedData
4346
+ }),
4347
+ // Chain-bound like every other read here: the wallet provider's chain is
4348
+ // not ours to rely on mid-swap.
4349
+ readSourceBalance: async () => {
4350
+ const src = quote.src.address;
4351
+ if (src.toLowerCase().startsWith("0xeeee")) {
4352
+ return srcPublic.getBalance({ address: state.walletAddress });
4353
+ }
4354
+ return srcPublic.readContract({
4355
+ address: src,
4356
+ abi: ERC20_ALLOWANCE_ABI,
4357
+ functionName: "balanceOf",
4358
+ args: [state.walletAddress]
4359
+ });
4360
+ },
4361
+ readAllowance: (spender) => srcPublic.readContract({
4362
+ address: quote.src.address,
4363
+ abi: ERC20_ALLOWANCE_ABI,
4364
+ functionName: "allowance",
4365
+ args: [state.walletAddress, spender]
4366
+ }),
4367
+ approve: async (spender, amount) => {
4368
+ const hash = await wallet.writeContract({
4369
+ address: quote.src.address,
4370
+ abi: ERC20_ALLOWANCE_ABI,
4371
+ functionName: "approve",
4372
+ args: [spender, amount],
4373
+ account: state.walletAddress,
4374
+ chain: srcChain
4375
+ });
4376
+ await srcPublic.waitForTransactionReceipt({
4377
+ hash,
4378
+ confirmations: 1,
4379
+ timeout: receiptTimeoutMs(quote.src.chainId)
4380
+ });
4381
+ return hash;
4382
+ },
4383
+ ensureChain: (chainId) => this.ensureSwapChain(chainId),
4384
+ runner: {
4385
+ readyForSecrets: (h) => this.swapApi().readyForSecrets(h),
4386
+ submitSecret: (h, secret) => this.swapApi().submitSecret(h, secret),
4387
+ orderStatus: (h) => this.swapApi().orderStatus(h),
4388
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
4389
+ now: () => Date.now()
4390
+ }
4391
+ };
4392
+ }
4393
+ /**
4394
+ * Assets the user may pay with, and what each chain deposits into.
4395
+ *
4396
+ * The source list is deliberately wider than the deposit list: it includes
4397
+ * native ETH and USDT, which Owney never holds but users often do.
4398
+ */
4399
+ async getSwapTokens() {
4400
+ return this.swapApi().listTokens();
4401
+ }
4402
+ /**
4403
+ * Search the assets a user may pay with on one chain.
4404
+ *
4405
+ * `getSwapTokens` returns the short list worth rendering unprompted. This
4406
+ * reaches everything else the routing API will accept — thousands per chain
4407
+ * once the wider allowlist is enabled, which is why it is a query rather
4408
+ * than a download.
4409
+ *
4410
+ * Results are filtered server-side to what a quote will accept, so anything
4411
+ * returned can be paid with. They are NOT ranked by trustworthiness: several
4412
+ * tokens can share a ticker, and `providers` (how many token lists carry the
4413
+ * address) is the only usable signal for telling them apart. Surface it.
4414
+ *
4415
+ * Returns nothing for a blank query rather than asking for the whole list.
4416
+ */
4417
+ async searchSwapTokens(params) {
4418
+ const query = params.query.trim();
4419
+ if (!query) return { tokens: [] };
4420
+ return this.swapApi().searchTokens({
4421
+ chainId: params.chainId,
4422
+ query,
4423
+ ...params.limit === void 0 ? {} : { limit: params.limit }
4424
+ });
4425
+ }
4426
+ /**
4427
+ * Price a swap without committing to it.
4428
+ *
4429
+ * `dstAmountMin` is the number to validate against a deposit minimum —
4430
+ * `dst.amount` is an estimate that a decaying auction or slippage can undercut,
4431
+ * and a swap landing below the floor leaves the user swapped but not
4432
+ * deposited.
4433
+ */
4434
+ async getSwapQuote(params) {
4435
+ const state = this.requireState();
4436
+ return this.swapApi().quote({
4437
+ ...params,
4438
+ walletAddress: state.walletAddress
4439
+ });
4440
+ }
4441
+ /**
4442
+ * Swap an asset the user holds into a deposit asset, then deposit it.
4443
+ *
4444
+ * Kept separate from `deposit()` rather than bolted on as an option: the
4445
+ * return shape differs, the staging callback is meaningless on the plain
4446
+ * path, and integrators who never swap should not have to reason about any
4447
+ * of it.
4448
+ *
4449
+ * The deposit runs on the MEASURED arrival, not the quote. A quote is an
4450
+ * estimate, so depositing the quoted figure would either strand dust or try
4451
+ * to move funds that never came.
4452
+ *
4453
+ * Failure modes differ in a way callers must respect. A same-chain swap is
4454
+ * atomic — if it fails, nothing moved. A cross-chain swap escrows the user's
4455
+ * funds first, so SWAP_ORDER_EXPIRED / REFUNDED / CANCELLED all mean the
4456
+ * money left the wallet. Only the former can honestly say "nothing has left
4457
+ * your wallet".
4458
+ */
4459
+ async swapAndDeposit(options) {
4460
+ const state = this.requireState();
4461
+ const api = this.swapApi();
4462
+ const quote = await api.quote({
4463
+ from: options.from,
4464
+ to: options.to,
4465
+ walletAddress: state.walletAddress
4466
+ });
4467
+ await this.ensureSwapChain(quote.src.chainId);
4468
+ const swap = await executeSwap(this.buildSwapDeps(quote), {
4469
+ quote,
4470
+ walletAddress: state.walletAddress,
4471
+ ...options.slippage === void 0 ? {} : { slippage: options.slippage },
4472
+ ...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
4473
+ });
4474
+ options.onSwapProgress?.("depositing");
4475
+ await this.ensureSwapChain(quote.dst.chainId);
4476
+ const deposit = await this.deposit({
4477
+ amount: swap.received,
4478
+ asset: options.to.symbol,
4479
+ ...options.agentId ? { agentId: options.agentId } : {}
4480
+ });
4481
+ return { swap, deposit };
4482
+ }
4483
+ /**
4484
+ * Withdraw from an agent and swap the proceeds into whatever the user wants
4485
+ * to hold, delivered to their own wallet.
4486
+ *
4487
+ * The mirror of `swapAndDeposit()`, with one structural difference that
4488
+ * drives the whole implementation: a deposit swap starts from funds already
4489
+ * sitting in the wallet, but a withdrawal has to wait for them. The agent's
4490
+ * provider acknowledges a withdrawal and *then* queues the on-chain transfer
4491
+ * to the EOA, so `withdraw()` resolving means "accepted", not "arrived".
4492
+ * Quoting before the tokens land would size the swap against a balance that
4493
+ * is not there yet.
4494
+ *
4495
+ * The swap is therefore sized from the MEASURED arrival, exactly as the
4496
+ * deposit path sizes its deposit from the measured swap output. On a full
4497
+ * withdrawal there is no other number available — "MAX" has no figure until
4498
+ * the agent picks one.
4499
+ *
4500
+ * **Failure here is not symmetrical with the deposit path.** A failed
4501
+ * deposit-swap leaves the user holding what they started with. A failed
4502
+ * withdrawal-swap leaves them holding the AGENT'S asset in their own wallet:
4503
+ * the money is out, safe, and in the wrong denomination. Both
4504
+ * `WITHDRAW_ARRIVAL_TIMEOUT` and `WITHDRAW_SWAP_FAILED` carry `withdrawn` for
4505
+ * that reason — the UI has to tell the user where their money actually is,
4506
+ * and must never present either as a lost withdrawal.
4507
+ */
4508
+ async withdrawAndSwap(options) {
4509
+ const state = this.requireState();
4510
+ const activeChainId = this.requireChainId();
4511
+ if (options.from.chainId !== activeChainId) {
4512
+ throw new OwneyError(
4513
+ "CHAIN_MISMATCH",
4514
+ `Cannot withdraw from chain ${options.from.chainId} while the active chain is ${activeChainId}. Activate on that chain first.`,
4515
+ { requested: options.from.chainId, active: activeChainId }
4516
+ );
4517
+ }
4518
+ const asset = SupportedAssets.find(
4519
+ (a) => a.chainId === options.from.chainId && a.symbol === options.from.symbol.toUpperCase()
4520
+ );
4521
+ if (!asset) {
4522
+ throw new OwneyError(
4523
+ "WITHDRAW_NO_PERMITTED_TOKENS",
4524
+ `${options.from.symbol} on chain ${options.from.chainId} is not an asset Owney holds`,
4525
+ { ...options.from }
4526
+ );
4527
+ }
4528
+ const srcChain = VIEM_CHAIN2[options.from.chainId];
4529
+ const srcPublic = createPublicClient2({
4530
+ chain: srcChain,
4531
+ transport: swapReadTransport(options.from.chainId, this.zyfaiRpcUrls)
4532
+ });
4533
+ const readWalletBalance = () => srcPublic.readContract({
4534
+ address: asset.address,
4535
+ abi: erc20Abi2,
4536
+ functionName: "balanceOf",
4537
+ args: [state.walletAddress]
4538
+ });
4539
+ const baseline = await readWalletBalance();
4540
+ debugLog("owney-sdk", "withdrawAndSwap: baseline", {
4541
+ asset: `${asset.symbol}@${asset.chainId}`,
4542
+ baseline: baseline.toString()
4543
+ });
4544
+ options.onSwapProgress?.("withdrawing");
4545
+ const withdraw = await this.withdraw({
4546
+ asset: options.from.symbol,
4547
+ ...options.amount === void 0 ? {} : { amount: options.amount },
4548
+ ...options.agentId ? { agentId: options.agentId } : {}
4549
+ });
4550
+ const arrived = await awaitWithdrawalArrival({
4551
+ readBalance: readWalletBalance,
4552
+ baseline,
4553
+ ...options.arrivalTimeoutMs === void 0 ? {} : { timeoutMs: options.arrivalTimeoutMs }
4554
+ });
4555
+ const withdrawn = arrived.toString();
4556
+ options.onSwapProgress?.("withdrawn");
4557
+ try {
4558
+ const quote = await this.swapApi().quote({
4559
+ from: { ...options.from, amount: withdrawn },
4560
+ to: options.to,
4561
+ direction: "withdraw",
4562
+ walletAddress: state.walletAddress
4563
+ });
4564
+ await this.ensureSwapChain(quote.src.chainId);
4565
+ const swap = await executeSwap(this.buildSwapDeps(quote), {
4566
+ quote,
4567
+ walletAddress: state.walletAddress,
4568
+ direction: "withdraw",
4569
+ ...options.slippage === void 0 ? {} : { slippage: options.slippage },
4570
+ ...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
4571
+ });
4572
+ return { withdraw, withdrawn, swap };
4573
+ } catch (error) {
4574
+ throw new OwneyError(
4575
+ "WITHDRAW_SWAP_FAILED",
4576
+ `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}.`,
4577
+ {
4578
+ withdrawn,
4579
+ asset: asset.symbol,
4580
+ chainId: asset.chainId,
4581
+ intendedSymbol: options.to.symbol,
4582
+ intendedChainId: options.to.chainId,
4583
+ cause: error instanceof Error ? error.message : String(error),
4584
+ ...error instanceof OwneyError ? { causeCode: error.code } : {}
4585
+ }
4586
+ );
4587
+ }
4588
+ }
5595
4589
  /**
5596
4590
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
5597
4591
  * Validates that the asset is supported by the target agent(s) on the active chain.
@@ -5661,10 +4655,6 @@ var OwneySDK = class {
5661
4655
  }
5662
4656
  const requested = BigInt(amount);
5663
4657
  const aggregated = await this.getBalances();
5664
- const unavailableAgents = eligibleAgents.filter(
5665
- (agent) => !(agent.id in aggregated.agentBalances)
5666
- );
5667
- const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
5668
4658
  const balances = projectAgentBalancesForAsset(
5669
4659
  eligibleAgents,
5670
4660
  aggregated.agentBalances,
@@ -5673,18 +4663,7 @@ var OwneySDK = class {
5673
4663
  assetInfo.decimals
5674
4664
  );
5675
4665
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
5676
- if (totalAvailable === 0n && unavailableAgents.length > 0) {
5677
- throw new OwneyError(
5678
- "WITHDRAW_BALANCE_UNAVAILABLE",
5679
- `Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
5680
- {
5681
- asset,
5682
- unavailableAgents: unavailableAgentIds,
5683
- agentErrors: aggregated.agentErrors
5684
- }
5685
- );
5686
- }
5687
- if (totalAvailable < requested && unavailableAgents.length === 0) {
4666
+ if (totalAvailable < requested) {
5688
4667
  throw new OwneyError(
5689
4668
  "WITHDRAW_INSUFFICIENT_BALANCE",
5690
4669
  `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
@@ -5695,7 +4674,6 @@ var OwneySDK = class {
5695
4674
  }
5696
4675
  );
5697
4676
  }
5698
- const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
5699
4677
  const disabledBalances = balances.filter(
5700
4678
  (b) => this.isAgentDisabled(b.agent.id)
5701
4679
  );
@@ -5704,7 +4682,7 @@ var OwneySDK = class {
5704
4682
  );
5705
4683
  const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
5706
4684
  disabledBalances,
5707
- plannedTarget
4685
+ requested
5708
4686
  );
5709
4687
  const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
5710
4688
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
@@ -5714,9 +4692,7 @@ var OwneySDK = class {
5714
4692
  }));
5715
4693
  const plans = [...disabledPlans, ...enabledPlans];
5716
4694
  const results = {};
5717
- const agentErrors = {
5718
- ...aggregated.agentErrors ?? {}
5719
- };
4695
+ const agentErrors = {};
5720
4696
  for (let i = 0; i < plans.length; i++) {
5721
4697
  const p = plans[i];
5722
4698
  if (p.planned === 0n) continue;
@@ -5763,8 +4739,7 @@ var OwneySDK = class {
5763
4739
  requested: amount,
5764
4740
  withdrawn: withdrawn.toString(),
5765
4741
  partialResults: results,
5766
- agentErrors,
5767
- ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
4742
+ agentErrors
5768
4743
  }
5769
4744
  );
5770
4745
  }
@@ -5781,25 +4756,24 @@ var OwneySDK = class {
5781
4756
  const chainId = this.requireChainId();
5782
4757
  if (agentId) {
5783
4758
  const agent = this.getAgent(agentId);
5784
- const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5785
- return {
5786
- ...result,
5787
- balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
5788
- };
4759
+ const result = await this.readAgent(
4760
+ agent,
4761
+ "balances",
4762
+ () => agent.getBalances(state, chainId)
4763
+ );
4764
+ return result;
5789
4765
  }
5790
4766
  let totalBalance = 0;
5791
4767
  const results = {};
5792
4768
  const entries = [...this.getActiveAgents().entries()];
5793
4769
  const balanceResults = await Promise.allSettled(
5794
4770
  entries.map(async ([id, agent]) => {
5795
- const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5796
- return [
5797
- id,
5798
- {
5799
- ...b,
5800
- balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
5801
- }
5802
- ];
4771
+ const b = await this.readAgent(
4772
+ agent,
4773
+ "balances",
4774
+ () => agent.getBalances(state, chainId)
4775
+ );
4776
+ return [id, b];
5803
4777
  })
5804
4778
  );
5805
4779
  let successCount = 0;
@@ -5819,9 +4793,9 @@ var OwneySDK = class {
5819
4793
  const reason = settledResult.reason;
5820
4794
  agentFailures.push(reason);
5821
4795
  const retryDelay = rateLimitDelay(reason);
5822
- if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
4796
+ if (retryDelay !== void 0)
4797
+ agentRetryAt[agentId2] = Date.now() + retryDelay;
5823
4798
  agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
5824
- console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
5825
4799
  }
5826
4800
  if (successCount === 0) {
5827
4801
  throw new OwneyError(
@@ -5847,14 +4821,22 @@ var OwneySDK = class {
5847
4821
  const chainId = this.requireChainId();
5848
4822
  if (agentId) {
5849
4823
  const agent = this.getAgent(agentId);
5850
- return this.readAgent(agent, "earnings", () => agent.getEarnings(state, chainId));
4824
+ return this.readAgent(
4825
+ agent,
4826
+ "earnings",
4827
+ () => agent.getEarnings(state, chainId)
4828
+ );
5851
4829
  }
5852
4830
  let totalEarnings = 0;
5853
4831
  const results = {};
5854
4832
  const entries = [...this.getActiveAgents().entries()];
5855
4833
  const earningsResults = await Promise.all(
5856
4834
  entries.map(async ([id, agent]) => {
5857
- const e = await this.readAgent(agent, "earnings", () => agent.getEarnings(state, chainId));
4835
+ const e = await this.readAgent(
4836
+ agent,
4837
+ "earnings",
4838
+ () => agent.getEarnings(state, chainId)
4839
+ );
5858
4840
  return [id, e];
5859
4841
  })
5860
4842
  );
@@ -5999,11 +4981,12 @@ var OwneySDK = class {
5999
4981
  ),
6000
4982
  Promise.all(
6001
4983
  entries.map(async ([id, agent]) => {
6002
- const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
6003
- return [
6004
- id,
6005
- balanceForApyScope(b, chainId, tokenSymbol)
6006
- ];
4984
+ const b = await this.readAgent(
4985
+ agent,
4986
+ "balances",
4987
+ () => agent.getBalances(state, chainId)
4988
+ );
4989
+ return [id, balanceForApyScope(b, chainId, tokenSymbol)];
6007
4990
  })
6008
4991
  )
6009
4992
  ]);
@@ -6062,7 +5045,12 @@ var OwneySDK = class {
6062
5045
  const { agentId, filters } = options ?? {};
6063
5046
  if (agentId) {
6064
5047
  const agent = this.getAgent(agentId);
6065
- return this.readAgent(agent, "history", () => agent.getHistory(state, chainId, filters), filters);
5048
+ return this.readAgent(
5049
+ agent,
5050
+ "history",
5051
+ () => agent.getHistory(state, chainId, filters),
5052
+ filters
5053
+ );
6066
5054
  }
6067
5055
  const activeAgents = [...this.getActiveAgents().values()];
6068
5056
  const cursorMap = filters?.cursor ? decodeMultiAgentCursor(filters.cursor) : {};
@@ -6119,13 +5107,21 @@ var OwneySDK = class {
6119
5107
  const chainId = this.requireChainId();
6120
5108
  if (agentId) {
6121
5109
  const agent = this.getAgent(agentId);
6122
- return this.readAgent(agent, "profile", () => agent.getUserProfile(state, chainId));
5110
+ return this.readAgent(
5111
+ agent,
5112
+ "profile",
5113
+ () => agent.getUserProfile(state, chainId)
5114
+ );
6123
5115
  }
6124
5116
  const results = {};
6125
5117
  const entries = [...this.getActiveAgents().entries()];
6126
5118
  const profileResults = await Promise.all(
6127
5119
  entries.map(async ([id, agent]) => {
6128
- const p = await this.readAgent(agent, "profile", () => agent.getUserProfile(state, chainId));
5120
+ const p = await this.readAgent(
5121
+ agent,
5122
+ "profile",
5123
+ () => agent.getUserProfile(state, chainId)
5124
+ );
6129
5125
  return [id, p];
6130
5126
  })
6131
5127
  );
@@ -6164,47 +5160,43 @@ var OwneySDK = class {
6164
5160
  return pending;
6165
5161
  }
6166
5162
  /**
6167
- * User-paid approval of Permit2 on the selected token for the active chain.
6168
- * Grants the maximum ERC20 allowance so later deposits do not require another
6169
- * approval. Resolves after one confirmation so the subsequent deposit attempt
6170
- * sees the new allowance. Deposit retries pass their captured chain id so a
6171
- * concurrent activation cannot redirect the approval to another network.
6172
- *
6173
- * @param requiredAmount Raw base-unit amount the pending deposit must cover.
6174
- * @param expectedChainId Chain captured by the deposit that requested approval.
5163
+ * One-time, user-paid approval of Permit2 on the sponsored WETH token for
5164
+ * the active chain. Required once per wallet per chain before gasless WETH
5165
+ * deposits; afterwards deposit() is signature-only. Resolves only after the
5166
+ * approval transaction is mined (1 confirmation), so a subsequent deposit()
5167
+ * will see the new allowance; throws if the transaction reverted.
6175
5168
  * @returns the approval transaction hash.
6176
5169
  */
6177
- async approvePermit2(asset = "WETH", requiredAmount = 0n, expectedChainId) {
5170
+ async approvePermit2(asset = "WETH") {
5171
+ void asset;
6178
5172
  const state = this.requireState();
6179
- const chainId = expectedChainId ?? this.requireChainId();
6180
- this.getEligibleAgents(chainId, asset, { excludeDisabled: true });
6181
- const token = sponsoredTokensFor(asset)[chainId];
5173
+ const chainId = this.requireChainId();
5174
+ this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
5175
+ const token = SPONSORED_WETH_BY_CHAIN[chainId];
6182
5176
  if (!token) {
6183
5177
  throw new OwneyError(
6184
5178
  "CHAIN_UNSUPPORTED",
6185
- `No sponsored token on chain ${chainId}`
5179
+ `No sponsored WETH on chain ${chainId}`
6186
5180
  );
6187
5181
  }
6188
5182
  const provider = this.requireConnectedProvider();
6189
- const publicClient = createPublicClient4({
6190
- chain: VIEM_CHAIN2[chainId],
6191
- transport: custom3(provider)
6192
- });
6193
- const approvalAmount = permit2ApprovalAmount(requiredAmount);
6194
- const wallet = createWalletClient3({
5183
+ const wallet = createWalletClient({
6195
5184
  account: state.walletAddress,
6196
5185
  chain: VIEM_CHAIN2[chainId],
6197
- transport: custom3(provider)
5186
+ transport: custom(provider)
6198
5187
  });
6199
- await ensureWalletOnChain(publicClient, wallet, chainId);
6200
5188
  const hash = await wallet.writeContract({
6201
5189
  address: token,
6202
5190
  abi: ERC20_ALLOWANCE_ABI,
6203
5191
  functionName: "approve",
6204
- args: [PERMIT2_ADDRESS, approvalAmount],
5192
+ args: [PERMIT2_ADDRESS, MAX_UINT256],
6205
5193
  account: state.walletAddress,
6206
5194
  chain: VIEM_CHAIN2[chainId]
6207
5195
  });
5196
+ const publicClient = createPublicClient2({
5197
+ chain: VIEM_CHAIN2[chainId],
5198
+ transport: custom(provider)
5199
+ });
6208
5200
  const receipt = await publicClient.waitForTransactionReceipt({
6209
5201
  hash,
6210
5202
  confirmations: 1
@@ -6212,42 +5204,7 @@ var OwneySDK = class {
6212
5204
  if (receipt.status !== "success") {
6213
5205
  throw new Error(`Permit2 approval reverted (tx ${hash})`);
6214
5206
  }
6215
- let observedAllowance = 0n;
6216
- let verificationError;
6217
- for (let attempt = 0; attempt < PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS; attempt += 1) {
6218
- try {
6219
- observedAllowance = await readPermit2Allowance(
6220
- publicClient,
6221
- token,
6222
- state.walletAddress,
6223
- attempt === 0 ? receipt.blockNumber : void 0
6224
- );
6225
- verificationError = void 0;
6226
- if (observedAllowance >= requiredAmount) return hash;
6227
- } catch (error) {
6228
- verificationError = error;
6229
- }
6230
- if (attempt + 1 < PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS) {
6231
- await new Promise(
6232
- (resolve) => setTimeout(resolve, PERMIT2_ALLOWANCE_VERIFY_DELAY_MS)
6233
- );
6234
- }
6235
- }
6236
- throw new OwneyError(
6237
- "PERMIT2_APPROVAL_REQUIRED",
6238
- "Permit2 approval was confirmed, but the required token allowance was not observable.",
6239
- {
6240
- approvalConfirmed: true,
6241
- approvalTxHash: hash,
6242
- owner: state.walletAddress,
6243
- token,
6244
- spender: PERMIT2_ADDRESS,
6245
- chainId,
6246
- requiredAmount: requiredAmount.toString(),
6247
- observedAllowance: observedAllowance.toString(),
6248
- ...verificationError instanceof Error ? { verificationError: verificationError.message } : {}
6249
- }
6250
- );
5207
+ return hash;
6251
5208
  }
6252
5209
  // --- Discovery (no wallet required) ---
6253
5210
  /**
@@ -6272,15 +5229,23 @@ var OwneySDK = class {
6272
5229
  const agentOptions = { tokenSymbol, chainId };
6273
5230
  if (agentId) {
6274
5231
  const agent = this.getAgent(agentId);
6275
- return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
5232
+ return this.readAgent(
5233
+ agent,
5234
+ "agentApy",
5235
+ () => agent.getAgentApy(days, agentOptions),
5236
+ { days, ...agentOptions }
5237
+ );
6276
5238
  }
6277
5239
  const results = {};
6278
- const agentEntries = [...this.agents.entries()].filter(
6279
- ([id]) => !this.isAgentDisabled(id)
6280
- );
5240
+ const agentEntries = [...this.agents.entries()];
6281
5241
  const apyResults = await Promise.all(
6282
5242
  agentEntries.map(async ([id, agent]) => {
6283
- const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
5243
+ const apy = await this.readAgent(
5244
+ agent,
5245
+ "agentApy",
5246
+ () => agent.getAgentApy(days, agentOptions),
5247
+ { days, ...agentOptions }
5248
+ );
6284
5249
  return [id, apy];
6285
5250
  })
6286
5251
  );
@@ -6307,7 +5272,11 @@ var OwneySDK = class {
6307
5272
  const entries = [...activeAgents.entries()];
6308
5273
  const balanceResults = await Promise.allSettled(
6309
5274
  entries.map(async ([id, agent]) => {
6310
- const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
5275
+ const b = await this.readAgent(
5276
+ agent,
5277
+ "balances",
5278
+ () => agent.getBalances(state, chainId)
5279
+ );
6311
5280
  return [id, b.positions ?? []];
6312
5281
  })
6313
5282
  );
@@ -6352,13 +5321,13 @@ var OwneySDK = class {
6352
5321
  };
6353
5322
 
6354
5323
  // src/agents/zyfai/zyfai.siwx.ts
6355
- import { getAddress as getAddress3 } from "viem";
6356
- import { SiweMessage as SiweMessage2 } from "siwe";
5324
+ import { getAddress } from "viem";
5325
+ import { SiweMessage } from "siwe";
6357
5326
  import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
6358
5327
 
6359
5328
  // src/agents/zyfai/zyfai.siwx-cache.ts
6360
- var KEY_PREFIX4 = "owney.siwx.session";
6361
- var storage4 = () => {
5329
+ var KEY_PREFIX3 = "owney.siwx.session";
5330
+ var storage3 = () => {
6362
5331
  if (typeof window === "undefined") return null;
6363
5332
  try {
6364
5333
  return window.localStorage;
@@ -6366,8 +5335,8 @@ var storage4 = () => {
6366
5335
  return null;
6367
5336
  }
6368
5337
  };
6369
- var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
6370
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
5338
+ var buildKey2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
5339
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
6371
5340
  var memorySiwxSessions = /* @__PURE__ */ new Map();
6372
5341
  var readLegacySiwxSession = (store, address) => {
6373
5342
  if (!store) return null;
@@ -6398,17 +5367,17 @@ var readLegacySiwxSession = (store, address) => {
6398
5367
  };
6399
5368
  var readSiwxSession = (address, chainId) => {
6400
5369
  if (typeof window === "undefined") return null;
6401
- const key2 = buildKey3(address);
6402
- const store = storage4();
6403
- let raw2 = null;
5370
+ const key2 = buildKey2(address);
5371
+ const store = storage3();
5372
+ let raw = null;
6404
5373
  try {
6405
- raw2 = store?.getItem(key2) ?? null;
5374
+ raw = store?.getItem(key2) ?? null;
6406
5375
  } catch {
6407
- raw2 = null;
5376
+ raw = null;
6408
5377
  }
6409
- if (raw2) {
5378
+ if (raw) {
6410
5379
  try {
6411
- return JSON.parse(raw2);
5380
+ return JSON.parse(raw);
6412
5381
  } catch {
6413
5382
  memorySiwxSessions.delete(key2);
6414
5383
  try {
@@ -6427,18 +5396,18 @@ var readSiwxSession = (address, chainId) => {
6427
5396
  };
6428
5397
  var writeSiwxSession = (address, _chainId, session) => {
6429
5398
  if (typeof window === "undefined") return;
6430
- const key2 = buildKey3(address);
5399
+ const key2 = buildKey2(address);
6431
5400
  memorySiwxSessions.set(key2, session);
6432
- const store = storage4();
5401
+ const store = storage3();
6433
5402
  try {
6434
5403
  store?.setItem(key2, JSON.stringify(session));
6435
5404
  } catch {
6436
5405
  }
6437
5406
  };
6438
5407
  var clearSiwxSession = (address, _chainId) => {
6439
- const key2 = buildKey3(address);
5408
+ const key2 = buildKey2(address);
6440
5409
  memorySiwxSessions.delete(key2);
6441
- const store = storage4();
5410
+ const store = storage3();
6442
5411
  try {
6443
5412
  store?.removeItem(key2);
6444
5413
  } catch {
@@ -6478,8 +5447,8 @@ function buildSIWXConfig(deps) {
6478
5447
  statement: STATEMENT,
6479
5448
  issuedAt,
6480
5449
  toString() {
6481
- return new SiweMessage2({
6482
- address: getAddress3(accountAddress),
5450
+ return new SiweMessage({
5451
+ address: getAddress(accountAddress),
6483
5452
  chainId: numericChainId(chainId),
6484
5453
  domain,
6485
5454
  uri,
@@ -6521,7 +5490,7 @@ function buildSIWXConfig(deps) {
6521
5490
  const persistSession = async (session) => {
6522
5491
  const address = session.data.accountAddress;
6523
5492
  const id = numericChainId(session.data.chainId);
6524
- const message = new SiweMessage2(session.message);
5493
+ const message = new SiweMessage(session.message);
6525
5494
  const login = await post("/auth/login", {
6526
5495
  message,
6527
5496
  signature: session.signature,
@@ -6557,9 +5526,9 @@ function buildSIWXConfig(deps) {
6557
5526
  }
6558
5527
  function createOwneySIWX(config) {
6559
5528
  const zyfai = new ZyfaiSDK2({ apiKey: config.apiKey });
6560
- const http2 = zyfai.httpClient;
5529
+ const http4 = zyfai.httpClient;
6561
5530
  return buildSIWXConfig({
6562
- post: (url, data) => http2.post(url, data),
5531
+ post: (url, data) => http4.post(url, data),
6563
5532
  referralSource: config.referralSource
6564
5533
  });
6565
5534
  }
@@ -6570,7 +5539,7 @@ export {
6570
5539
  NotConnectedError,
6571
5540
  OwneyError,
6572
5541
  OwneySDK,
6573
- YieldseekerAgent,
6574
5542
  createOwneySIWX,
5543
+ listOrders as listPendingSwaps,
6575
5544
  setOwneyDebug
6576
5545
  };