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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -26,6 +26,7 @@ __export(index_exports, {
26
26
  NotConnectedError: () => NotConnectedError,
27
27
  OwneyError: () => OwneyError,
28
28
  OwneySDK: () => OwneySDK,
29
+ YieldseekerAgent: () => YieldseekerAgent,
29
30
  createOwneySIWX: () => createOwneySIWX,
30
31
  setOwneyDebug: () => setOwneyDebug
31
32
  });
@@ -83,23 +84,6 @@ var SupportedAssets = [
83
84
  }
84
85
  ];
85
86
 
86
- // src/lib/debug.ts
87
- var configuredDebug = false;
88
- function setOwneyDebug(enabled) {
89
- configuredDebug = enabled;
90
- }
91
- function isOwneyDebug() {
92
- return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
93
- }
94
- function debugLog(scope, message, data) {
95
- if (!isOwneyDebug()) return;
96
- if (data === void 0) {
97
- console.log(`[${scope}] ${message}`);
98
- } else {
99
- console.log(`[${scope}] ${message}`, data);
100
- }
101
- }
102
-
103
87
  // src/errors.ts
104
88
  var OwneyError = class extends Error {
105
89
  code;
@@ -299,7 +283,7 @@ function mapBalances(raw, _chainId, smartWallet) {
299
283
  )
300
284
  )
301
285
  ),
302
- apy: netApy(p.pool_apy_withFee, p.pool_apy, "pool_apy_withFee"),
286
+ apy: p.pool_apy,
303
287
  tvl: p.pool_tvl,
304
288
  // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
305
289
  // structural read keeps it undefined today and auto-populates later.
@@ -356,27 +340,14 @@ function mapWeightedApyByChain(raw) {
356
340
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
357
341
  const perAsset = {};
358
342
  for (const [symbol, apy] of Object.entries(tokenApy)) {
359
- const token = symbol;
360
- if (!SUPPORTED_TOKENS.includes(token)) continue;
361
- perAsset[token] = apy;
343
+ const token2 = symbol;
344
+ if (!SUPPORTED_TOKENS.includes(token2)) continue;
345
+ perAsset[token2] = apy;
362
346
  }
363
347
  if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
364
348
  }
365
349
  return Object.keys(out).length > 0 ? out : void 0;
366
350
  }
367
- var warnedGrossApyFallbacks = /* @__PURE__ */ new Set();
368
- function netApy(net, gross, field) {
369
- if (net !== void 0 && net !== null) return net;
370
- if (gross === void 0 || gross === null) return void 0;
371
- if (!warnedGrossApyFallbacks.has(field)) {
372
- warnedGrossApyFallbacks.add(field);
373
- console.warn(
374
- `[owney] @zyfai/sdk omitted "${field}"; falling back to the gross APY, which does not deduct Zyfai's performance fee and so reads high.`
375
- );
376
- }
377
- debugLog("zyfai:apy", `gross fallback for ${field}`, { gross });
378
- return gross;
379
- }
380
351
  function rawPoolApyForChain(entry, chainId, tokenSymbol) {
381
352
  let weightedSum = 0;
382
353
  let totalBalance = 0;
@@ -384,9 +355,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
384
355
  if (p.chainId !== chainId) continue;
385
356
  if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
386
357
  const balance = p.balance ?? 0;
387
- const apy = netApy(p.apy_withFee, p.apy, "apy_withFee");
388
- if (balance <= 0 || typeof apy !== "number") continue;
389
- weightedSum += apy * balance;
358
+ if (balance <= 0 || typeof p.apy !== "number") continue;
359
+ weightedSum += p.apy * balance;
390
360
  totalBalance += balance;
391
361
  }
392
362
  return totalBalance > 0 ? weightedSum / totalBalance : null;
@@ -434,12 +404,8 @@ function mapEntries(rawEntries, chainId) {
434
404
  const matched = positions.find(
435
405
  (p) => p.protocol_name ? rawLog.newOpportunity.includes(`${p.protocol_name} (${p.pool})`) : false
436
406
  );
437
- oldApy = Number(
438
- netApy(rawLog.oldApy_withFee, rawLog.oldApy, "oldApy_withFee")
439
- );
440
- newApy = Number(
441
- netApy(rawLog.newApy_withFee, rawLog.newApy, "newApy_withFee")
442
- );
407
+ oldApy = Number(rawLog.oldApy);
408
+ newApy = Number(rawLog.newApy);
443
409
  const from = splitOpportunity(rawLog.oldOpportunity);
444
410
  const to = splitOpportunity(rawLog.newOpportunity);
445
411
  rebalanceLog = [
@@ -519,11 +485,7 @@ function mapApyByStrategy(raw) {
519
485
  const chainKey = supported.chainId;
520
486
  const symbolKey = supported.symbol;
521
487
  const bucket = apyPerAsset[chainKey] ?? {};
522
- const apy = netApy(
523
- entry.average_apy_withFee,
524
- entry.average_apy,
525
- "average_apy_withFee"
526
- ) ?? entry.average_apy;
488
+ const apy = entry.average_apy_with_fee ?? entry.average_apy;
527
489
  bucket[symbolKey] = apy;
528
490
  apyPerAsset[chainKey] = bucket;
529
491
  apySum += apy;
@@ -549,23 +511,23 @@ function computeAllocationApy(positions) {
549
511
  weightedSum += apy * value;
550
512
  totalValue += value;
551
513
  const chainId = resolveChainId(p.chain);
552
- const token = p.asset;
514
+ const token2 = p.asset;
553
515
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
554
- if (!SUPPORTED_TOKENS.includes(token)) continue;
516
+ if (!SUPPORTED_TOKENS.includes(token2)) continue;
555
517
  const perAsset = buckets[chainId] ?? {};
556
- const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
518
+ const cell = perAsset[token2] ?? { weightedSum: 0, value: 0 };
557
519
  cell.weightedSum += apy * value;
558
520
  cell.value += value;
559
- perAsset[token] = cell;
521
+ perAsset[token2] = cell;
560
522
  buckets[chainId] = perAsset;
561
523
  }
562
524
  const apyByChainAndAsset = {};
563
525
  for (const [chainKey, perAsset] of Object.entries(buckets)) {
564
526
  const chainId = Number(chainKey);
565
527
  const out = {};
566
- for (const [token, cell] of Object.entries(perAsset ?? {})) {
528
+ for (const [token2, cell] of Object.entries(perAsset ?? {})) {
567
529
  if (cell && cell.value > 0) {
568
- out[token] = cell.weightedSum / cell.value;
530
+ out[token2] = cell.weightedSum / cell.value;
569
531
  }
570
532
  }
571
533
  if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
@@ -579,9 +541,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
579
541
  function topicToAddress(topic) {
580
542
  return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
581
543
  }
582
- function extractWithdrawnAmount(logs, recipient, token, decimals) {
544
+ function extractWithdrawnAmount(logs, recipient, token2, decimals) {
583
545
  const wantRecipient = recipient.toLowerCase();
584
- const wantToken = token.toLowerCase();
546
+ const wantToken = token2.toLowerCase();
585
547
  let total = 0n;
586
548
  let matched = false;
587
549
  for (const log of logs) {
@@ -607,11 +569,11 @@ var InvalidHistoryCursorError = class extends Error {
607
569
  function encodeHistoryCursor(payload) {
608
570
  return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
609
571
  }
610
- function decodeHistoryCursor(token) {
611
- if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
572
+ function decodeHistoryCursor(token2) {
573
+ if (!token2) throw new InvalidHistoryCursorError("Cursor is empty.");
612
574
  let parsed;
613
575
  try {
614
- const json = Buffer.from(token, "base64").toString("utf8");
576
+ const json = Buffer.from(token2, "base64").toString("utf8");
615
577
  parsed = JSON.parse(json);
616
578
  } catch {
617
579
  throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
@@ -634,8 +596,8 @@ var storage = () => {
634
596
  };
635
597
  var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
636
598
  var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
637
- var isJwtExpired = (token) => {
638
- const parts = token.split(".");
599
+ var isJwtExpired = (token2) => {
600
+ const parts = token2.split(".");
639
601
  if (parts.length !== 3) return false;
640
602
  try {
641
603
  const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
@@ -883,6 +845,23 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
883
845
  return !protocolListsEqual(current.protocols, desiredProtocols);
884
846
  }
885
847
 
848
+ // src/lib/debug.ts
849
+ var configuredDebug = false;
850
+ function setOwneyDebug(enabled) {
851
+ configuredDebug = enabled;
852
+ }
853
+ function isOwneyDebug() {
854
+ return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
855
+ }
856
+ function debugLog(scope, message, data) {
857
+ if (!isOwneyDebug()) return;
858
+ if (data === void 0) {
859
+ console.log(`[${scope}] ${message}`);
860
+ } else {
861
+ console.log(`[${scope}] ${message}`, data);
862
+ }
863
+ }
864
+
886
865
  // src/agents/zyfai/zyfai.agent.ts
887
866
  var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem.parseAbi)([
888
867
  "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
@@ -1720,20 +1699,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
1720
1699
  }
1721
1700
  }
1722
1701
  // --- IAgent: Fund operations ---
1723
- async withdraw(state, chainId, token, amount) {
1702
+ async withdraw(state, chainId, token2, amount) {
1724
1703
  const validChainId = isValidChainId(chainId);
1725
1704
  await this.ensureConnected(state, validChainId);
1726
1705
  const raw = await this.sdk.withdrawFunds(
1727
1706
  this.getAddress(),
1728
1707
  validChainId,
1729
1708
  amount,
1730
- token
1709
+ token2
1731
1710
  );
1732
1711
  if (!raw.success) {
1733
1712
  throw new OwneyError(
1734
1713
  "WITHDRAW_FAILED",
1735
1714
  raw.message || "Zyfai withdraw failed.",
1736
- { chainId: validChainId, token, amount, response: raw },
1715
+ { chainId: validChainId, token: token2, amount, response: raw },
1737
1716
  this.id
1738
1717
  );
1739
1718
  }
@@ -1884,345 +1863,169 @@ var ZyfaiAgent = class _ZyfaiAgent {
1884
1863
  }
1885
1864
  };
1886
1865
 
1887
- // src/lib/routing-api.ts
1888
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1889
- async function fetchOrgAgentConfig(apiKey) {
1890
- const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
1866
+ // src/agents/yieldseeker/yieldseeker.agent.ts
1867
+ var import_viem6 = require("viem");
1868
+ var import_chains3 = require("viem/chains");
1869
+
1870
+ // src/lib/chain-guard.ts
1871
+ var CHAIN_NAMES = {
1872
+ 1: "Ethereum",
1873
+ 8453: "Base",
1874
+ 42161: "Arbitrum"
1875
+ };
1876
+ function chainName(chainId) {
1877
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
1878
+ }
1879
+ async function ensureWalletOnChain(pub, wallet, expected) {
1880
+ const actual = await pub.getChainId();
1881
+ if (actual === expected) return;
1891
1882
  try {
1892
- const res = await fetch(url, {
1893
- method: "GET",
1894
- headers: {
1895
- "Content-Type": "application/json",
1896
- "x-owney-api-key": `${apiKey}`
1897
- }
1898
- });
1899
- if (!res.ok) {
1900
- if (res.status !== 404) {
1901
- console.warn(
1902
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
1903
- );
1883
+ await wallet.switchChain({ id: expected });
1884
+ } catch (error) {
1885
+ throw new OwneyError(
1886
+ "CHAIN_MISMATCH",
1887
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
1888
+ {
1889
+ expectedChainId: expected,
1890
+ actualChainId: actual,
1891
+ cause: error instanceof Error ? error.message : String(error)
1904
1892
  }
1905
- return null;
1906
- }
1907
- const json = await res.json();
1908
- const policy = json.success ? json.data ?? null : null;
1909
- debugLog(
1910
- "owney-sdk",
1911
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
1912
- policy ?? void 0
1913
1893
  );
1914
- return policy;
1915
- } catch (error) {
1916
- console.warn(
1917
- "[owney-sdk] Could not read org agent config (non-fatal):",
1918
- error instanceof Error ? error.message : String(error)
1894
+ }
1895
+ const after = await pub.getChainId();
1896
+ if (after !== expected) {
1897
+ throw new OwneyError(
1898
+ "CHAIN_MISMATCH",
1899
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
1900
+ { expectedChainId: expected, actualChainId: after }
1919
1901
  );
1920
- return null;
1921
1902
  }
1922
1903
  }
1923
- async function fetchAgentKeys(apiKey) {
1924
- const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
1925
- const res = await fetch(url, {
1926
- method: "GET",
1927
- headers: {
1928
- "Content-Type": "application/json",
1929
- "x-owney-api-key": `${apiKey}`
1930
- }
1931
- });
1932
- if (!res.ok) {
1933
- const text = await res.text().catch(() => "");
1904
+
1905
+ // src/lib/transfer-auth.ts
1906
+ var import_viem2 = require("viem");
1907
+ var ERC20_META_ABI = [
1908
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
1909
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
1910
+ ];
1911
+ function buildTransferWithAuthorizationTypedData(input) {
1912
+ return {
1913
+ domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
1914
+ types: {
1915
+ TransferWithAuthorization: [
1916
+ { name: "from", type: "address" },
1917
+ { name: "to", type: "address" },
1918
+ { name: "value", type: "uint256" },
1919
+ { name: "validAfter", type: "uint256" },
1920
+ { name: "validBefore", type: "uint256" },
1921
+ { name: "nonce", type: "bytes32" }
1922
+ ]
1923
+ },
1924
+ primaryType: "TransferWithAuthorization",
1925
+ message: input.message
1926
+ };
1927
+ }
1928
+ async function readTokenMeta(publicClient, token2) {
1929
+ const [tokenName, tokenVersion] = await Promise.all([
1930
+ publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "name" }),
1931
+ publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1932
+ ]);
1933
+ return { tokenName, tokenVersion };
1934
+ }
1935
+ function randomAuthNonce() {
1936
+ const bytes = new Uint8Array(32);
1937
+ globalThis.crypto.getRandomValues(bytes);
1938
+ return (0, import_viem2.bytesToHex)(bytes);
1939
+ }
1940
+
1941
+ // src/lib/sponsor-client.ts
1942
+ var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1943
+ async function postSponsorTransferAuth(input) {
1944
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1945
+ let res;
1946
+ try {
1947
+ res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
1948
+ method: "POST",
1949
+ headers: {
1950
+ "content-type": "application/json",
1951
+ "x-owney-api-key": input.apiKey,
1952
+ ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
1953
+ },
1954
+ body: JSON.stringify(input.body)
1955
+ });
1956
+ } catch (networkError) {
1934
1957
  throw new OwneyError(
1935
- "API_ROUTING_ERROR",
1936
- `Routing API error ${res.status}: ${text}`,
1937
- { statusCode: res.status, responseBody: text }
1958
+ "SPONSOR_REQUEST_FAILED",
1959
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1960
+ { cause: String(networkError) }
1938
1961
  );
1939
1962
  }
1940
- const json = await res.json();
1941
- if (!json.success) {
1963
+ const text = await res.text();
1964
+ let parsed = null;
1965
+ try {
1966
+ parsed = JSON.parse(text);
1967
+ } catch {
1968
+ }
1969
+ if (!res.ok || !parsed?.success || !parsed.data) {
1942
1970
  throw new OwneyError(
1943
- "API_ROUTING_FAILED",
1944
- `Routing API request failed: ${json.message}`,
1945
- { message: json.message }
1971
+ "SPONSOR_REQUEST_FAILED",
1972
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1973
+ {
1974
+ statusCode: res.status,
1975
+ responseBody: text.slice(0, 500),
1976
+ // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
1977
+ // before broadcast, so it is safe to fall back to a user-paid deposit.
1978
+ safeToFallback: res.status === 503
1979
+ }
1946
1980
  );
1947
1981
  }
1948
- return json.data;
1982
+ return parsed.data;
1949
1983
  }
1950
-
1951
- // src/lib/health-report.ts
1952
- var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1953
- async function reportAgentFailure(apiKey, agentType, errorCode) {
1984
+ async function postSponsorPermit2Transfer(input) {
1985
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1986
+ let res;
1954
1987
  try {
1955
- await fetch(`${ROUTING_API_BASE_URL2}/api/v1/agent/health-report`, {
1988
+ res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
1956
1989
  method: "POST",
1957
1990
  headers: {
1958
- "Content-Type": "application/json",
1959
- "x-owney-api-key": apiKey
1991
+ "content-type": "application/json",
1992
+ "x-owney-api-key": input.apiKey,
1993
+ ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
1960
1994
  },
1961
- body: JSON.stringify({
1962
- agent_type: agentType,
1963
- error_code: errorCode,
1964
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1965
- })
1995
+ body: JSON.stringify(input.body)
1966
1996
  });
1967
- } catch (err) {
1968
- console.warn(
1969
- `[owney-sdk] health-report failed for agent "${agentType}":`,
1970
- err instanceof Error ? err.message : err
1997
+ } catch (networkError) {
1998
+ throw new OwneyError(
1999
+ "SPONSOR_REQUEST_FAILED",
2000
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2001
+ { cause: String(networkError), safeToFallback: false }
1971
2002
  );
1972
2003
  }
1973
- }
1974
- async function withFailureReporting(apiKey, agentType, fn) {
2004
+ const text = await res.text();
2005
+ let parsed = null;
1975
2006
  try {
1976
- return await fn();
1977
- } catch (err) {
1978
- const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
1979
- void reportAgentFailure(apiKey, agentType, errorCode);
1980
- throw err;
2007
+ parsed = JSON.parse(text);
2008
+ } catch {
1981
2009
  }
1982
- }
1983
-
1984
- // src/lib/helpers/withdraw-helper.ts
1985
- var import_viem2 = require("viem");
1986
- function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1987
- const target = asset.toUpperCase();
1988
- return agents.map((agent) => {
1989
- const agentBalance = aggregated[agent.id];
1990
- const tokenBalance = agentBalance?.tokens.find(
1991
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
2010
+ if (!res.ok || !parsed?.success || !parsed.data) {
2011
+ throw new OwneyError(
2012
+ "SPONSOR_REQUEST_FAILED",
2013
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2014
+ {
2015
+ statusCode: res.status,
2016
+ responseBody: text.slice(0, 500),
2017
+ safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
2018
+ }
1992
2019
  );
1993
- if (!tokenBalance) return { agent, balance: 0n };
1994
- return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
1995
- });
1996
- }
1997
- function planProportionalShares(balances, requested, totalAvailable) {
1998
- const plans = balances.map(({ agent, balance }) => ({
1999
- agent,
2000
- balance,
2001
- planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
2002
- }));
2003
- const assigned = plans.reduce((s, p) => s + p.planned, 0n);
2004
- let remainder = requested - assigned;
2005
- const byHeadroom = [...plans].sort((a, b) => {
2006
- const diff = b.balance - b.planned - (a.balance - a.planned);
2007
- return diff > 0n ? 1 : diff < 0n ? -1 : 0;
2008
- });
2009
- for (const p of byHeadroom) {
2010
- if (remainder === 0n) break;
2011
- const headroom = p.balance - p.planned;
2012
- if (headroom <= 0n) continue;
2013
- const take = headroom < remainder ? headroom : remainder;
2014
- p.planned += take;
2015
- remainder -= take;
2016
2020
  }
2017
- return plans;
2018
- }
2019
- function planDisabledDrain(disabled, requested) {
2020
- const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
2021
- const plans = [];
2022
- let remaining = requested;
2023
- for (const { agent, balance } of sorted) {
2024
- if (remaining === 0n) {
2025
- plans.push({ agent, balance, planned: 0n });
2026
- continue;
2027
- }
2028
- const take = balance < remaining ? balance : remaining;
2029
- plans.push({ agent, balance, planned: take });
2030
- remaining -= take;
2031
- }
2032
- return { plans, remaining };
2033
- }
2034
- function redistributeShare(plans, fromIndex, amount, candidatePool) {
2035
- const pool = candidatePool ?? plans.slice(fromIndex + 1);
2036
- const candidates = pool.filter((c) => c.balance - c.planned > 0n);
2037
- const totalHeadroom = candidates.reduce(
2038
- (s, c) => s + (c.balance - c.planned),
2039
- 0n
2040
- );
2041
- if (totalHeadroom === 0n) return;
2042
- let distributed = 0n;
2043
- for (const c of candidates) {
2044
- const headroom = c.balance - c.planned;
2045
- const proportional = headroom * amount / totalHeadroom;
2046
- const give = proportional > headroom ? headroom : proportional;
2047
- c.planned += give;
2048
- distributed += give;
2049
- }
2050
- let leftover = amount - distributed;
2051
- for (const c of candidates) {
2052
- if (leftover === 0n) break;
2053
- const headroom = c.balance - c.planned;
2054
- if (headroom <= 0n) continue;
2055
- const take = headroom < leftover ? headroom : leftover;
2056
- c.planned += take;
2057
- leftover -= take;
2058
- }
2059
- }
2060
- function sumWithdrawnAmount(results) {
2061
- return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
2062
- }
2063
-
2064
- // src/lib/helpers/account-apy-helper.ts
2065
- function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2066
- const sums = {};
2067
- const weights = {};
2068
- for (const id of Object.keys(agentApys)) {
2069
- const cells = agentApys[id].apyByChainAndAsset;
2070
- const balance = agentBalances[id] ?? 0;
2071
- if (!cells || balance <= 0) continue;
2072
- for (const [chainKey, perAsset] of Object.entries(cells)) {
2073
- if (!perAsset) continue;
2074
- const chainId = Number(chainKey);
2075
- for (const [asset, apyValue] of Object.entries(perAsset)) {
2076
- const apy = Number(apyValue ?? 0);
2077
- if (apy === 0) continue;
2078
- sums[chainId] ??= {};
2079
- weights[chainId] ??= {};
2080
- sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
2081
- weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2082
- }
2083
- }
2084
- }
2085
- const out = {};
2086
- for (const chainKey of Object.keys(sums)) {
2087
- const chainId = Number(chainKey);
2088
- const perAssetOut = {};
2089
- for (const asset of Object.keys(sums[chainId])) {
2090
- const w = weights[chainId][asset];
2091
- if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
2092
- }
2093
- if (Object.keys(perAssetOut).length > 0) {
2094
- out[chainId] = perAssetOut;
2095
- }
2096
- }
2097
- return out;
2098
- }
2099
-
2100
- // src/client.ts
2101
- var import_viem6 = require("viem");
2102
- var import_chains2 = require("viem/chains");
2103
-
2104
- // src/lib/transfer-auth.ts
2105
- var import_viem3 = require("viem");
2106
- var ERC20_META_ABI = [
2107
- { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
2108
- { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
2109
- ];
2110
- function buildTransferWithAuthorizationTypedData(input) {
2111
- return {
2112
- domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
2113
- types: {
2114
- TransferWithAuthorization: [
2115
- { name: "from", type: "address" },
2116
- { name: "to", type: "address" },
2117
- { name: "value", type: "uint256" },
2118
- { name: "validAfter", type: "uint256" },
2119
- { name: "validBefore", type: "uint256" },
2120
- { name: "nonce", type: "bytes32" }
2121
- ]
2122
- },
2123
- primaryType: "TransferWithAuthorization",
2124
- message: input.message
2125
- };
2126
- }
2127
- async function readTokenMeta(publicClient, token) {
2128
- const [tokenName, tokenVersion] = await Promise.all([
2129
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
2130
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
2131
- ]);
2132
- return { tokenName, tokenVersion };
2133
- }
2134
- function randomAuthNonce() {
2135
- const bytes = new Uint8Array(32);
2136
- globalThis.crypto.getRandomValues(bytes);
2137
- return (0, import_viem3.bytesToHex)(bytes);
2138
- }
2139
-
2140
- // src/lib/sponsor-client.ts
2141
- var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2142
- async function postSponsorTransferAuth(input) {
2143
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2144
- let res;
2145
- try {
2146
- res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
2147
- method: "POST",
2148
- headers: {
2149
- "content-type": "application/json",
2150
- "x-owney-api-key": input.apiKey
2151
- },
2152
- body: JSON.stringify(input.body)
2153
- });
2154
- } catch (networkError) {
2155
- throw new OwneyError(
2156
- "SPONSOR_REQUEST_FAILED",
2157
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2158
- { cause: String(networkError) }
2159
- );
2160
- }
2161
- const text = await res.text();
2162
- let parsed = null;
2163
- try {
2164
- parsed = JSON.parse(text);
2165
- } catch {
2166
- }
2167
- if (!res.ok || !parsed?.success || !parsed.data) {
2168
- throw new OwneyError(
2169
- "SPONSOR_REQUEST_FAILED",
2170
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2171
- {
2172
- statusCode: res.status,
2173
- responseBody: text.slice(0, 500),
2174
- // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
2175
- // before broadcast, so it is safe to fall back to a user-paid deposit.
2176
- safeToFallback: res.status === 503
2177
- }
2178
- );
2179
- }
2180
- return parsed.data;
2181
- }
2182
- async function postSponsorPermit2Transfer(input) {
2183
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2184
- let res;
2185
- try {
2186
- res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
2187
- method: "POST",
2188
- headers: {
2189
- "content-type": "application/json",
2190
- "x-owney-api-key": input.apiKey
2191
- },
2192
- body: JSON.stringify(input.body)
2193
- });
2194
- } catch (networkError) {
2195
- throw new OwneyError(
2196
- "SPONSOR_REQUEST_FAILED",
2197
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2198
- { cause: String(networkError), safeToFallback: false }
2199
- );
2200
- }
2201
- const text = await res.text();
2202
- let parsed = null;
2203
- try {
2204
- parsed = JSON.parse(text);
2205
- } catch {
2206
- }
2207
- if (!res.ok || !parsed?.success || !parsed.data) {
2208
- throw new OwneyError(
2209
- "SPONSOR_REQUEST_FAILED",
2210
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2211
- {
2212
- statusCode: res.status,
2213
- responseBody: text.slice(0, 500),
2214
- safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
2215
- }
2216
- );
2217
- }
2218
- return parsed.data;
2021
+ return parsed.data;
2219
2022
  }
2220
2023
  async function getSponsorRelayerAddress(input) {
2221
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2024
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2222
2025
  let res;
2223
2026
  try {
2224
2027
  res = await fetch(
2225
- `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2028
+ `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2226
2029
  {
2227
2030
  headers: { "x-owney-api-key": input.apiKey }
2228
2031
  }
@@ -2255,7 +2058,7 @@ async function getSponsorRelayerAddress(input) {
2255
2058
  }
2256
2059
 
2257
2060
  // src/lib/permit2.ts
2258
- var import_viem4 = require("viem");
2061
+ var import_viem3 = require("viem");
2259
2062
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2260
2063
  var MAX_UINT256 = 2n ** 256n - 1n;
2261
2064
  var ERC20_ALLOWANCE_ABI = [
@@ -2313,223 +2116,1391 @@ function buildPermitTransferFromTypedData(input) {
2313
2116
  function randomPermit2Nonce() {
2314
2117
  const bytes = new Uint8Array(32);
2315
2118
  globalThis.crypto.getRandomValues(bytes);
2316
- return BigInt((0, import_viem4.bytesToHex)(bytes));
2119
+ return BigInt((0, import_viem3.bytesToHex)(bytes));
2317
2120
  }
2318
- async function readPermit2Allowance(publicClient, token, owner) {
2121
+ async function readPermit2Allowance(publicClient, token2, owner) {
2319
2122
  return publicClient.readContract({
2320
- address: token,
2123
+ address: token2,
2321
2124
  abi: ERC20_ALLOWANCE_ABI,
2322
2125
  functionName: "allowance",
2323
2126
  args: [owner, PERMIT2_ADDRESS]
2324
2127
  });
2325
2128
  }
2326
- async function readErc20Balance(publicClient, token, owner) {
2327
- return publicClient.readContract({
2328
- address: token,
2329
- abi: ERC20_ALLOWANCE_ABI,
2330
- functionName: "balanceOf",
2331
- args: [owner]
2129
+ async function readErc20Balance(publicClient, token2, owner) {
2130
+ return publicClient.readContract({
2131
+ address: token2,
2132
+ abi: ERC20_ALLOWANCE_ABI,
2133
+ functionName: "balanceOf",
2134
+ args: [owner]
2135
+ });
2136
+ }
2137
+
2138
+ // src/lib/sponsored-deposit.ts
2139
+ var AUTH_WINDOW_SECONDS = 15 * 60;
2140
+ var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
2141
+ function provideDepositVerificationContext(callback, context) {
2142
+ callback[verificationSetter]?.(context);
2143
+ }
2144
+ function makeVerificationAwareDepositCallback(implementation) {
2145
+ let nextVerification;
2146
+ const callback = async (smartWallet, chainId, amount) => {
2147
+ const verification = nextVerification;
2148
+ nextVerification = void 0;
2149
+ return implementation(smartWallet, chainId, amount, verification);
2150
+ };
2151
+ Object.defineProperty(callback, verificationSetter, {
2152
+ value: (context) => {
2153
+ nextVerification = context;
2154
+ }
2155
+ });
2156
+ return callback;
2157
+ }
2158
+ function makeSponsoredDepositCallback(deps) {
2159
+ const post = deps.httpPost ?? postSponsorTransferAuth;
2160
+ return makeVerificationAwareDepositCallback(
2161
+ async (smartWallet, chainId, amount, verification) => {
2162
+ const cid = chainId;
2163
+ const token2 = deps.tokenAddressByChain[cid];
2164
+ if (!token2) {
2165
+ throw new OwneyError(
2166
+ "CHAIN_UNSUPPORTED",
2167
+ `No sponsored token configured for chain ${chainId}`
2168
+ );
2169
+ }
2170
+ const pub = deps.getPublicClient(cid);
2171
+ const wallet = deps.getWalletClient(cid);
2172
+ await ensureWalletOnChain(pub, wallet, cid);
2173
+ try {
2174
+ const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
2175
+ if (balance < BigInt(amount)) {
2176
+ throw new OwneyError(
2177
+ "DEPOSIT_INSUFFICIENT_BALANCE",
2178
+ "Insufficient balance for this deposit.",
2179
+ { token: token2, chainId: cid, balance: balance.toString(), amount }
2180
+ );
2181
+ }
2182
+ } catch (err) {
2183
+ if (err instanceof OwneyError) throw err;
2184
+ console.warn(
2185
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2186
+ err instanceof Error ? err.message : String(err)
2187
+ );
2188
+ }
2189
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token2);
2190
+ const validAfter = 0n;
2191
+ const validBefore = BigInt(
2192
+ Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2193
+ );
2194
+ const nonce = randomAuthNonce();
2195
+ const typedData = buildTransferWithAuthorizationTypedData({
2196
+ token: token2,
2197
+ chainId: cid,
2198
+ tokenName,
2199
+ tokenVersion,
2200
+ message: {
2201
+ from: deps.ownerAddress,
2202
+ to: smartWallet,
2203
+ value: BigInt(amount),
2204
+ validAfter,
2205
+ validBefore,
2206
+ nonce
2207
+ }
2208
+ });
2209
+ const authSignature = await wallet.signTypedData({
2210
+ account: deps.ownerAddress,
2211
+ ...typedData
2212
+ });
2213
+ deps.onApproved?.();
2214
+ const result = await post({
2215
+ baseUrl: deps.baseUrl,
2216
+ apiKey: deps.apiKey,
2217
+ ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
2218
+ body: {
2219
+ chainId: cid,
2220
+ token: token2,
2221
+ from: deps.ownerAddress,
2222
+ to: smartWallet,
2223
+ value: amount,
2224
+ validAfter: validAfter.toString(),
2225
+ validBefore: validBefore.toString(),
2226
+ nonce,
2227
+ authSignature,
2228
+ tokenName,
2229
+ tokenVersion
2230
+ }
2231
+ });
2232
+ return result.txHash;
2233
+ }
2234
+ );
2235
+ }
2236
+
2237
+ // src/agents/yieldseeker/yieldseeker.auth.ts
2238
+ var import_siwe = require("siwe");
2239
+ var import_viem4 = require("viem");
2240
+ var import_chains2 = require("viem/chains");
2241
+
2242
+ // src/agents/yieldseeker/yieldseeker.auth-cache.ts
2243
+ var KEY_PREFIX2 = "owney.yieldseeker.session";
2244
+ var storage2 = () => {
2245
+ if (typeof window === "undefined") return null;
2246
+ try {
2247
+ return window.localStorage;
2248
+ } catch {
2249
+ return null;
2250
+ }
2251
+ };
2252
+ var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2253
+ var memorySessions2 = /* @__PURE__ */ new Map();
2254
+ var isValidSession = (session) => {
2255
+ if (!session?.token) return false;
2256
+ try {
2257
+ const parsed = JSON.parse(atob(session.token));
2258
+ return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
2259
+ } catch {
2260
+ return false;
2261
+ }
2262
+ };
2263
+ var readYieldseekerSession = (address, chainId) => {
2264
+ if (typeof window === "undefined") return null;
2265
+ const key2 = buildKey2(address, chainId);
2266
+ const store = storage2();
2267
+ let raw = null;
2268
+ try {
2269
+ raw = store?.getItem(key2) ?? null;
2270
+ } catch {
2271
+ raw = null;
2272
+ }
2273
+ if (raw) {
2274
+ try {
2275
+ const parsed = JSON.parse(raw);
2276
+ if (isValidSession(parsed)) return parsed.token;
2277
+ } catch {
2278
+ }
2279
+ memorySessions2.delete(key2);
2280
+ try {
2281
+ store?.removeItem(key2);
2282
+ } catch {
2283
+ }
2284
+ return null;
2285
+ }
2286
+ const cached = memorySessions2.get(key2);
2287
+ if (isValidSession(cached)) return cached.token;
2288
+ if (cached) memorySessions2.delete(key2);
2289
+ return null;
2290
+ };
2291
+ var writeYieldseekerSession = (address, chainId, token2) => {
2292
+ if (typeof window === "undefined") return;
2293
+ const session = { token: token2 };
2294
+ if (!isValidSession(session)) return;
2295
+ const key2 = buildKey2(address, chainId);
2296
+ memorySessions2.set(key2, session);
2297
+ const store = storage2();
2298
+ try {
2299
+ store?.setItem(key2, JSON.stringify(session));
2300
+ } catch {
2301
+ }
2302
+ };
2303
+ var clearYieldseekerSession = (address, chainId) => {
2304
+ const key2 = buildKey2(address, chainId);
2305
+ memorySessions2.delete(key2);
2306
+ const store = storage2();
2307
+ try {
2308
+ store?.removeItem(key2);
2309
+ } catch {
2310
+ }
2311
+ };
2312
+
2313
+ // src/agents/yieldseeker/yieldseeker.auth.ts
2314
+ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2315
+ return new import_siwe.SiweMessage({
2316
+ domain: "yieldseeker.xyz",
2317
+ address: (0, import_viem4.getAddress)(address),
2318
+ uri: "https://yieldseeker.xyz",
2319
+ version: "1",
2320
+ chainId,
2321
+ nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
2322
+ issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
2323
+ }).prepareMessage();
2324
+ }
2325
+ function encodeYieldseekerAuthToken(token2) {
2326
+ const bytes = new TextEncoder().encode(JSON.stringify(token2));
2327
+ let binary = "";
2328
+ for (const byte of bytes) binary += String.fromCharCode(byte);
2329
+ return btoa(binary);
2330
+ }
2331
+ var YieldseekerAuth = class {
2332
+ constructor(dependencies = {}) {
2333
+ this.dependencies = dependencies;
2334
+ }
2335
+ dependencies;
2336
+ tokens = /* @__PURE__ */ new Map();
2337
+ pending = /* @__PURE__ */ new Map();
2338
+ scopes = /* @__PURE__ */ new Map();
2339
+ key(state, chainId) {
2340
+ return `${state.walletAddress.toLowerCase()}:${chainId}`;
2341
+ }
2342
+ async getToken(state, chainId) {
2343
+ const key2 = this.key(state, chainId);
2344
+ const scope = { address: state.walletAddress, chainId };
2345
+ this.scopes.set(key2, scope);
2346
+ const cached = this.tokens.get(key2);
2347
+ if (cached) return cached;
2348
+ const persisted = readYieldseekerSession(scope.address, scope.chainId);
2349
+ if (persisted) {
2350
+ this.tokens.set(key2, persisted);
2351
+ return persisted;
2352
+ }
2353
+ const inFlight = this.pending.get(key2);
2354
+ if (inFlight) return inFlight;
2355
+ const request = this.sign(state, chainId).then((token2) => {
2356
+ this.tokens.set(key2, token2);
2357
+ writeYieldseekerSession(scope.address, scope.chainId, token2);
2358
+ return token2;
2359
+ });
2360
+ this.pending.set(key2, request);
2361
+ try {
2362
+ return await request;
2363
+ } finally {
2364
+ this.pending.delete(key2);
2365
+ }
2366
+ }
2367
+ clear(state, chainId) {
2368
+ if (!state || chainId === void 0) {
2369
+ for (const scope of this.scopes.values()) {
2370
+ clearYieldseekerSession(scope.address, scope.chainId);
2371
+ }
2372
+ this.tokens.clear();
2373
+ this.pending.clear();
2374
+ this.scopes.clear();
2375
+ return;
2376
+ }
2377
+ const key2 = this.key(state, chainId);
2378
+ this.tokens.delete(key2);
2379
+ this.pending.delete(key2);
2380
+ this.scopes.delete(key2);
2381
+ clearYieldseekerSession(state.walletAddress, chainId);
2382
+ }
2383
+ async sign(state, chainId) {
2384
+ const account = (0, import_viem4.getAddress)(state.walletAddress);
2385
+ const publicClient = (0, import_viem4.createPublicClient)({
2386
+ chain: import_chains2.base,
2387
+ transport: (0, import_viem4.custom)(state.provider)
2388
+ });
2389
+ const walletClient = (0, import_viem4.createWalletClient)({
2390
+ account,
2391
+ chain: import_chains2.base,
2392
+ transport: (0, import_viem4.custom)(state.provider)
2393
+ });
2394
+ await ensureWalletOnChain(
2395
+ publicClient,
2396
+ walletClient,
2397
+ 8453
2398
+ );
2399
+ const message = createYieldseekerSiweMessage(
2400
+ account,
2401
+ chainId,
2402
+ this.dependencies
2403
+ );
2404
+ const signature = await walletClient.signMessage({ account, message });
2405
+ return encodeYieldseekerAuthToken({ message, signature });
2406
+ }
2407
+ };
2408
+
2409
+ // src/agents/yieldseeker/yieldseeker.client.ts
2410
+ var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2411
+ function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
2412
+ return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
2413
+ }
2414
+ var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
2415
+ var YieldseekerApiError = class extends Error {
2416
+ constructor(status, providerCode, responseFields) {
2417
+ super(`Yieldseeker request failed (${status}): ${providerCode}`);
2418
+ this.status = status;
2419
+ this.providerCode = providerCode;
2420
+ this.responseFields = responseFields;
2421
+ this.name = "YieldseekerApiError";
2422
+ }
2423
+ status;
2424
+ providerCode;
2425
+ responseFields;
2426
+ get isAuthenticationError() {
2427
+ return this.status === 401 || this.status === 403;
2428
+ }
2429
+ };
2430
+ function providerError(body, fallback) {
2431
+ if (!body || typeof body !== "object") return { code: fallback };
2432
+ const record = body;
2433
+ return {
2434
+ code: typeof record.message === "string" ? record.message : fallback,
2435
+ fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
2436
+ };
2437
+ }
2438
+ var YieldseekerApiClient = class {
2439
+ constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
2440
+ this.owneyApiKey = owneyApiKey;
2441
+ this.baseUrl = baseUrl;
2442
+ this.fetchFn = fetchFn;
2443
+ }
2444
+ owneyApiKey;
2445
+ baseUrl;
2446
+ fetchFn;
2447
+ async request(path, options = {}) {
2448
+ const controller = new AbortController();
2449
+ const timer = setTimeout(
2450
+ () => controller.abort(),
2451
+ options.timeoutMs ?? 15e3
2452
+ );
2453
+ try {
2454
+ const response = await this.fetchFn(`${this.baseUrl}${path}`, {
2455
+ method: options.method ?? "GET",
2456
+ headers: {
2457
+ "Content-Type": "application/json",
2458
+ "x-owney-api-key": this.owneyApiKey,
2459
+ ...options.signature ? { "X-Signature": options.signature } : {}
2460
+ },
2461
+ body: options.body ? JSON.stringify(options.body) : void 0,
2462
+ signal: controller.signal
2463
+ });
2464
+ const payload = await response.json().catch(() => null);
2465
+ if (!response.ok) {
2466
+ const error = providerError(payload, `HTTP_${response.status}`);
2467
+ throw new YieldseekerApiError(
2468
+ response.status,
2469
+ error.code,
2470
+ error.fields
2471
+ );
2472
+ }
2473
+ if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
2474
+ return payload.data;
2475
+ }
2476
+ return payload;
2477
+ } catch (error) {
2478
+ if (error instanceof YieldseekerApiError) throw error;
2479
+ if (error instanceof DOMException && error.name === "AbortError") {
2480
+ throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
2481
+ }
2482
+ throw new YieldseekerApiError(0, "NETWORK_ERROR", {
2483
+ cause: error instanceof Error ? error.message : String(error)
2484
+ });
2485
+ } finally {
2486
+ clearTimeout(timer);
2487
+ }
2488
+ }
2489
+ };
2490
+
2491
+ // src/agents/yieldseeker/yieldseeker.mapper.ts
2492
+ var import_viem5 = require("viem");
2493
+ var REBALANCE_AMOUNT_DECIMALS = {
2494
+ USDC: 6,
2495
+ WETH: 18
2496
+ };
2497
+ function rebalanceAmount(value, tokenSymbol) {
2498
+ const decimals = REBALANCE_AMOUNT_DECIMALS[tokenSymbol.toUpperCase()];
2499
+ if (decimals === void 0) {
2500
+ return invalid(
2501
+ "history",
2502
+ `unsupported rebalance token ${tokenSymbol || "<empty>"}`
2503
+ );
2504
+ }
2505
+ try {
2506
+ return (0, import_viem5.formatUnits)(BigInt(value), decimals);
2507
+ } catch {
2508
+ return invalid("history", `invalid rebalance amount ${value}`);
2509
+ }
2510
+ }
2511
+ function invalid(endpoint, detail) {
2512
+ throw new OwneyError(
2513
+ "AGENT_INVALID_RESPONSE",
2514
+ `Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
2515
+ { endpoint, detail },
2516
+ "yieldseeker"
2517
+ );
2518
+ }
2519
+ function requireObject(value, endpoint) {
2520
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2521
+ return invalid(endpoint, "expected an object");
2522
+ }
2523
+ return value;
2524
+ }
2525
+ function requireEnvelope(value, key2, endpoint) {
2526
+ const root = requireObject(value, endpoint);
2527
+ return requireObject(root[key2], endpoint);
2528
+ }
2529
+ function token(value) {
2530
+ return {
2531
+ chain: value.chain,
2532
+ chainId: value.chainId,
2533
+ asset: value.asset,
2534
+ amount: String(value.amount)
2535
+ };
2536
+ }
2537
+ function position(value) {
2538
+ return {
2539
+ chain: String(value.chain),
2540
+ protocol: String(value.protocol),
2541
+ ...value.protocolId ? { protocolId: value.protocolId } : {},
2542
+ ...value.pool ? { pool: value.pool } : {},
2543
+ asset: String(value.asset),
2544
+ amount: String(value.amount),
2545
+ ...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
2546
+ ...typeof value.apy === "number" ? { apy: value.apy } : {},
2547
+ ...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
2548
+ ...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
2549
+ };
2550
+ }
2551
+ function mapApyMatrix(matrix) {
2552
+ const mapped = {};
2553
+ for (const [chain, assets] of Object.entries(matrix ?? {})) {
2554
+ const chainId = Number(chain);
2555
+ if (chainId !== 8453 || !assets || typeof assets !== "object") continue;
2556
+ mapped[8453] = {};
2557
+ for (const [asset, apy] of Object.entries(assets)) {
2558
+ if ((asset === "USDC" || asset === "WETH") && typeof apy === "number") {
2559
+ mapped[8453][asset] = apy;
2560
+ }
2561
+ }
2562
+ }
2563
+ return mapped;
2564
+ }
2565
+ function mapYieldseekerProfile(value) {
2566
+ const profile = requireEnvelope(
2567
+ value,
2568
+ "profile",
2569
+ "profile"
2570
+ );
2571
+ if (typeof profile.address !== "string" || typeof profile.smartWallet !== "string" || !Array.isArray(profile.chains) || typeof profile.hasActiveSessionKey !== "boolean" || !Array.isArray(profile.protocols)) {
2572
+ return invalid("profile", "missing required fields");
2573
+ }
2574
+ return {
2575
+ address: profile.address,
2576
+ smartWallet: profile.smartWallet,
2577
+ chains: profile.chains.map(Number),
2578
+ hasActiveSessionKey: profile.hasActiveSessionKey,
2579
+ protocols: profile.protocols.map(String)
2580
+ };
2581
+ }
2582
+ function mapYieldseekerBalances(value) {
2583
+ const balances = requireEnvelope(
2584
+ value,
2585
+ "balances",
2586
+ "balances"
2587
+ );
2588
+ if (typeof balances.smartWallet !== "string" || typeof balances.totalBalance !== "string" || typeof balances.totalBalanceAsset !== "string" || !Array.isArray(balances.tokens) || !Array.isArray(balances.positions)) {
2589
+ return invalid("balances", "missing required fields");
2590
+ }
2591
+ return {
2592
+ smartWallet: balances.smartWallet,
2593
+ totalBalance: balances.totalBalance,
2594
+ totalBalanceAsset: balances.totalBalanceAsset.toLowerCase(),
2595
+ tokens: balances.tokens.map(token),
2596
+ positions: balances.positions.map(position)
2597
+ };
2598
+ }
2599
+ function mapYieldseekerEarnings(value) {
2600
+ const earnings = requireEnvelope(
2601
+ value,
2602
+ "earnings",
2603
+ "earnings"
2604
+ );
2605
+ if (typeof earnings.smartWallet !== "string" || typeof earnings.lifetimeEarnings !== "number" || !Array.isArray(earnings.tokens)) {
2606
+ return invalid("earnings", "missing required fields");
2607
+ }
2608
+ return {
2609
+ smartWallet: earnings.smartWallet,
2610
+ lifetimeEarnings: earnings.lifetimeEarnings,
2611
+ tokens: earnings.tokens.map(token)
2612
+ };
2613
+ }
2614
+ function mapYieldseekerApy(value) {
2615
+ const apy = requireEnvelope(
2616
+ value,
2617
+ "apy",
2618
+ "apy"
2619
+ );
2620
+ if (typeof apy.walletAddress !== "string" || typeof apy.weightedApyAfterFee !== "number" || !apy.apyByChainAndAsset || typeof apy.apyByChainAndAsset !== "object" || !Array.isArray(apy.history)) {
2621
+ return invalid("apy", "missing required fields");
2622
+ }
2623
+ return {
2624
+ walletAddress: apy.walletAddress,
2625
+ weightedApyAfterFee: apy.weightedApyAfterFee,
2626
+ apyByChainAndAsset: mapApyMatrix(apy.apyByChainAndAsset),
2627
+ history: apy.history.map((point) => ({
2628
+ date: String(point.date),
2629
+ apy: Number(point.apy)
2630
+ }))
2631
+ };
2632
+ }
2633
+ function historyAction(value) {
2634
+ const normalized = value.trim().toLowerCase();
2635
+ if (normalized === "rebalance") return "Rebalance";
2636
+ if (normalized === "deposit") return "Deposit";
2637
+ if (normalized === "top up" || normalized === "topup") return "Top up";
2638
+ if (normalized === "withdraw" || normalized === "withdrawal")
2639
+ return "Withdraw";
2640
+ if (normalized === "earned" || normalized === "earnings") return "Earned";
2641
+ return invalid("history", `unsupported action ${value}`);
2642
+ }
2643
+ function historyItem(value) {
2644
+ return {
2645
+ agent: "yieldseeker",
2646
+ action: historyAction(value.action),
2647
+ date: String(value.date),
2648
+ oldApy: value.oldApy === null ? null : String(value.oldApy),
2649
+ newApy: value.newApy === null ? null : String(value.newApy),
2650
+ transactions: (value.transactions ?? []).map((transaction) => ({
2651
+ txHashes: transaction.txHashes.map(String),
2652
+ ...typeof transaction.chainId === "number" ? { chainId: transaction.chainId } : {},
2653
+ ...typeof transaction.tokenSymbol === "string" ? { tokenSymbol: transaction.tokenSymbol } : {},
2654
+ ...typeof transaction.amount === "string" ? { amount: transaction.amount } : {}
2655
+ })),
2656
+ rebalanceLog: (value.rebalanceLog ?? []).map((entry) => ({
2657
+ fromProtocol: String(entry.fromProtocol),
2658
+ toProtocol: String(entry.toProtocol),
2659
+ ...entry.fromPool ? { fromPool: entry.fromPool } : {},
2660
+ ...entry.toPool ? { toPool: entry.toPool } : {},
2661
+ tokenSymbol: String(entry.tokenSymbol),
2662
+ // Unlike Yieldseeker's other read-response amounts, live history
2663
+ // rebalance amounts are returned in the token's smallest unit. Normalize
2664
+ // them at the adapter boundary so every Owney consumer receives the
2665
+ // shared human-readable decimal shape.
2666
+ amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
2667
+ status: entry.status
2668
+ }))
2669
+ };
2670
+ }
2671
+ function mapYieldseekerHistory(value) {
2672
+ const history = requireEnvelope(
2673
+ value,
2674
+ "history",
2675
+ "history"
2676
+ );
2677
+ if (!Array.isArray(history.data) || typeof history.hasMore !== "boolean") {
2678
+ return invalid("history", "missing required fields");
2679
+ }
2680
+ return {
2681
+ data: history.data.map(historyItem),
2682
+ hasMore: history.hasMore,
2683
+ ...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
2684
+ };
2685
+ }
2686
+ function mapYieldseekerAgentApy(value) {
2687
+ const agentApy = requireEnvelope(
2688
+ value,
2689
+ "agentApy",
2690
+ "agent APY"
2691
+ );
2692
+ if (typeof agentApy.averageApy !== "number") {
2693
+ return invalid("agent APY", "missing averageApy");
2694
+ }
2695
+ return {
2696
+ averageApy: agentApy.averageApy,
2697
+ ...agentApy.detailedApys ? {
2698
+ detailedApys: {
2699
+ apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
2700
+ }
2701
+ } : {}
2702
+ };
2703
+ }
2704
+
2705
+ // src/agents/yieldseeker/yieldseeker.agent.ts
2706
+ function query(params) {
2707
+ const search = new URLSearchParams();
2708
+ for (const [key2, value] of Object.entries(params)) {
2709
+ if (value !== void 0) search.set(key2, String(value));
2710
+ }
2711
+ const encoded = search.toString();
2712
+ return encoded ? `?${encoded}` : "";
2713
+ }
2714
+ var YieldseekerAgent = class {
2715
+ id = "yieldseeker";
2716
+ balanceComposition = "tokens-plus-positions";
2717
+ supportedChainIds = [8453];
2718
+ supportedAssets = [
2719
+ {
2720
+ chainId: 8453,
2721
+ chain: "BASE",
2722
+ assets: [
2723
+ { symbol: "USDC", minDepositAmount: "1" },
2724
+ { symbol: "WETH", minDepositAmount: "1" }
2725
+ ]
2726
+ }
2727
+ ];
2728
+ api;
2729
+ auth;
2730
+ transactionExecutor;
2731
+ unwindReceiptWaiter;
2732
+ activated = /* @__PURE__ */ new Set();
2733
+ constructor(owneyApiKey, options = {}) {
2734
+ this.api = new YieldseekerApiClient(
2735
+ owneyApiKey,
2736
+ options.baseUrl ?? getYieldseekerProxyBaseUrl(),
2737
+ options.fetchFn
2738
+ );
2739
+ this.auth = new YieldseekerAuth(options.auth);
2740
+ this.transactionExecutor = options.transactionExecutor;
2741
+ this.unwindReceiptWaiter = options.unwindReceiptWaiter;
2742
+ }
2743
+ async disconnect() {
2744
+ this.auth.clear();
2745
+ this.activated.clear();
2746
+ }
2747
+ async activateAgent(state, chainId, asset) {
2748
+ this.assertChain(chainId);
2749
+ await this.auth.getToken(state, chainId);
2750
+ if (asset === "USDC" || asset === "WETH") {
2751
+ await this.ensureActivated(state, chainId, asset);
2752
+ }
2753
+ }
2754
+ async deposit(state, chainId, amount, asset, depositCallback) {
2755
+ this.assertChain(chainId);
2756
+ this.assertAsset(asset);
2757
+ if (BigInt(amount) <= 0n) {
2758
+ throw new OwneyError(
2759
+ "DEPOSIT_AMOUNT_BELOW_MINIMUM",
2760
+ "Yieldseeker deposits must be greater than zero.",
2761
+ { amount, minDepositAmount: "1" },
2762
+ this.id
2763
+ );
2764
+ }
2765
+ await this.ensureActivated(state, chainId, asset);
2766
+ const response = await this.walletRequest(
2767
+ state,
2768
+ chainId,
2769
+ "/deposits",
2770
+ { method: "POST", body: { chainId, asset, amount } }
2771
+ );
2772
+ const deposit = response?.deposit;
2773
+ if (!deposit?.transaction || typeof deposit.amount !== "string" || deposit.amount !== amount || typeof deposit.smartWallet !== "string" || !(0, import_viem6.isAddress)(deposit.smartWallet)) {
2774
+ throw this.invalidResponse("deposit");
2775
+ }
2776
+ if (depositCallback) {
2777
+ provideDepositVerificationContext(depositCallback, {
2778
+ agentId: "yieldseeker",
2779
+ // Re-read after /deposits so an authentication retry cannot forward
2780
+ // the stale token that Yieldseeker just rejected.
2781
+ signature: await this.auth.getToken(state, chainId)
2782
+ });
2783
+ }
2784
+ const txHash = depositCallback ? await depositCallback(deposit.smartWallet, chainId, amount) : await this.submitTransaction(state, chainId, deposit.transaction);
2785
+ const confirmation = await this.walletRequest(
2786
+ state,
2787
+ chainId,
2788
+ "/deposits/confirm",
2789
+ { method: "POST", body: { chainId, asset } }
2790
+ );
2791
+ if (typeof confirmation?.confirmation?.autoseekQueued !== "boolean") {
2792
+ throw this.invalidResponse("deposit confirmation", {
2793
+ transactionHash: txHash,
2794
+ transactionConfirmed: true
2795
+ });
2796
+ }
2797
+ return {
2798
+ txHash,
2799
+ smartWallet: deposit.smartWallet,
2800
+ amount: deposit.amount
2801
+ };
2802
+ }
2803
+ async withdraw(state, chainId, asset, amount) {
2804
+ this.assertChain(chainId);
2805
+ this.assertAsset(asset);
2806
+ if (amount !== void 0 && BigInt(amount) <= 0n) {
2807
+ throw new OwneyError(
2808
+ "WITHDRAW_FAILED",
2809
+ "Yieldseeker withdrawals must be greater than zero.",
2810
+ { amount },
2811
+ this.id
2812
+ );
2813
+ }
2814
+ const balances = await this.getBalances(state, chainId);
2815
+ const decimals = asset === "USDC" ? 6 : 18;
2816
+ const targetAsset = asset.toUpperCase();
2817
+ const idle = balances.tokens.filter(
2818
+ (token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
2819
+ ).reduce((total, token2) => total + (0, import_viem6.parseUnits)(token2.amount, decimals), 0n);
2820
+ const positions = (balances.positions ?? []).filter(
2821
+ (position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
2822
+ ).map((position2) => ({
2823
+ position: position2,
2824
+ amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : (0, import_viem6.parseUnits)(position2.amount, decimals)
2825
+ }));
2826
+ const deployed = positions.reduce(
2827
+ (total, position2) => total + position2.amount,
2828
+ 0n
2829
+ );
2830
+ const totalAvailable = idle + deployed;
2831
+ const requested = amount === void 0 ? totalAvailable : BigInt(amount);
2832
+ if (requested > totalAvailable) {
2833
+ throw new OwneyError(
2834
+ "WITHDRAW_INSUFFICIENT_BALANCE",
2835
+ `Requested withdrawal "${requested.toString()}" exceeds available Yieldseeker balance "${totalAvailable.toString()}" for asset "${asset}".`,
2836
+ {
2837
+ asset,
2838
+ requested: requested.toString(),
2839
+ available: totalAvailable.toString()
2840
+ },
2841
+ this.id
2842
+ );
2843
+ }
2844
+ const requiredFromPositions = requested > idle ? requested - idle : 0n;
2845
+ const unwindPlans = [];
2846
+ let remainingToUnwind = requiredFromPositions;
2847
+ for (const { position: position2, amount: positionAmount } of positions) {
2848
+ if (remainingToUnwind === 0n) break;
2849
+ if (positionAmount <= 0n) continue;
2850
+ if (!position2.protocolId || !(0, import_viem6.isAddress)(position2.protocolId)) {
2851
+ throw this.invalidResponse("position", {
2852
+ reason: "A deployed position is missing its vault address.",
2853
+ protocol: position2.protocol,
2854
+ pool: position2.pool
2855
+ });
2856
+ }
2857
+ const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
2858
+ unwindPlans.push({
2859
+ vaultAddress: (0, import_viem6.getAddress)(position2.protocolId),
2860
+ amount: unwindAmount
2861
+ });
2862
+ remainingToUnwind -= unwindAmount;
2863
+ }
2864
+ if (remainingToUnwind > 0n) {
2865
+ throw this.invalidResponse("balances", {
2866
+ reason: "Deployed position balances could not cover the unwind.",
2867
+ required: requiredFromPositions.toString(),
2868
+ planned: (requiredFromPositions - remainingToUnwind).toString()
2869
+ });
2870
+ }
2871
+ for (const plan of unwindPlans) {
2872
+ const response2 = await this.walletRequest(
2873
+ state,
2874
+ chainId,
2875
+ "/positions/unwind",
2876
+ {
2877
+ method: "POST",
2878
+ body: {
2879
+ chainId,
2880
+ asset,
2881
+ vaultAddress: plan.vaultAddress,
2882
+ amount: plan.amount.toString()
2883
+ }
2884
+ }
2885
+ );
2886
+ const transactionHash = response2?.unwind?.transactionHash;
2887
+ if (!this.isTransactionHash(transactionHash)) {
2888
+ throw this.invalidResponse("position unwind");
2889
+ }
2890
+ await this.waitForUnwindReceipt(state, chainId, transactionHash);
2891
+ }
2892
+ const response = await this.walletRequest(
2893
+ state,
2894
+ chainId,
2895
+ "/withdrawals",
2896
+ {
2897
+ method: "POST",
2898
+ body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
2899
+ }
2900
+ );
2901
+ const withdrawal = response?.withdrawal;
2902
+ if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
2903
+ throw this.invalidResponse("withdrawal");
2904
+ }
2905
+ const txHash = await this.submitTransaction(
2906
+ state,
2907
+ chainId,
2908
+ withdrawal.transaction
2909
+ );
2910
+ return {
2911
+ txHash,
2912
+ type: amount === void 0 ? "full" : "partial",
2913
+ amount: withdrawal.amount
2914
+ };
2915
+ }
2916
+ async getBalances(state, chainId) {
2917
+ return mapYieldseekerBalances(
2918
+ await this.walletRequest(
2919
+ state,
2920
+ chainId,
2921
+ `/balances${query({ chainId })}`
2922
+ )
2923
+ );
2924
+ }
2925
+ async getEarnings(state, chainId) {
2926
+ return mapYieldseekerEarnings(
2927
+ await this.walletRequest(
2928
+ state,
2929
+ chainId,
2930
+ `/earnings${query({ chainId })}`
2931
+ )
2932
+ );
2933
+ }
2934
+ async getAccountApy(state, chainId, days, tokenSymbol) {
2935
+ return mapYieldseekerApy(
2936
+ await this.walletRequest(
2937
+ state,
2938
+ chainId,
2939
+ `/apy${query({ chainId, days, tokenSymbol })}`
2940
+ )
2941
+ );
2942
+ }
2943
+ async getHistory(state, chainId, options) {
2944
+ return mapYieldseekerHistory(
2945
+ await this.walletRequest(
2946
+ state,
2947
+ chainId,
2948
+ `/history${query({
2949
+ chainId,
2950
+ limit: options?.limit ?? 10,
2951
+ cursor: options?.cursor,
2952
+ fromDate: options?.fromDate,
2953
+ toDate: options?.toDate
2954
+ })}`
2955
+ )
2956
+ );
2957
+ }
2958
+ async getUserProfile(state, chainId) {
2959
+ return mapYieldseekerProfile(
2960
+ await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
2961
+ );
2962
+ }
2963
+ async getAgentApy(days, options) {
2964
+ this.assertOptionalChain(options?.chainId);
2965
+ return mapYieldseekerAgentApy(
2966
+ await this.api.request(
2967
+ `/agent/apy${query({
2968
+ days,
2969
+ tokenSymbol: options?.tokenSymbol,
2970
+ chainId: options?.chainId
2971
+ })}`
2972
+ )
2973
+ );
2974
+ }
2975
+ async ensureActivated(state, chainId, asset) {
2976
+ const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
2977
+ if (this.activated.has(key2)) return;
2978
+ const response = await this.walletRequest(
2979
+ state,
2980
+ chainId,
2981
+ "/activation",
2982
+ { method: "POST", body: { chainId, asset } }
2983
+ );
2984
+ if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
2985
+ throw this.invalidResponse("activation");
2986
+ }
2987
+ this.activated.add(key2);
2988
+ }
2989
+ async walletRequest(state, chainId, path, options = {}) {
2990
+ this.assertChain(chainId);
2991
+ let signature = await this.auth.getToken(state, chainId);
2992
+ try {
2993
+ return await this.api.request(path, { ...options, signature });
2994
+ } catch (error) {
2995
+ if (!(error instanceof YieldseekerApiError)) throw error;
2996
+ if (error.isAuthenticationError) {
2997
+ this.auth.clear(state, chainId);
2998
+ signature = await this.auth.getToken(state, chainId);
2999
+ try {
3000
+ return await this.api.request(path, { ...options, signature });
3001
+ } catch (retryError) {
3002
+ throw this.mapApiError(retryError);
3003
+ }
3004
+ }
3005
+ throw this.mapApiError(error);
3006
+ }
3007
+ }
3008
+ mapApiError(error) {
3009
+ if (!(error instanceof YieldseekerApiError)) {
3010
+ return new OwneyError(
3011
+ "AGENT_API_ERROR",
3012
+ "Yieldseeker request failed.",
3013
+ { cause: error instanceof Error ? error.message : String(error) },
3014
+ this.id
3015
+ );
3016
+ }
3017
+ const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
3018
+ return new OwneyError(
3019
+ code,
3020
+ `Yieldseeker request failed: ${error.providerCode}.`,
3021
+ {
3022
+ statusCode: error.status,
3023
+ providerCode: error.providerCode,
3024
+ ...error.responseFields ? { fields: error.responseFields } : {}
3025
+ },
3026
+ this.id
3027
+ );
3028
+ }
3029
+ async submitTransaction(state, chainId, transaction) {
3030
+ if (this.transactionExecutor) {
3031
+ return this.transactionExecutor(state, chainId, transaction);
3032
+ }
3033
+ if (typeof transaction.from !== "string" || typeof transaction.to !== "string" || typeof transaction.data !== "string" || typeof transaction.value !== "string" || transaction.chainId !== chainId) {
3034
+ throw this.invalidResponse("transaction");
3035
+ }
3036
+ const account = (0, import_viem6.getAddress)(state.walletAddress);
3037
+ if ((0, import_viem6.getAddress)(transaction.from) !== account) {
3038
+ throw this.invalidResponse("transaction", {
3039
+ reason: "transaction.from does not match the connected wallet"
3040
+ });
3041
+ }
3042
+ const walletClient = (0, import_viem6.createWalletClient)({
3043
+ account,
3044
+ chain: import_chains3.base,
3045
+ transport: (0, import_viem6.custom)(state.provider)
3046
+ });
3047
+ const publicClient = (0, import_viem6.createPublicClient)({
3048
+ chain: import_chains3.base,
3049
+ transport: (0, import_viem6.custom)(state.provider)
3050
+ });
3051
+ await ensureWalletOnChain(
3052
+ publicClient,
3053
+ walletClient,
3054
+ 8453
3055
+ );
3056
+ const hash = await walletClient.sendTransaction({
3057
+ account,
3058
+ chain: import_chains3.base,
3059
+ to: (0, import_viem6.getAddress)(transaction.to),
3060
+ data: transaction.data,
3061
+ value: BigInt(transaction.value)
3062
+ });
3063
+ const receipt = await publicClient.waitForTransactionReceipt({
3064
+ hash,
3065
+ confirmations: 1
3066
+ });
3067
+ if (receipt.status !== "success") {
3068
+ throw new OwneyError(
3069
+ "AGENT_TRANSACTION_REVERTED",
3070
+ `Yieldseeker transaction reverted (${hash}).`,
3071
+ { transactionHash: hash },
3072
+ this.id
3073
+ );
3074
+ }
3075
+ return hash;
3076
+ }
3077
+ async waitForUnwindReceipt(state, chainId, transactionHash) {
3078
+ if (this.unwindReceiptWaiter) {
3079
+ await this.unwindReceiptWaiter(state, chainId, transactionHash);
3080
+ return;
3081
+ }
3082
+ const publicClient = (0, import_viem6.createPublicClient)({
3083
+ chain: import_chains3.base,
3084
+ transport: (0, import_viem6.custom)(state.provider)
3085
+ });
3086
+ const receipt = await publicClient.waitForTransactionReceipt({
3087
+ hash: transactionHash,
3088
+ confirmations: 1
3089
+ });
3090
+ if (receipt.status !== "success") {
3091
+ throw new OwneyError(
3092
+ "AGENT_TRANSACTION_REVERTED",
3093
+ `Yieldseeker position unwind reverted (${transactionHash}).`,
3094
+ { transactionHash },
3095
+ this.id
3096
+ );
3097
+ }
3098
+ }
3099
+ positionMatchesChain(chain, chainId) {
3100
+ const normalized = chain.trim().toUpperCase();
3101
+ return normalized === String(chainId) || normalized === "BASE";
3102
+ }
3103
+ isTransactionHash(value) {
3104
+ return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
3105
+ }
3106
+ assertChain(chainId) {
3107
+ if (chainId !== 8453) {
3108
+ throw new OwneyError(
3109
+ "CHAIN_UNSUPPORTED",
3110
+ `Yieldseeker does not support chain ${chainId}.`,
3111
+ { chainId, supportedChainIds: [8453] },
3112
+ this.id
3113
+ );
3114
+ }
3115
+ }
3116
+ assertOptionalChain(chainId) {
3117
+ if (chainId !== void 0) this.assertChain(chainId);
3118
+ }
3119
+ assertAsset(asset) {
3120
+ if (asset !== "USDC" && asset !== "WETH") {
3121
+ throw new OwneyError(
3122
+ "ASSET_UNSUPPORTED",
3123
+ `Yieldseeker does not support asset ${asset}.`,
3124
+ { asset, supportedAssets: ["USDC", "WETH"] },
3125
+ this.id
3126
+ );
3127
+ }
3128
+ }
3129
+ invalidResponse(operation, details = {}) {
3130
+ return new OwneyError(
3131
+ "AGENT_INVALID_RESPONSE",
3132
+ `Yieldseeker returned an invalid ${operation} response.`,
3133
+ details,
3134
+ this.id
3135
+ );
3136
+ }
3137
+ };
3138
+
3139
+ // src/lib/routing-api.ts
3140
+ var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3141
+ async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3142
+ const url = `${baseUrl}/api/v1/agent/org-config`;
3143
+ try {
3144
+ const res = await fetch(url, {
3145
+ method: "GET",
3146
+ headers: {
3147
+ "Content-Type": "application/json",
3148
+ "x-owney-api-key": `${apiKey}`
3149
+ }
3150
+ });
3151
+ if (!res.ok) {
3152
+ if (res.status !== 404) {
3153
+ console.warn(
3154
+ `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
3155
+ );
3156
+ }
3157
+ return null;
3158
+ }
3159
+ const json = await res.json();
3160
+ const policy = json.success ? json.data ?? null : null;
3161
+ debugLog(
3162
+ "owney-sdk",
3163
+ policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
3164
+ policy ?? void 0
3165
+ );
3166
+ return policy;
3167
+ } catch (error) {
3168
+ console.warn(
3169
+ "[owney-sdk] Could not read org agent config (non-fatal):",
3170
+ error instanceof Error ? error.message : String(error)
3171
+ );
3172
+ return null;
3173
+ }
3174
+ }
3175
+ async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3176
+ const url = `${baseUrl}/api/v1/agent/keys`;
3177
+ const res = await fetch(url, {
3178
+ method: "GET",
3179
+ headers: {
3180
+ "Content-Type": "application/json",
3181
+ "x-owney-api-key": `${apiKey}`
3182
+ }
3183
+ });
3184
+ if (!res.ok) {
3185
+ const text = await res.text().catch(() => "");
3186
+ throw new OwneyError(
3187
+ "API_ROUTING_ERROR",
3188
+ `Routing API error ${res.status}: ${text}`,
3189
+ { statusCode: res.status, responseBody: text }
3190
+ );
3191
+ }
3192
+ const json = await res.json();
3193
+ if (!json.success) {
3194
+ throw new OwneyError(
3195
+ "API_ROUTING_FAILED",
3196
+ `Routing API request failed: ${json.message}`,
3197
+ { message: json.message }
3198
+ );
3199
+ }
3200
+ return json.data;
3201
+ }
3202
+
3203
+ // src/lib/health-report.ts
3204
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3205
+ async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
3206
+ try {
3207
+ await fetch(`${baseUrl}/api/v1/agent/health-report`, {
3208
+ method: "POST",
3209
+ headers: {
3210
+ "Content-Type": "application/json",
3211
+ "x-owney-api-key": apiKey
3212
+ },
3213
+ body: JSON.stringify({
3214
+ agent_type: agentType,
3215
+ error_code: errorCode,
3216
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3217
+ })
3218
+ });
3219
+ } catch (err) {
3220
+ console.warn(
3221
+ `[owney-sdk] health-report failed for agent "${agentType}":`,
3222
+ err instanceof Error ? err.message : err
3223
+ );
3224
+ }
3225
+ }
3226
+ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
3227
+ try {
3228
+ return await fn();
3229
+ } catch (err) {
3230
+ const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3231
+ void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
3232
+ throw err;
3233
+ }
3234
+ }
3235
+
3236
+ // src/lib/helpers/withdraw-helper.ts
3237
+ var import_viem7 = require("viem");
3238
+ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
3239
+ const target = asset.toUpperCase();
3240
+ return agents.map((agent) => {
3241
+ const agentBalance = aggregated[agent.id];
3242
+ const tokenBalance = agentBalance?.tokens.find(
3243
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
3244
+ );
3245
+ let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
3246
+ if (agent.balanceComposition === "tokens-plus-positions") {
3247
+ const chainNameById = {
3248
+ 1: "ETHEREUM",
3249
+ 8453: "BASE",
3250
+ 42161: "ARBITRUM"
3251
+ };
3252
+ const targetChain = chainNameById[chainId];
3253
+ for (const position2 of agentBalance?.positions ?? []) {
3254
+ const positionChain = position2.chain.trim().toUpperCase();
3255
+ const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
3256
+ if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
3257
+ if (position2.amountRaw !== void 0) {
3258
+ try {
3259
+ balance += BigInt(position2.amountRaw);
3260
+ continue;
3261
+ } catch {
3262
+ }
3263
+ }
3264
+ balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
3265
+ }
3266
+ }
3267
+ return { agent, balance };
3268
+ });
3269
+ }
3270
+ function planProportionalShares(balances, requested, totalAvailable) {
3271
+ const plans = balances.map(({ agent, balance }) => ({
3272
+ agent,
3273
+ balance,
3274
+ planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
3275
+ }));
3276
+ const assigned = plans.reduce((s, p) => s + p.planned, 0n);
3277
+ let remainder = requested - assigned;
3278
+ const byHeadroom = [...plans].sort((a, b) => {
3279
+ const diff = b.balance - b.planned - (a.balance - a.planned);
3280
+ return diff > 0n ? 1 : diff < 0n ? -1 : 0;
2332
3281
  });
3282
+ for (const p of byHeadroom) {
3283
+ if (remainder === 0n) break;
3284
+ const headroom = p.balance - p.planned;
3285
+ if (headroom <= 0n) continue;
3286
+ const take = headroom < remainder ? headroom : remainder;
3287
+ p.planned += take;
3288
+ remainder -= take;
3289
+ }
3290
+ return plans;
2333
3291
  }
2334
-
2335
- // src/lib/chain-guard.ts
2336
- var CHAIN_NAMES = {
2337
- 1: "Ethereum",
2338
- 8453: "Base",
2339
- 42161: "Arbitrum"
2340
- };
2341
- function chainName(chainId) {
2342
- return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
3292
+ function planDisabledDrain(disabled, requested) {
3293
+ const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
3294
+ (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
3295
+ );
3296
+ const plans = [];
3297
+ let remaining = requested;
3298
+ for (const { agent, balance } of sorted) {
3299
+ if (remaining === 0n) {
3300
+ plans.push({ agent, balance, planned: 0n });
3301
+ continue;
3302
+ }
3303
+ const take = balance < remaining ? balance : remaining;
3304
+ plans.push({ agent, balance, planned: take });
3305
+ remaining -= take;
3306
+ }
3307
+ return { plans, remaining };
2343
3308
  }
2344
- async function ensureWalletOnChain(pub, wallet, expected) {
2345
- const actual = await pub.getChainId();
2346
- if (actual === expected) return;
2347
- try {
2348
- await wallet.switchChain({ id: expected });
2349
- } catch (error) {
2350
- throw new OwneyError(
2351
- "CHAIN_MISMATCH",
2352
- `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2353
- {
2354
- expectedChainId: expected,
2355
- actualChainId: actual,
2356
- cause: error instanceof Error ? error.message : String(error)
2357
- }
2358
- );
3309
+ function redistributeShare(plans, fromIndex, amount, candidatePool) {
3310
+ const pool = candidatePool ?? plans.slice(fromIndex + 1);
3311
+ const candidates = pool.filter((c) => c.balance - c.planned > 0n);
3312
+ const totalHeadroom = candidates.reduce(
3313
+ (s, c) => s + (c.balance - c.planned),
3314
+ 0n
3315
+ );
3316
+ if (totalHeadroom === 0n) return;
3317
+ let distributed = 0n;
3318
+ for (const c of candidates) {
3319
+ const headroom = c.balance - c.planned;
3320
+ const proportional = headroom * amount / totalHeadroom;
3321
+ const give = proportional > headroom ? headroom : proportional;
3322
+ c.planned += give;
3323
+ distributed += give;
2359
3324
  }
2360
- const after = await pub.getChainId();
2361
- if (after !== expected) {
2362
- throw new OwneyError(
2363
- "CHAIN_MISMATCH",
2364
- `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2365
- { expectedChainId: expected, actualChainId: after }
2366
- );
3325
+ let leftover = amount - distributed;
3326
+ for (const c of candidates) {
3327
+ if (leftover === 0n) break;
3328
+ const headroom = c.balance - c.planned;
3329
+ if (headroom <= 0n) continue;
3330
+ const take = headroom < leftover ? headroom : leftover;
3331
+ c.planned += take;
3332
+ leftover -= take;
2367
3333
  }
2368
3334
  }
3335
+ function sumWithdrawnAmount(results) {
3336
+ return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
3337
+ }
2369
3338
 
2370
- // src/lib/sponsored-deposit.ts
2371
- var AUTH_WINDOW_SECONDS = 15 * 60;
2372
- function makeSponsoredDepositCallback(deps) {
2373
- const post = deps.httpPost ?? postSponsorTransferAuth;
2374
- return async (smartWallet, chainId, amount) => {
2375
- const cid = chainId;
2376
- const token = deps.tokenAddressByChain[cid];
2377
- if (!token) {
2378
- throw new OwneyError(
2379
- "CHAIN_UNSUPPORTED",
2380
- `No sponsored token configured for chain ${chainId}`
2381
- );
3339
+ // src/lib/helpers/account-apy-helper.ts
3340
+ function balanceForApyScope(balance, chainId, tokenSymbol) {
3341
+ if (!tokenSymbol) {
3342
+ const total = Number(balance.totalBalance);
3343
+ return Number.isFinite(total) && total > 0 ? total : 0;
3344
+ }
3345
+ const normalizedToken = tokenSymbol.toUpperCase();
3346
+ return balance.tokens.reduce((total, token2) => {
3347
+ if (Number(token2.chainId) !== chainId || String(token2.asset).toUpperCase() !== normalizedToken) {
3348
+ return total;
2382
3349
  }
2383
- const pub = deps.getPublicClient(cid);
2384
- const wallet = deps.getWalletClient(cid);
2385
- await ensureWalletOnChain(pub, wallet, cid);
2386
- try {
2387
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2388
- if (balance < BigInt(amount)) {
2389
- throw new OwneyError(
2390
- "DEPOSIT_INSUFFICIENT_BALANCE",
2391
- "Insufficient balance for this deposit.",
2392
- { token, chainId: cid, balance: balance.toString(), amount }
2393
- );
2394
- }
2395
- } catch (err) {
2396
- if (err instanceof OwneyError) throw err;
2397
- console.warn(
2398
- "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2399
- err instanceof Error ? err.message : String(err)
2400
- );
2401
- }
2402
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2403
- const validAfter = 0n;
2404
- const validBefore = BigInt(
2405
- Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2406
- );
2407
- const nonce = randomAuthNonce();
2408
- const typedData = buildTransferWithAuthorizationTypedData({
2409
- token,
2410
- chainId: cid,
2411
- tokenName,
2412
- tokenVersion,
2413
- message: {
2414
- from: deps.ownerAddress,
2415
- to: smartWallet,
2416
- value: BigInt(amount),
2417
- validAfter,
2418
- validBefore,
2419
- nonce
2420
- }
2421
- });
2422
- const authSignature = await wallet.signTypedData({
2423
- account: deps.ownerAddress,
2424
- ...typedData
2425
- });
2426
- deps.onApproved?.();
2427
- const result = await post({
2428
- baseUrl: deps.baseUrl,
2429
- apiKey: deps.apiKey,
2430
- body: {
2431
- chainId: cid,
2432
- token,
2433
- from: deps.ownerAddress,
2434
- to: smartWallet,
2435
- value: amount,
2436
- validAfter: validAfter.toString(),
2437
- validBefore: validBefore.toString(),
2438
- nonce,
2439
- authSignature,
2440
- tokenName,
2441
- tokenVersion
3350
+ const amount = Number(token2.amount);
3351
+ return Number.isFinite(amount) && amount > 0 ? total + amount : total;
3352
+ }, 0);
3353
+ }
3354
+ function aggregateApyHistory(agentApys, agentBalances) {
3355
+ const byDate = /* @__PURE__ */ new Map();
3356
+ for (const [id, accountApy] of Object.entries(agentApys)) {
3357
+ const balance = agentBalances[id] ?? 0;
3358
+ if (!Number.isFinite(balance) || balance <= 0) continue;
3359
+ for (const point of accountApy.history ?? []) {
3360
+ const apy = Number(point.apy);
3361
+ if (!point.date || !Number.isFinite(apy)) continue;
3362
+ const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
3363
+ current.weightedSum += apy * balance;
3364
+ current.weight += balance;
3365
+ byDate.set(point.date, current);
3366
+ }
3367
+ }
3368
+ return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
3369
+ date,
3370
+ apy: value.weightedSum / value.weight
3371
+ }));
3372
+ }
3373
+ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
3374
+ const sums = {};
3375
+ const weights = {};
3376
+ for (const id of Object.keys(agentApys)) {
3377
+ const cells = agentApys[id].apyByChainAndAsset;
3378
+ const balance = agentBalances[id] ?? 0;
3379
+ if (!cells || balance <= 0) continue;
3380
+ for (const [chainKey, perAsset] of Object.entries(cells)) {
3381
+ if (!perAsset) continue;
3382
+ const chainId = Number(chainKey);
3383
+ for (const [asset, apyValue] of Object.entries(perAsset)) {
3384
+ const apy = Number(apyValue ?? 0);
3385
+ if (apy === 0) continue;
3386
+ sums[chainId] ??= {};
3387
+ weights[chainId] ??= {};
3388
+ sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
3389
+ weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2442
3390
  }
2443
- });
2444
- return result.txHash;
2445
- };
3391
+ }
3392
+ }
3393
+ const out = {};
3394
+ for (const chainKey of Object.keys(sums)) {
3395
+ const chainId = Number(chainKey);
3396
+ const perAssetOut = {};
3397
+ for (const asset of Object.keys(sums[chainId])) {
3398
+ const w = weights[chainId][asset];
3399
+ if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
3400
+ }
3401
+ if (Object.keys(perAssetOut).length > 0) {
3402
+ out[chainId] = perAssetOut;
3403
+ }
3404
+ }
3405
+ return out;
2446
3406
  }
2447
3407
 
3408
+ // src/client.ts
3409
+ var import_viem9 = require("viem");
3410
+ var import_chains4 = require("viem/chains");
3411
+
2448
3412
  // src/lib/sponsored-weth-deposit.ts
2449
3413
  var PERMIT_WINDOW_SECONDS = 15 * 60;
2450
3414
  function makeSponsoredWethCallback(deps) {
2451
3415
  const get = deps.httpGet ?? getSponsorRelayerAddress;
2452
3416
  const post = deps.httpPost ?? postSponsorPermit2Transfer;
2453
- return async (smartWallet, chainId, amount) => {
2454
- const cid = chainId;
2455
- const token = deps.tokenAddressByChain[cid];
2456
- if (!token) {
2457
- throw new OwneyError(
2458
- "CHAIN_UNSUPPORTED",
2459
- `No sponsored WETH configured for chain ${chainId}`
2460
- );
2461
- }
2462
- const amountWei = BigInt(amount);
2463
- const pub = deps.getPublicClient(cid);
2464
- const wallet = deps.getWalletClient(cid);
2465
- await ensureWalletOnChain(pub, wallet, cid);
2466
- try {
2467
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2468
- if (balance < amountWei) {
3417
+ return makeVerificationAwareDepositCallback(
3418
+ async (smartWallet, chainId, amount, verification) => {
3419
+ const cid = chainId;
3420
+ const token2 = deps.tokenAddressByChain[cid];
3421
+ if (!token2) {
2469
3422
  throw new OwneyError(
2470
- "DEPOSIT_INSUFFICIENT_BALANCE",
2471
- "Insufficient WETH balance for this deposit.",
2472
- { token, chainId: cid, balance: balance.toString(), amount }
3423
+ "CHAIN_UNSUPPORTED",
3424
+ `No sponsored WETH configured for chain ${chainId}`
2473
3425
  );
2474
3426
  }
2475
- } catch (err) {
2476
- if (err instanceof OwneyError) throw err;
2477
- console.warn(
2478
- "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2479
- err instanceof Error ? err.message : String(err)
2480
- );
2481
- }
2482
- const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2483
- if (allowance < amountWei) {
2484
- throw new OwneyError(
2485
- "PERMIT2_APPROVAL_REQUIRED",
2486
- "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
2487
- { token, chainId: cid, allowance: allowance.toString(), amount }
3427
+ const amountWei = BigInt(amount);
3428
+ const pub = deps.getPublicClient(cid);
3429
+ const wallet = deps.getWalletClient(cid);
3430
+ await ensureWalletOnChain(pub, wallet, cid);
3431
+ try {
3432
+ const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
3433
+ if (balance < amountWei) {
3434
+ throw new OwneyError(
3435
+ "DEPOSIT_INSUFFICIENT_BALANCE",
3436
+ "Insufficient WETH balance for this deposit.",
3437
+ { token: token2, chainId: cid, balance: balance.toString(), amount }
3438
+ );
3439
+ }
3440
+ } catch (err) {
3441
+ if (err instanceof OwneyError) throw err;
3442
+ console.warn(
3443
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
3444
+ err instanceof Error ? err.message : String(err)
3445
+ );
3446
+ }
3447
+ const allowance = await readPermit2Allowance(
3448
+ pub,
3449
+ token2,
3450
+ deps.ownerAddress
2488
3451
  );
2489
- }
2490
- const relayer = await get({
2491
- baseUrl: deps.baseUrl,
2492
- apiKey: deps.apiKey,
2493
- chainId: cid
2494
- });
2495
- const nonce = randomPermit2Nonce();
2496
- const deadline = BigInt(
2497
- Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
2498
- );
2499
- const typedData = buildPermitTransferFromTypedData({
2500
- chainId: cid,
2501
- message: {
2502
- permitted: { token, amount: amountWei },
2503
- spender: relayer,
2504
- nonce,
2505
- deadline
3452
+ if (allowance < amountWei) {
3453
+ throw new OwneyError(
3454
+ "PERMIT2_APPROVAL_REQUIRED",
3455
+ "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3456
+ { token: token2, chainId: cid, allowance: allowance.toString(), amount }
3457
+ );
2506
3458
  }
2507
- });
2508
- const signature = await wallet.signTypedData({
2509
- account: deps.ownerAddress,
2510
- ...typedData
2511
- });
2512
- deps.onApproved?.();
2513
- const result = await post({
2514
- baseUrl: deps.baseUrl,
2515
- apiKey: deps.apiKey,
2516
- body: {
3459
+ const relayer = await get({
3460
+ baseUrl: deps.baseUrl,
3461
+ apiKey: deps.apiKey,
3462
+ chainId: cid
3463
+ });
3464
+ const nonce = randomPermit2Nonce();
3465
+ const deadline = BigInt(
3466
+ Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
3467
+ );
3468
+ const typedData = buildPermitTransferFromTypedData({
2517
3469
  chainId: cid,
2518
- token,
2519
- from: deps.ownerAddress,
2520
- to: smartWallet,
2521
- amount,
2522
- nonce: nonce.toString(),
2523
- deadline: deadline.toString(),
2524
- signature
2525
- }
2526
- });
2527
- return result.txHash;
2528
- };
3470
+ message: {
3471
+ permitted: { token: token2, amount: amountWei },
3472
+ spender: relayer,
3473
+ nonce,
3474
+ deadline
3475
+ }
3476
+ });
3477
+ const signature = await wallet.signTypedData({
3478
+ account: deps.ownerAddress,
3479
+ ...typedData
3480
+ });
3481
+ deps.onApproved?.();
3482
+ const result = await post({
3483
+ baseUrl: deps.baseUrl,
3484
+ apiKey: deps.apiKey,
3485
+ ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
3486
+ body: {
3487
+ chainId: cid,
3488
+ token: token2,
3489
+ from: deps.ownerAddress,
3490
+ to: smartWallet,
3491
+ amount,
3492
+ nonce: nonce.toString(),
3493
+ deadline: deadline.toString(),
3494
+ signature
3495
+ }
3496
+ });
3497
+ return result.txHash;
3498
+ }
3499
+ );
2529
3500
  }
2530
3501
 
2531
3502
  // src/lib/sponsored-calls-deposit.ts
2532
- var import_viem5 = require("viem");
3503
+ var import_viem8 = require("viem");
2533
3504
  var DEFAULT_POLL_INTERVAL_MS = 1500;
2534
3505
  var DEFAULT_MAX_POLLS = 30;
2535
3506
  async function paymasterSupported(provider, owner, chainId) {
@@ -2537,7 +3508,7 @@ async function paymasterSupported(provider, owner, chainId) {
2537
3508
  method: "wallet_getCapabilities",
2538
3509
  params: [owner]
2539
3510
  });
2540
- const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
3511
+ const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
2541
3512
  return Boolean(forChain?.paymasterService?.supported);
2542
3513
  }
2543
3514
  function makeSponsoredCallsCallback(deps) {
@@ -2557,8 +3528,8 @@ function makeSponsoredCallsCallback(deps) {
2557
3528
  };
2558
3529
  return async (smartWallet, chainId, amount) => {
2559
3530
  const cid = chainId;
2560
- const token = deps.tokenAddressByChain[cid];
2561
- if (!token) {
3531
+ const token2 = deps.tokenAddressByChain[cid];
3532
+ if (!token2) {
2562
3533
  throw new OwneyError(
2563
3534
  "CHAIN_UNSUPPORTED",
2564
3535
  `No sponsored token configured for chain ${chainId}`
@@ -2571,8 +3542,8 @@ function makeSponsoredCallsCallback(deps) {
2571
3542
  { chainId }
2572
3543
  );
2573
3544
  }
2574
- const data = (0, import_viem5.encodeFunctionData)({
2575
- abi: import_viem5.erc20Abi,
3545
+ const data = (0, import_viem8.encodeFunctionData)({
3546
+ abi: import_viem8.erc20Abi,
2576
3547
  functionName: "transfer",
2577
3548
  args: [smartWallet, BigInt(amount)]
2578
3549
  });
@@ -2582,9 +3553,9 @@ function makeSponsoredCallsCallback(deps) {
2582
3553
  {
2583
3554
  version: "2.0.0",
2584
3555
  from: deps.ownerAddress,
2585
- chainId: (0, import_viem5.toHex)(chainId),
3556
+ chainId: (0, import_viem8.toHex)(chainId),
2586
3557
  atomicRequired: false,
2587
- calls: [{ to: token, value: "0x0", data }],
3558
+ calls: [{ to: token2, value: "0x0", data }],
2588
3559
  capabilities: {
2589
3560
  paymasterService: { url: absolutePaymasterUrl() }
2590
3561
  }
@@ -2623,10 +3594,10 @@ function makeSponsoredCallsCallback(deps) {
2623
3594
  function encodeMultiAgentCursor(map) {
2624
3595
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
2625
3596
  }
2626
- function decodeMultiAgentCursor(token) {
3597
+ function decodeMultiAgentCursor(token2) {
2627
3598
  let parsed;
2628
3599
  try {
2629
- const json = Buffer.from(token, "base64").toString("utf8");
3600
+ const json = Buffer.from(token2, "base64").toString("utf8");
2630
3601
  parsed = JSON.parse(json);
2631
3602
  } catch {
2632
3603
  throw new InvalidHistoryCursorError(
@@ -2646,9 +3617,9 @@ var SPONSORED_USDC_BY_CHAIN = {
2646
3617
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
2647
3618
  };
2648
3619
  var VIEM_CHAIN2 = {
2649
- 8453: import_chains2.base,
2650
- 42161: import_chains2.arbitrum,
2651
- 1: import_chains2.mainnet
3620
+ 8453: import_chains4.base,
3621
+ 42161: import_chains4.arbitrum,
3622
+ 1: import_chains4.mainnet
2652
3623
  };
2653
3624
  var SPONSORED_WETH_BY_CHAIN = {
2654
3625
  8453: "0x4200000000000000000000000000000000000006",
@@ -2676,6 +3647,7 @@ var OwneySDK = class {
2676
3647
  orgAgentConfig;
2677
3648
  orgAgentConfigPromise = null;
2678
3649
  zyfaiRpcUrls;
3650
+ yieldseekerApiBaseUrl;
2679
3651
  routingApiBaseUrl;
2680
3652
  referralSource;
2681
3653
  cachedSponsoredCallback = null;
@@ -2687,6 +3659,7 @@ var OwneySDK = class {
2687
3659
  this.apiKey = config.apiKey;
2688
3660
  if (config.debug) setOwneyDebug(true);
2689
3661
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
3662
+ this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
2690
3663
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2691
3664
  this.paymasterServiceUrl = config.paymasterServiceUrl;
2692
3665
  this.referralSource = config.referralSource;
@@ -2789,14 +3762,14 @@ var OwneySDK = class {
2789
3762
  // Casts work around viem's chain-narrowed Client vs the generic
2790
3763
  // PublicClient/WalletClient param types — structurally identical at
2791
3764
  // runtime, but the two share a name TS treats as unrelated.
2792
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
3765
+ getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
2793
3766
  chain: VIEM_CHAIN2[cid],
2794
- transport: (0, import_viem6.custom)(provider)
3767
+ transport: (0, import_viem9.custom)(provider)
2795
3768
  }),
2796
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
3769
+ getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
2797
3770
  account: owner,
2798
3771
  chain: VIEM_CHAIN2[cid],
2799
- transport: (0, import_viem6.custom)(provider)
3772
+ transport: (0, import_viem9.custom)(provider)
2800
3773
  })
2801
3774
  });
2802
3775
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -2842,14 +3815,14 @@ var OwneySDK = class {
2842
3815
  // Casts work around viem's chain-narrowed Client vs the generic
2843
3816
  // PublicClient/WalletClient param types — structurally identical at
2844
3817
  // runtime, but the two share a name TS treats as unrelated.
2845
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
3818
+ getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
2846
3819
  chain: VIEM_CHAIN2[cid],
2847
- transport: (0, import_viem6.custom)(provider)
3820
+ transport: (0, import_viem9.custom)(provider)
2848
3821
  }),
2849
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
3822
+ getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
2850
3823
  account: owner,
2851
3824
  chain: VIEM_CHAIN2[cid],
2852
- transport: (0, import_viem6.custom)(provider)
3825
+ transport: (0, import_viem9.custom)(provider)
2853
3826
  })
2854
3827
  });
2855
3828
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -2877,12 +3850,13 @@ var OwneySDK = class {
2877
3850
  async getOrgAgentConfig() {
2878
3851
  if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
2879
3852
  if (!this.orgAgentConfigPromise) {
2880
- this.orgAgentConfigPromise = fetchOrgAgentConfig(this.apiKey).then(
2881
- (config) => {
2882
- this.orgAgentConfig = config;
2883
- return config;
2884
- }
2885
- );
3853
+ this.orgAgentConfigPromise = fetchOrgAgentConfig(
3854
+ this.apiKey,
3855
+ this.routingApiBaseUrl
3856
+ ).then((config) => {
3857
+ this.orgAgentConfig = config;
3858
+ return config;
3859
+ });
2886
3860
  }
2887
3861
  return this.orgAgentConfigPromise;
2888
3862
  }
@@ -2917,9 +3891,19 @@ var OwneySDK = class {
2917
3891
  if (this.agents.size > 0) return;
2918
3892
  if (!this.initializingAgentsPromise) {
2919
3893
  this.initializingAgentsPromise = (async () => {
2920
- const agentKeys = await fetchAgentKeys(this.apiKey);
3894
+ const agentKeys = await fetchAgentKeys(
3895
+ this.apiKey,
3896
+ this.routingApiBaseUrl
3897
+ );
2921
3898
  this.disabledAgents.clear();
2922
- for (const { key: key2, agent_type, is_enabled } of agentKeys) {
3899
+ for (const {
3900
+ key: key2,
3901
+ agent_type,
3902
+ is_enabled,
3903
+ is_configured
3904
+ } of agentKeys) {
3905
+ const configured = is_configured ?? Boolean(key2);
3906
+ if (!configured) continue;
2923
3907
  const agent = this.createAgent(agent_type, key2);
2924
3908
  if (!agent) continue;
2925
3909
  this.agents.set(agent_type, agent);
@@ -2943,10 +3927,47 @@ var OwneySDK = class {
2943
3927
  }
2944
3928
  createAgent(agentId, key2) {
2945
3929
  if (agentId === "zyfai") {
3930
+ if (!key2) return null;
2946
3931
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2947
3932
  }
3933
+ if (agentId === "yieldseeker") {
3934
+ return new YieldseekerAgent(this.apiKey, {
3935
+ baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
3936
+ });
3937
+ }
2948
3938
  return null;
2949
3939
  }
3940
+ /**
3941
+ * Discover the agents actually returned by routing for this organization.
3942
+ * Consumers should use this instead of hardcoding a global agent roster.
3943
+ */
3944
+ async getAvailableAgents(options = {}) {
3945
+ await this.ensureAgentsInitialized();
3946
+ const { chainId, asset, includeDisabled = false } = options;
3947
+ const available = [];
3948
+ for (const [id, agent] of this.agents) {
3949
+ const isEnabled = !this.isAgentDisabled(id);
3950
+ if (!includeDisabled && !isEnabled) continue;
3951
+ if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
3952
+ continue;
3953
+ }
3954
+ if (asset !== void 0) {
3955
+ const supportsAsset = agent.supportedAssets.some(
3956
+ (entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
3957
+ );
3958
+ if (!supportsAsset) {
3959
+ continue;
3960
+ }
3961
+ }
3962
+ available.push({
3963
+ id,
3964
+ isEnabled,
3965
+ supportedChainIds: agent.supportedChainIds,
3966
+ supportedAssets: agent.supportedAssets
3967
+ });
3968
+ }
3969
+ return available;
3970
+ }
2950
3971
  // --- Account lifecycle ---
2951
3972
  /**
2952
3973
  * Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
@@ -2958,7 +3979,7 @@ var OwneySDK = class {
2958
3979
  * If provided, ALL specified agents must support the chainId or the call
2959
3980
  * throws before activating any agent.
2960
3981
  */
2961
- async activateAgent(chainId, agentId) {
3982
+ async activateAgent(chainId, agentId, asset) {
2962
3983
  const state = this.requireState();
2963
3984
  await this.ensureAgentsInitialized();
2964
3985
  if (agentId !== void 0) {
@@ -2994,9 +4015,7 @@ var OwneySDK = class {
2994
4015
  this.activeAgents.add(id);
2995
4016
  }
2996
4017
  state.chainId = chainId;
2997
- this.activateAgentsInTurn(agents, state, chainId).catch((error) => {
2998
- console.error("activateAgent background init failed:", error);
2999
- });
4018
+ await this.activateAgentsInTurn(agents, state, chainId, asset);
3000
4019
  return;
3001
4020
  }
3002
4021
  const compatible = [...this.agents.values()].filter(
@@ -3017,11 +4036,7 @@ var OwneySDK = class {
3017
4036
  const enabledCompatible = compatible.filter(
3018
4037
  (agent) => !this.isAgentDisabled(agent.id)
3019
4038
  );
3020
- this.activateAgentsInTurn(enabledCompatible, state, chainId).catch(
3021
- (error) => {
3022
- console.error("activateAgent background init failed:", error);
3023
- }
3024
- );
4039
+ await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
3025
4040
  }
3026
4041
  /**
3027
4042
  * Activate agents ONE AT A TIME, each followed by its org policy.
@@ -3041,11 +4056,11 @@ var OwneySDK = class {
3041
4056
  * rethrown (matching the previous `Promise.all` rejection) once all agents
3042
4057
  * have had a chance to activate.
3043
4058
  */
3044
- async activateAgentsInTurn(agents, state, chainId) {
4059
+ async activateAgentsInTurn(agents, state, chainId, asset) {
3045
4060
  let firstError = null;
3046
4061
  for (const agent of agents) {
3047
4062
  try {
3048
- await agent.activateAgent(state, chainId);
4063
+ await agent.activateAgent(state, chainId, asset);
3049
4064
  await this.applyOrgPolicyTo(agent, state, chainId);
3050
4065
  } catch (error) {
3051
4066
  if (firstError === null) {
@@ -3173,7 +4188,8 @@ var OwneySDK = class {
3173
4188
  amount,
3174
4189
  asset,
3175
4190
  callback
3176
- )
4191
+ ),
4192
+ this.routingApiBaseUrl
3177
4193
  );
3178
4194
  let approvalAttempted = false;
3179
4195
  for (; ; ) {
@@ -3253,10 +4269,10 @@ var OwneySDK = class {
3253
4269
  try {
3254
4270
  const balance = await agent.getBalances(state, chainId);
3255
4271
  const target = asset.toLowerCase();
3256
- const token = balance.tokens.find(
4272
+ const token2 = balance.tokens.find(
3257
4273
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3258
4274
  );
3259
- return !!token && Number(token.amount) > 0;
4275
+ return !!token2 && Number(token2.amount) > 0;
3260
4276
  } catch {
3261
4277
  return false;
3262
4278
  }
@@ -3307,9 +4323,9 @@ var OwneySDK = class {
3307
4323
  const { asset, amount, agentId } = options;
3308
4324
  const state = this.requireState();
3309
4325
  const chainId = this.requireChainId();
3310
- const token = asset;
4326
+ const token2 = asset;
3311
4327
  const assetInfo = SupportedAssets.find(
3312
- (a) => a.chainId === chainId && a.symbol === token
4328
+ (a) => a.chainId === chainId && a.symbol === token2
3313
4329
  );
3314
4330
  if (!assetInfo) {
3315
4331
  throw new OwneyError(
@@ -3323,7 +4339,8 @@ var OwneySDK = class {
3323
4339
  return withFailureReporting(
3324
4340
  this.apiKey,
3325
4341
  agent.id,
3326
- () => agent.withdraw(state, chainId, token, amount)
4342
+ () => agent.withdraw(state, chainId, token2, amount),
4343
+ this.routingApiBaseUrl
3327
4344
  );
3328
4345
  }
3329
4346
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
@@ -3332,12 +4349,17 @@ var OwneySDK = class {
3332
4349
  const agentErrors2 = {};
3333
4350
  for (const agent of eligibleAgents) {
3334
4351
  try {
3335
- results2[agent.id] = await agent.withdraw(state, chainId, token);
4352
+ results2[agent.id] = await agent.withdraw(state, chainId, token2);
3336
4353
  } catch (err) {
3337
4354
  console.error(`withdraw failed for agent "${agent.id}":`, err);
3338
4355
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
3339
4356
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3340
- void reportAgentFailure(this.apiKey, agent.id, code);
4357
+ void reportAgentFailure(
4358
+ this.apiKey,
4359
+ agent.id,
4360
+ code,
4361
+ this.routingApiBaseUrl
4362
+ );
3341
4363
  }
3342
4364
  }
3343
4365
  if (Object.keys(results2).length === 0) {
@@ -3354,6 +4376,10 @@ var OwneySDK = class {
3354
4376
  }
3355
4377
  const requested = BigInt(amount);
3356
4378
  const aggregated = await this.getBalances();
4379
+ const unavailableAgents = eligibleAgents.filter(
4380
+ (agent) => !(agent.id in aggregated.agentBalances)
4381
+ );
4382
+ const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
3357
4383
  const balances = projectAgentBalancesForAsset(
3358
4384
  eligibleAgents,
3359
4385
  aggregated.agentBalances,
@@ -3362,7 +4388,18 @@ var OwneySDK = class {
3362
4388
  assetInfo.decimals
3363
4389
  );
3364
4390
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
3365
- if (totalAvailable < requested) {
4391
+ if (totalAvailable === 0n && unavailableAgents.length > 0) {
4392
+ throw new OwneyError(
4393
+ "WITHDRAW_BALANCE_UNAVAILABLE",
4394
+ `Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
4395
+ {
4396
+ asset,
4397
+ unavailableAgents: unavailableAgentIds,
4398
+ agentErrors: aggregated.agentErrors
4399
+ }
4400
+ );
4401
+ }
4402
+ if (totalAvailable < requested && unavailableAgents.length === 0) {
3366
4403
  throw new OwneyError(
3367
4404
  "WITHDRAW_INSUFFICIENT_BALANCE",
3368
4405
  `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
@@ -3373,6 +4410,7 @@ var OwneySDK = class {
3373
4410
  }
3374
4411
  );
3375
4412
  }
4413
+ const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
3376
4414
  const disabledBalances = balances.filter(
3377
4415
  (b) => this.isAgentDisabled(b.agent.id)
3378
4416
  );
@@ -3381,7 +4419,7 @@ var OwneySDK = class {
3381
4419
  );
3382
4420
  const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
3383
4421
  disabledBalances,
3384
- requested
4422
+ plannedTarget
3385
4423
  );
3386
4424
  const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
3387
4425
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
@@ -3391,7 +4429,9 @@ var OwneySDK = class {
3391
4429
  }));
3392
4430
  const plans = [...disabledPlans, ...enabledPlans];
3393
4431
  const results = {};
3394
- const agentErrors = {};
4432
+ const agentErrors = {
4433
+ ...aggregated.agentErrors ?? {}
4434
+ };
3395
4435
  for (let i = 0; i < plans.length; i++) {
3396
4436
  const p = plans[i];
3397
4437
  if (p.planned === 0n) continue;
@@ -3399,14 +4439,19 @@ var OwneySDK = class {
3399
4439
  results[p.agent.id] = await p.agent.withdraw(
3400
4440
  state,
3401
4441
  chainId,
3402
- token,
4442
+ token2,
3403
4443
  p.planned.toString()
3404
4444
  );
3405
4445
  } catch (err) {
3406
4446
  console.error(`withdraw failed for agent "${p.agent.id}":`, err);
3407
4447
  agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
3408
4448
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3409
- void reportAgentFailure(this.apiKey, p.agent.id, code);
4449
+ void reportAgentFailure(
4450
+ this.apiKey,
4451
+ p.agent.id,
4452
+ code,
4453
+ this.routingApiBaseUrl
4454
+ );
3410
4455
  const failedAmount = p.planned;
3411
4456
  p.planned = 0n;
3412
4457
  redistributeShare(plans, i, failedAmount);
@@ -3433,7 +4478,8 @@ var OwneySDK = class {
3433
4478
  requested: amount,
3434
4479
  withdrawn: withdrawn.toString(),
3435
4480
  partialResults: results,
3436
- agentErrors
4481
+ agentErrors,
4482
+ ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
3437
4483
  }
3438
4484
  );
3439
4485
  }
@@ -3474,7 +4520,12 @@ var OwneySDK = class {
3474
4520
  continue;
3475
4521
  }
3476
4522
  const reason = settledResult.reason;
3477
- agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
4523
+ const message = reason instanceof Error ? reason.message : String(reason);
4524
+ agentErrors[agentId2] = message;
4525
+ console.error(
4526
+ `[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
4527
+ reason
4528
+ );
3478
4529
  }
3479
4530
  if (successCount === 0) {
3480
4531
  throw new OwneyError(
@@ -3486,7 +4537,8 @@ var OwneySDK = class {
3486
4537
  return {
3487
4538
  totalBalance: String(totalBalance),
3488
4539
  totalBalanceAsset: "usdc",
3489
- agentBalances: results
4540
+ agentBalances: results,
4541
+ ...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
3490
4542
  };
3491
4543
  }
3492
4544
  /**
@@ -3580,7 +4632,10 @@ var OwneySDK = class {
3580
4632
  Promise.all(
3581
4633
  entries.map(async ([id, agent]) => {
3582
4634
  const b = await agent.getBalances(state, chainId);
3583
- return [id, Number(b.totalBalance)];
4635
+ return [
4636
+ id,
4637
+ balanceForApyScope(b, chainId, tokenSymbol)
4638
+ ];
3584
4639
  })
3585
4640
  )
3586
4641
  ]);
@@ -3608,10 +4663,12 @@ var OwneySDK = class {
3608
4663
  }
3609
4664
  }
3610
4665
  const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
4666
+ const history = aggregateApyHistory(results, balances);
3611
4667
  return {
3612
4668
  totalApy: String(totalApy),
3613
4669
  agentApy: results,
3614
- apyByChainAndAsset
4670
+ apyByChainAndAsset,
4671
+ history
3615
4672
  };
3616
4673
  }
3617
4674
  /**
@@ -3733,30 +4790,30 @@ var OwneySDK = class {
3733
4790
  const state = this.requireState();
3734
4791
  const chainId = this.requireChainId();
3735
4792
  this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
3736
- const token = SPONSORED_WETH_BY_CHAIN[chainId];
3737
- if (!token) {
4793
+ const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
4794
+ if (!token2) {
3738
4795
  throw new OwneyError(
3739
4796
  "CHAIN_UNSUPPORTED",
3740
4797
  `No sponsored WETH on chain ${chainId}`
3741
4798
  );
3742
4799
  }
3743
4800
  const provider = this.requireConnectedProvider();
3744
- const wallet = (0, import_viem6.createWalletClient)({
4801
+ const wallet = (0, import_viem9.createWalletClient)({
3745
4802
  account: state.walletAddress,
3746
4803
  chain: VIEM_CHAIN2[chainId],
3747
- transport: (0, import_viem6.custom)(provider)
4804
+ transport: (0, import_viem9.custom)(provider)
3748
4805
  });
3749
4806
  const hash = await wallet.writeContract({
3750
- address: token,
4807
+ address: token2,
3751
4808
  abi: ERC20_ALLOWANCE_ABI,
3752
4809
  functionName: "approve",
3753
4810
  args: [PERMIT2_ADDRESS, MAX_UINT256],
3754
4811
  account: state.walletAddress,
3755
4812
  chain: VIEM_CHAIN2[chainId]
3756
4813
  });
3757
- const publicClient = (0, import_viem6.createPublicClient)({
4814
+ const publicClient = (0, import_viem9.createPublicClient)({
3758
4815
  chain: VIEM_CHAIN2[chainId],
3759
- transport: (0, import_viem6.custom)(provider)
4816
+ transport: (0, import_viem9.custom)(provider)
3760
4817
  });
3761
4818
  const receipt = await publicClient.waitForTransactionReceipt({
3762
4819
  hash,
@@ -3792,7 +4849,9 @@ var OwneySDK = class {
3792
4849
  return this.getAgent(agentId).getAgentApy(days, agentOptions);
3793
4850
  }
3794
4851
  const results = {};
3795
- const agentEntries = [...this.agents.entries()];
4852
+ const agentEntries = [...this.agents.entries()].filter(
4853
+ ([id]) => !this.isAgentDisabled(id)
4854
+ );
3796
4855
  const apyResults = await Promise.all(
3797
4856
  agentEntries.map(async ([id, agent]) => {
3798
4857
  const apy = await agent.getAgentApy(days, agentOptions);
@@ -3865,13 +4924,13 @@ var OwneySDK = class {
3865
4924
  };
3866
4925
 
3867
4926
  // src/agents/zyfai/zyfai.siwx.ts
3868
- var import_viem7 = require("viem");
3869
- var import_siwe = require("siwe");
4927
+ var import_viem10 = require("viem");
4928
+ var import_siwe2 = require("siwe");
3870
4929
  var import_sdk2 = require("@zyfai/sdk");
3871
4930
 
3872
4931
  // src/agents/zyfai/zyfai.siwx-cache.ts
3873
- var KEY_PREFIX2 = "owney.siwx.session";
3874
- var storage2 = () => {
4932
+ var KEY_PREFIX3 = "owney.siwx.session";
4933
+ var storage3 = () => {
3875
4934
  if (typeof window === "undefined") return null;
3876
4935
  try {
3877
4936
  return window.localStorage;
@@ -3879,8 +4938,8 @@ var storage2 = () => {
3879
4938
  return null;
3880
4939
  }
3881
4940
  };
3882
- var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
3883
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
4941
+ var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
4942
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
3884
4943
  var memorySiwxSessions = /* @__PURE__ */ new Map();
3885
4944
  var readLegacySiwxSession = (store, address) => {
3886
4945
  if (!store) return null;
@@ -3911,8 +4970,8 @@ var readLegacySiwxSession = (store, address) => {
3911
4970
  };
3912
4971
  var readSiwxSession = (address, chainId) => {
3913
4972
  if (typeof window === "undefined") return null;
3914
- const key2 = buildKey2(address);
3915
- const store = storage2();
4973
+ const key2 = buildKey3(address);
4974
+ const store = storage3();
3916
4975
  let raw = null;
3917
4976
  try {
3918
4977
  raw = store?.getItem(key2) ?? null;
@@ -3940,18 +4999,18 @@ var readSiwxSession = (address, chainId) => {
3940
4999
  };
3941
5000
  var writeSiwxSession = (address, _chainId, session) => {
3942
5001
  if (typeof window === "undefined") return;
3943
- const key2 = buildKey2(address);
5002
+ const key2 = buildKey3(address);
3944
5003
  memorySiwxSessions.set(key2, session);
3945
- const store = storage2();
5004
+ const store = storage3();
3946
5005
  try {
3947
5006
  store?.setItem(key2, JSON.stringify(session));
3948
5007
  } catch {
3949
5008
  }
3950
5009
  };
3951
5010
  var clearSiwxSession = (address, _chainId) => {
3952
- const key2 = buildKey2(address);
5011
+ const key2 = buildKey3(address);
3953
5012
  memorySiwxSessions.delete(key2);
3954
- const store = storage2();
5013
+ const store = storage3();
3955
5014
  try {
3956
5015
  store?.removeItem(key2);
3957
5016
  } catch {
@@ -3991,8 +5050,8 @@ function buildSIWXConfig(deps) {
3991
5050
  statement: STATEMENT,
3992
5051
  issuedAt,
3993
5052
  toString() {
3994
- return new import_siwe.SiweMessage({
3995
- address: (0, import_viem7.getAddress)(accountAddress),
5053
+ return new import_siwe2.SiweMessage({
5054
+ address: (0, import_viem10.getAddress)(accountAddress),
3996
5055
  chainId: numericChainId(chainId),
3997
5056
  domain,
3998
5057
  uri,
@@ -4034,7 +5093,7 @@ function buildSIWXConfig(deps) {
4034
5093
  const persistSession = async (session) => {
4035
5094
  const address = session.data.accountAddress;
4036
5095
  const id = numericChainId(session.data.chainId);
4037
- const message = new import_siwe.SiweMessage(session.message);
5096
+ const message = new import_siwe2.SiweMessage(session.message);
4038
5097
  const login = await post("/auth/login", {
4039
5098
  message,
4040
5099
  signature: session.signature,
@@ -4083,6 +5142,7 @@ function createOwneySIWX(config) {
4083
5142
  NotConnectedError,
4084
5143
  OwneyError,
4085
5144
  OwneySDK,
5145
+ YieldseekerAgent,
4086
5146
  createOwneySIWX,
4087
5147
  setOwneyDebug
4088
5148
  });