@owney/sdk 0.7.15 → 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
  });
@@ -339,9 +340,9 @@ function mapWeightedApyByChain(raw) {
339
340
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
340
341
  const perAsset = {};
341
342
  for (const [symbol, apy] of Object.entries(tokenApy)) {
342
- const token = symbol;
343
- if (!SUPPORTED_TOKENS.includes(token)) continue;
344
- perAsset[token] = apy;
343
+ const token2 = symbol;
344
+ if (!SUPPORTED_TOKENS.includes(token2)) continue;
345
+ perAsset[token2] = apy;
345
346
  }
346
347
  if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
347
348
  }
@@ -510,23 +511,23 @@ function computeAllocationApy(positions) {
510
511
  weightedSum += apy * value;
511
512
  totalValue += value;
512
513
  const chainId = resolveChainId(p.chain);
513
- const token = p.asset;
514
+ const token2 = p.asset;
514
515
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
515
- if (!SUPPORTED_TOKENS.includes(token)) continue;
516
+ if (!SUPPORTED_TOKENS.includes(token2)) continue;
516
517
  const perAsset = buckets[chainId] ?? {};
517
- const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
518
+ const cell = perAsset[token2] ?? { weightedSum: 0, value: 0 };
518
519
  cell.weightedSum += apy * value;
519
520
  cell.value += value;
520
- perAsset[token] = cell;
521
+ perAsset[token2] = cell;
521
522
  buckets[chainId] = perAsset;
522
523
  }
523
524
  const apyByChainAndAsset = {};
524
525
  for (const [chainKey, perAsset] of Object.entries(buckets)) {
525
526
  const chainId = Number(chainKey);
526
527
  const out = {};
527
- for (const [token, cell] of Object.entries(perAsset ?? {})) {
528
+ for (const [token2, cell] of Object.entries(perAsset ?? {})) {
528
529
  if (cell && cell.value > 0) {
529
- out[token] = cell.weightedSum / cell.value;
530
+ out[token2] = cell.weightedSum / cell.value;
530
531
  }
531
532
  }
532
533
  if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
@@ -540,9 +541,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
540
541
  function topicToAddress(topic) {
541
542
  return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
542
543
  }
543
- function extractWithdrawnAmount(logs, recipient, token, decimals) {
544
+ function extractWithdrawnAmount(logs, recipient, token2, decimals) {
544
545
  const wantRecipient = recipient.toLowerCase();
545
- const wantToken = token.toLowerCase();
546
+ const wantToken = token2.toLowerCase();
546
547
  let total = 0n;
547
548
  let matched = false;
548
549
  for (const log of logs) {
@@ -568,11 +569,11 @@ var InvalidHistoryCursorError = class extends Error {
568
569
  function encodeHistoryCursor(payload) {
569
570
  return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
570
571
  }
571
- function decodeHistoryCursor(token) {
572
- if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
572
+ function decodeHistoryCursor(token2) {
573
+ if (!token2) throw new InvalidHistoryCursorError("Cursor is empty.");
573
574
  let parsed;
574
575
  try {
575
- const json = Buffer.from(token, "base64").toString("utf8");
576
+ const json = Buffer.from(token2, "base64").toString("utf8");
576
577
  parsed = JSON.parse(json);
577
578
  } catch {
578
579
  throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
@@ -595,8 +596,8 @@ var storage = () => {
595
596
  };
596
597
  var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
597
598
  var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
598
- var isJwtExpired = (token) => {
599
- const parts = token.split(".");
599
+ var isJwtExpired = (token2) => {
600
+ const parts = token2.split(".");
600
601
  if (parts.length !== 3) return false;
601
602
  try {
602
603
  const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
@@ -1698,20 +1699,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
1698
1699
  }
1699
1700
  }
1700
1701
  // --- IAgent: Fund operations ---
1701
- async withdraw(state, chainId, token, amount) {
1702
+ async withdraw(state, chainId, token2, amount) {
1702
1703
  const validChainId = isValidChainId(chainId);
1703
1704
  await this.ensureConnected(state, validChainId);
1704
1705
  const raw = await this.sdk.withdrawFunds(
1705
1706
  this.getAddress(),
1706
1707
  validChainId,
1707
1708
  amount,
1708
- token
1709
+ token2
1709
1710
  );
1710
1711
  if (!raw.success) {
1711
1712
  throw new OwneyError(
1712
1713
  "WITHDRAW_FAILED",
1713
1714
  raw.message || "Zyfai withdraw failed.",
1714
- { chainId: validChainId, token, amount, response: raw },
1715
+ { chainId: validChainId, token: token2, amount, response: raw },
1715
1716
  this.id
1716
1717
  );
1717
1718
  }
@@ -1862,225 +1863,47 @@ var ZyfaiAgent = class _ZyfaiAgent {
1862
1863
  }
1863
1864
  };
1864
1865
 
1865
- // src/lib/routing-api.ts
1866
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1867
- async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL) {
1868
- const url = `${baseUrl}/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;
1869
1882
  try {
1870
- const res = await fetch(url, {
1871
- method: "GET",
1872
- headers: {
1873
- "Content-Type": "application/json",
1874
- "x-owney-api-key": `${apiKey}`
1875
- }
1876
- });
1877
- if (!res.ok) {
1878
- if (res.status !== 404) {
1879
- console.warn(
1880
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
1881
- );
1882
- }
1883
- return null;
1884
- }
1885
- const json = await res.json();
1886
- const policy = json.success ? json.data ?? null : null;
1887
- debugLog(
1888
- "owney-sdk",
1889
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
1890
- policy ?? void 0
1891
- );
1892
- return policy;
1883
+ await wallet.switchChain({ id: expected });
1893
1884
  } catch (error) {
1894
- console.warn(
1895
- "[owney-sdk] Could not read org agent config (non-fatal):",
1896
- error instanceof Error ? error.message : String(error)
1897
- );
1898
- return null;
1899
- }
1900
- }
1901
- async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
1902
- const url = `${baseUrl}/api/v1/agent/keys`;
1903
- const res = await fetch(url, {
1904
- method: "GET",
1905
- headers: {
1906
- "Content-Type": "application/json",
1907
- "x-owney-api-key": `${apiKey}`
1908
- }
1909
- });
1910
- if (!res.ok) {
1911
- const text = await res.text().catch(() => "");
1912
1885
  throw new OwneyError(
1913
- "API_ROUTING_ERROR",
1914
- `Routing API error ${res.status}: ${text}`,
1915
- { statusCode: res.status, responseBody: text }
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)
1892
+ }
1916
1893
  );
1917
1894
  }
1918
- const json = await res.json();
1919
- if (!json.success) {
1895
+ const after = await pub.getChainId();
1896
+ if (after !== expected) {
1920
1897
  throw new OwneyError(
1921
- "API_ROUTING_FAILED",
1922
- `Routing API request failed: ${json.message}`,
1923
- { message: json.message }
1924
- );
1925
- }
1926
- return json.data;
1927
- }
1928
-
1929
- // src/lib/health-report.ts
1930
- var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1931
- async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
1932
- try {
1933
- await fetch(`${baseUrl}/api/v1/agent/health-report`, {
1934
- method: "POST",
1935
- headers: {
1936
- "Content-Type": "application/json",
1937
- "x-owney-api-key": apiKey
1938
- },
1939
- body: JSON.stringify({
1940
- agent_type: agentType,
1941
- error_code: errorCode,
1942
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1943
- })
1944
- });
1945
- } catch (err) {
1946
- console.warn(
1947
- `[owney-sdk] health-report failed for agent "${agentType}":`,
1948
- err instanceof Error ? err.message : err
1949
- );
1950
- }
1951
- }
1952
- async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
1953
- try {
1954
- return await fn();
1955
- } catch (err) {
1956
- const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
1957
- void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
1958
- throw err;
1959
- }
1960
- }
1961
-
1962
- // src/lib/helpers/withdraw-helper.ts
1963
- var import_viem2 = require("viem");
1964
- function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1965
- const target = asset.toUpperCase();
1966
- return agents.map((agent) => {
1967
- const agentBalance = aggregated[agent.id];
1968
- const tokenBalance = agentBalance?.tokens.find(
1969
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
1898
+ "CHAIN_MISMATCH",
1899
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
1900
+ { expectedChainId: expected, actualChainId: after }
1970
1901
  );
1971
- if (!tokenBalance) return { agent, balance: 0n };
1972
- return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
1973
- });
1974
- }
1975
- function planProportionalShares(balances, requested, totalAvailable) {
1976
- const plans = balances.map(({ agent, balance }) => ({
1977
- agent,
1978
- balance,
1979
- planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
1980
- }));
1981
- const assigned = plans.reduce((s, p) => s + p.planned, 0n);
1982
- let remainder = requested - assigned;
1983
- const byHeadroom = [...plans].sort((a, b) => {
1984
- const diff = b.balance - b.planned - (a.balance - a.planned);
1985
- return diff > 0n ? 1 : diff < 0n ? -1 : 0;
1986
- });
1987
- for (const p of byHeadroom) {
1988
- if (remainder === 0n) break;
1989
- const headroom = p.balance - p.planned;
1990
- if (headroom <= 0n) continue;
1991
- const take = headroom < remainder ? headroom : remainder;
1992
- p.planned += take;
1993
- remainder -= take;
1994
- }
1995
- return plans;
1996
- }
1997
- function planDisabledDrain(disabled, requested) {
1998
- const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
1999
- const plans = [];
2000
- let remaining = requested;
2001
- for (const { agent, balance } of sorted) {
2002
- if (remaining === 0n) {
2003
- plans.push({ agent, balance, planned: 0n });
2004
- continue;
2005
- }
2006
- const take = balance < remaining ? balance : remaining;
2007
- plans.push({ agent, balance, planned: take });
2008
- remaining -= take;
2009
- }
2010
- return { plans, remaining };
2011
- }
2012
- function redistributeShare(plans, fromIndex, amount, candidatePool) {
2013
- const pool = candidatePool ?? plans.slice(fromIndex + 1);
2014
- const candidates = pool.filter((c) => c.balance - c.planned > 0n);
2015
- const totalHeadroom = candidates.reduce(
2016
- (s, c) => s + (c.balance - c.planned),
2017
- 0n
2018
- );
2019
- if (totalHeadroom === 0n) return;
2020
- let distributed = 0n;
2021
- for (const c of candidates) {
2022
- const headroom = c.balance - c.planned;
2023
- const proportional = headroom * amount / totalHeadroom;
2024
- const give = proportional > headroom ? headroom : proportional;
2025
- c.planned += give;
2026
- distributed += give;
2027
- }
2028
- let leftover = amount - distributed;
2029
- for (const c of candidates) {
2030
- if (leftover === 0n) break;
2031
- const headroom = c.balance - c.planned;
2032
- if (headroom <= 0n) continue;
2033
- const take = headroom < leftover ? headroom : leftover;
2034
- c.planned += take;
2035
- leftover -= take;
2036
- }
2037
- }
2038
- function sumWithdrawnAmount(results) {
2039
- return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
2040
- }
2041
-
2042
- // src/lib/helpers/account-apy-helper.ts
2043
- function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2044
- const sums = {};
2045
- const weights = {};
2046
- for (const id of Object.keys(agentApys)) {
2047
- const cells = agentApys[id].apyByChainAndAsset;
2048
- const balance = agentBalances[id] ?? 0;
2049
- if (!cells || balance <= 0) continue;
2050
- for (const [chainKey, perAsset] of Object.entries(cells)) {
2051
- if (!perAsset) continue;
2052
- const chainId = Number(chainKey);
2053
- for (const [asset, apyValue] of Object.entries(perAsset)) {
2054
- const apy = Number(apyValue ?? 0);
2055
- if (apy === 0) continue;
2056
- sums[chainId] ??= {};
2057
- weights[chainId] ??= {};
2058
- sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
2059
- weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2060
- }
2061
- }
2062
- }
2063
- const out = {};
2064
- for (const chainKey of Object.keys(sums)) {
2065
- const chainId = Number(chainKey);
2066
- const perAssetOut = {};
2067
- for (const asset of Object.keys(sums[chainId])) {
2068
- const w = weights[chainId][asset];
2069
- if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
2070
- }
2071
- if (Object.keys(perAssetOut).length > 0) {
2072
- out[chainId] = perAssetOut;
2073
- }
2074
1902
  }
2075
- return out;
2076
1903
  }
2077
1904
 
2078
- // src/client.ts
2079
- var import_viem6 = require("viem");
2080
- var import_chains2 = require("viem/chains");
2081
-
2082
1905
  // src/lib/transfer-auth.ts
2083
- var import_viem3 = require("viem");
1906
+ var import_viem2 = require("viem");
2084
1907
  var ERC20_META_ABI = [
2085
1908
  { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
2086
1909
  { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
@@ -2102,30 +1925,31 @@ function buildTransferWithAuthorizationTypedData(input) {
2102
1925
  message: input.message
2103
1926
  };
2104
1927
  }
2105
- async function readTokenMeta(publicClient, token) {
1928
+ async function readTokenMeta(publicClient, token2) {
2106
1929
  const [tokenName, tokenVersion] = await Promise.all([
2107
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
2108
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1930
+ publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "name" }),
1931
+ publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
2109
1932
  ]);
2110
1933
  return { tokenName, tokenVersion };
2111
1934
  }
2112
1935
  function randomAuthNonce() {
2113
1936
  const bytes = new Uint8Array(32);
2114
1937
  globalThis.crypto.getRandomValues(bytes);
2115
- return (0, import_viem3.bytesToHex)(bytes);
1938
+ return (0, import_viem2.bytesToHex)(bytes);
2116
1939
  }
2117
1940
 
2118
1941
  // src/lib/sponsor-client.ts
2119
- var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1942
+ var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2120
1943
  async function postSponsorTransferAuth(input) {
2121
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1944
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2122
1945
  let res;
2123
1946
  try {
2124
- res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
1947
+ res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
2125
1948
  method: "POST",
2126
1949
  headers: {
2127
1950
  "content-type": "application/json",
2128
- "x-owney-api-key": input.apiKey
1951
+ "x-owney-api-key": input.apiKey,
1952
+ ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
2129
1953
  },
2130
1954
  body: JSON.stringify(input.body)
2131
1955
  });
@@ -2158,14 +1982,15 @@ async function postSponsorTransferAuth(input) {
2158
1982
  return parsed.data;
2159
1983
  }
2160
1984
  async function postSponsorPermit2Transfer(input) {
2161
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1985
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2162
1986
  let res;
2163
1987
  try {
2164
- res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
1988
+ res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
2165
1989
  method: "POST",
2166
1990
  headers: {
2167
1991
  "content-type": "application/json",
2168
- "x-owney-api-key": input.apiKey
1992
+ "x-owney-api-key": input.apiKey,
1993
+ ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
2169
1994
  },
2170
1995
  body: JSON.stringify(input.body)
2171
1996
  });
@@ -2196,11 +2021,11 @@ async function postSponsorPermit2Transfer(input) {
2196
2021
  return parsed.data;
2197
2022
  }
2198
2023
  async function getSponsorRelayerAddress(input) {
2199
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2024
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2200
2025
  let res;
2201
2026
  try {
2202
2027
  res = await fetch(
2203
- `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2028
+ `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2204
2029
  {
2205
2030
  headers: { "x-owney-api-key": input.apiKey }
2206
2031
  }
@@ -2233,7 +2058,7 @@ async function getSponsorRelayerAddress(input) {
2233
2058
  }
2234
2059
 
2235
2060
  // src/lib/permit2.ts
2236
- var import_viem4 = require("viem");
2061
+ var import_viem3 = require("viem");
2237
2062
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2238
2063
  var MAX_UINT256 = 2n ** 256n - 1n;
2239
2064
  var ERC20_ALLOWANCE_ABI = [
@@ -2291,223 +2116,1391 @@ function buildPermitTransferFromTypedData(input) {
2291
2116
  function randomPermit2Nonce() {
2292
2117
  const bytes = new Uint8Array(32);
2293
2118
  globalThis.crypto.getRandomValues(bytes);
2294
- return BigInt((0, import_viem4.bytesToHex)(bytes));
2119
+ return BigInt((0, import_viem3.bytesToHex)(bytes));
2295
2120
  }
2296
- async function readPermit2Allowance(publicClient, token, owner) {
2121
+ async function readPermit2Allowance(publicClient, token2, owner) {
2297
2122
  return publicClient.readContract({
2298
- address: token,
2123
+ address: token2,
2299
2124
  abi: ERC20_ALLOWANCE_ABI,
2300
2125
  functionName: "allowance",
2301
2126
  args: [owner, PERMIT2_ADDRESS]
2302
2127
  });
2303
2128
  }
2304
- async function readErc20Balance(publicClient, token, owner) {
2305
- return publicClient.readContract({
2306
- address: token,
2307
- abi: ERC20_ALLOWANCE_ABI,
2308
- functionName: "balanceOf",
2309
- 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;
2310
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;
2311
3291
  }
2312
-
2313
- // src/lib/chain-guard.ts
2314
- var CHAIN_NAMES = {
2315
- 1: "Ethereum",
2316
- 8453: "Base",
2317
- 42161: "Arbitrum"
2318
- };
2319
- function chainName(chainId) {
2320
- 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 };
2321
3308
  }
2322
- async function ensureWalletOnChain(pub, wallet, expected) {
2323
- const actual = await pub.getChainId();
2324
- if (actual === expected) return;
2325
- try {
2326
- await wallet.switchChain({ id: expected });
2327
- } catch (error) {
2328
- throw new OwneyError(
2329
- "CHAIN_MISMATCH",
2330
- `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2331
- {
2332
- expectedChainId: expected,
2333
- actualChainId: actual,
2334
- cause: error instanceof Error ? error.message : String(error)
2335
- }
2336
- );
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;
2337
3324
  }
2338
- const after = await pub.getChainId();
2339
- if (after !== expected) {
2340
- throw new OwneyError(
2341
- "CHAIN_MISMATCH",
2342
- `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2343
- { expectedChainId: expected, actualChainId: after }
2344
- );
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;
2345
3333
  }
2346
3334
  }
3335
+ function sumWithdrawnAmount(results) {
3336
+ return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
3337
+ }
2347
3338
 
2348
- // src/lib/sponsored-deposit.ts
2349
- var AUTH_WINDOW_SECONDS = 15 * 60;
2350
- function makeSponsoredDepositCallback(deps) {
2351
- const post = deps.httpPost ?? postSponsorTransferAuth;
2352
- return async (smartWallet, chainId, amount) => {
2353
- const cid = chainId;
2354
- const token = deps.tokenAddressByChain[cid];
2355
- if (!token) {
2356
- throw new OwneyError(
2357
- "CHAIN_UNSUPPORTED",
2358
- `No sponsored token configured for chain ${chainId}`
2359
- );
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;
2360
3349
  }
2361
- const pub = deps.getPublicClient(cid);
2362
- const wallet = deps.getWalletClient(cid);
2363
- await ensureWalletOnChain(pub, wallet, cid);
2364
- try {
2365
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2366
- if (balance < BigInt(amount)) {
2367
- throw new OwneyError(
2368
- "DEPOSIT_INSUFFICIENT_BALANCE",
2369
- "Insufficient balance for this deposit.",
2370
- { token, chainId: cid, balance: balance.toString(), amount }
2371
- );
2372
- }
2373
- } catch (err) {
2374
- if (err instanceof OwneyError) throw err;
2375
- console.warn(
2376
- "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2377
- err instanceof Error ? err.message : String(err)
2378
- );
2379
- }
2380
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2381
- const validAfter = 0n;
2382
- const validBefore = BigInt(
2383
- Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2384
- );
2385
- const nonce = randomAuthNonce();
2386
- const typedData = buildTransferWithAuthorizationTypedData({
2387
- token,
2388
- chainId: cid,
2389
- tokenName,
2390
- tokenVersion,
2391
- message: {
2392
- from: deps.ownerAddress,
2393
- to: smartWallet,
2394
- value: BigInt(amount),
2395
- validAfter,
2396
- validBefore,
2397
- nonce
2398
- }
2399
- });
2400
- const authSignature = await wallet.signTypedData({
2401
- account: deps.ownerAddress,
2402
- ...typedData
2403
- });
2404
- deps.onApproved?.();
2405
- const result = await post({
2406
- baseUrl: deps.baseUrl,
2407
- apiKey: deps.apiKey,
2408
- body: {
2409
- chainId: cid,
2410
- token,
2411
- from: deps.ownerAddress,
2412
- to: smartWallet,
2413
- value: amount,
2414
- validAfter: validAfter.toString(),
2415
- validBefore: validBefore.toString(),
2416
- nonce,
2417
- authSignature,
2418
- tokenName,
2419
- 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;
2420
3390
  }
2421
- });
2422
- return result.txHash;
2423
- };
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;
2424
3406
  }
2425
3407
 
3408
+ // src/client.ts
3409
+ var import_viem9 = require("viem");
3410
+ var import_chains4 = require("viem/chains");
3411
+
2426
3412
  // src/lib/sponsored-weth-deposit.ts
2427
3413
  var PERMIT_WINDOW_SECONDS = 15 * 60;
2428
3414
  function makeSponsoredWethCallback(deps) {
2429
3415
  const get = deps.httpGet ?? getSponsorRelayerAddress;
2430
3416
  const post = deps.httpPost ?? postSponsorPermit2Transfer;
2431
- return async (smartWallet, chainId, amount) => {
2432
- const cid = chainId;
2433
- const token = deps.tokenAddressByChain[cid];
2434
- if (!token) {
2435
- throw new OwneyError(
2436
- "CHAIN_UNSUPPORTED",
2437
- `No sponsored WETH configured for chain ${chainId}`
2438
- );
2439
- }
2440
- const amountWei = BigInt(amount);
2441
- const pub = deps.getPublicClient(cid);
2442
- const wallet = deps.getWalletClient(cid);
2443
- await ensureWalletOnChain(pub, wallet, cid);
2444
- try {
2445
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2446
- 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) {
2447
3422
  throw new OwneyError(
2448
- "DEPOSIT_INSUFFICIENT_BALANCE",
2449
- "Insufficient WETH balance for this deposit.",
2450
- { token, chainId: cid, balance: balance.toString(), amount }
3423
+ "CHAIN_UNSUPPORTED",
3424
+ `No sponsored WETH configured for chain ${chainId}`
2451
3425
  );
2452
3426
  }
2453
- } catch (err) {
2454
- if (err instanceof OwneyError) throw err;
2455
- console.warn(
2456
- "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2457
- err instanceof Error ? err.message : String(err)
2458
- );
2459
- }
2460
- const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2461
- if (allowance < amountWei) {
2462
- throw new OwneyError(
2463
- "PERMIT2_APPROVAL_REQUIRED",
2464
- "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
2465
- { 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
2466
3451
  );
2467
- }
2468
- const relayer = await get({
2469
- baseUrl: deps.baseUrl,
2470
- apiKey: deps.apiKey,
2471
- chainId: cid
2472
- });
2473
- const nonce = randomPermit2Nonce();
2474
- const deadline = BigInt(
2475
- Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
2476
- );
2477
- const typedData = buildPermitTransferFromTypedData({
2478
- chainId: cid,
2479
- message: {
2480
- permitted: { token, amount: amountWei },
2481
- spender: relayer,
2482
- nonce,
2483
- 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
+ );
2484
3458
  }
2485
- });
2486
- const signature = await wallet.signTypedData({
2487
- account: deps.ownerAddress,
2488
- ...typedData
2489
- });
2490
- deps.onApproved?.();
2491
- const result = await post({
2492
- baseUrl: deps.baseUrl,
2493
- apiKey: deps.apiKey,
2494
- 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({
2495
3469
  chainId: cid,
2496
- token,
2497
- from: deps.ownerAddress,
2498
- to: smartWallet,
2499
- amount,
2500
- nonce: nonce.toString(),
2501
- deadline: deadline.toString(),
2502
- signature
2503
- }
2504
- });
2505
- return result.txHash;
2506
- };
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
+ );
2507
3500
  }
2508
3501
 
2509
3502
  // src/lib/sponsored-calls-deposit.ts
2510
- var import_viem5 = require("viem");
3503
+ var import_viem8 = require("viem");
2511
3504
  var DEFAULT_POLL_INTERVAL_MS = 1500;
2512
3505
  var DEFAULT_MAX_POLLS = 30;
2513
3506
  async function paymasterSupported(provider, owner, chainId) {
@@ -2515,7 +3508,7 @@ async function paymasterSupported(provider, owner, chainId) {
2515
3508
  method: "wallet_getCapabilities",
2516
3509
  params: [owner]
2517
3510
  });
2518
- const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
3511
+ const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
2519
3512
  return Boolean(forChain?.paymasterService?.supported);
2520
3513
  }
2521
3514
  function makeSponsoredCallsCallback(deps) {
@@ -2535,8 +3528,8 @@ function makeSponsoredCallsCallback(deps) {
2535
3528
  };
2536
3529
  return async (smartWallet, chainId, amount) => {
2537
3530
  const cid = chainId;
2538
- const token = deps.tokenAddressByChain[cid];
2539
- if (!token) {
3531
+ const token2 = deps.tokenAddressByChain[cid];
3532
+ if (!token2) {
2540
3533
  throw new OwneyError(
2541
3534
  "CHAIN_UNSUPPORTED",
2542
3535
  `No sponsored token configured for chain ${chainId}`
@@ -2549,8 +3542,8 @@ function makeSponsoredCallsCallback(deps) {
2549
3542
  { chainId }
2550
3543
  );
2551
3544
  }
2552
- const data = (0, import_viem5.encodeFunctionData)({
2553
- abi: import_viem5.erc20Abi,
3545
+ const data = (0, import_viem8.encodeFunctionData)({
3546
+ abi: import_viem8.erc20Abi,
2554
3547
  functionName: "transfer",
2555
3548
  args: [smartWallet, BigInt(amount)]
2556
3549
  });
@@ -2560,9 +3553,9 @@ function makeSponsoredCallsCallback(deps) {
2560
3553
  {
2561
3554
  version: "2.0.0",
2562
3555
  from: deps.ownerAddress,
2563
- chainId: (0, import_viem5.toHex)(chainId),
3556
+ chainId: (0, import_viem8.toHex)(chainId),
2564
3557
  atomicRequired: false,
2565
- calls: [{ to: token, value: "0x0", data }],
3558
+ calls: [{ to: token2, value: "0x0", data }],
2566
3559
  capabilities: {
2567
3560
  paymasterService: { url: absolutePaymasterUrl() }
2568
3561
  }
@@ -2601,10 +3594,10 @@ function makeSponsoredCallsCallback(deps) {
2601
3594
  function encodeMultiAgentCursor(map) {
2602
3595
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
2603
3596
  }
2604
- function decodeMultiAgentCursor(token) {
3597
+ function decodeMultiAgentCursor(token2) {
2605
3598
  let parsed;
2606
3599
  try {
2607
- const json = Buffer.from(token, "base64").toString("utf8");
3600
+ const json = Buffer.from(token2, "base64").toString("utf8");
2608
3601
  parsed = JSON.parse(json);
2609
3602
  } catch {
2610
3603
  throw new InvalidHistoryCursorError(
@@ -2624,9 +3617,9 @@ var SPONSORED_USDC_BY_CHAIN = {
2624
3617
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
2625
3618
  };
2626
3619
  var VIEM_CHAIN2 = {
2627
- 8453: import_chains2.base,
2628
- 42161: import_chains2.arbitrum,
2629
- 1: import_chains2.mainnet
3620
+ 8453: import_chains4.base,
3621
+ 42161: import_chains4.arbitrum,
3622
+ 1: import_chains4.mainnet
2630
3623
  };
2631
3624
  var SPONSORED_WETH_BY_CHAIN = {
2632
3625
  8453: "0x4200000000000000000000000000000000000006",
@@ -2654,6 +3647,7 @@ var OwneySDK = class {
2654
3647
  orgAgentConfig;
2655
3648
  orgAgentConfigPromise = null;
2656
3649
  zyfaiRpcUrls;
3650
+ yieldseekerApiBaseUrl;
2657
3651
  routingApiBaseUrl;
2658
3652
  referralSource;
2659
3653
  cachedSponsoredCallback = null;
@@ -2665,6 +3659,7 @@ var OwneySDK = class {
2665
3659
  this.apiKey = config.apiKey;
2666
3660
  if (config.debug) setOwneyDebug(true);
2667
3661
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
3662
+ this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
2668
3663
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2669
3664
  this.paymasterServiceUrl = config.paymasterServiceUrl;
2670
3665
  this.referralSource = config.referralSource;
@@ -2767,14 +3762,14 @@ var OwneySDK = class {
2767
3762
  // Casts work around viem's chain-narrowed Client vs the generic
2768
3763
  // PublicClient/WalletClient param types — structurally identical at
2769
3764
  // runtime, but the two share a name TS treats as unrelated.
2770
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
3765
+ getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
2771
3766
  chain: VIEM_CHAIN2[cid],
2772
- transport: (0, import_viem6.custom)(provider)
3767
+ transport: (0, import_viem9.custom)(provider)
2773
3768
  }),
2774
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
3769
+ getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
2775
3770
  account: owner,
2776
3771
  chain: VIEM_CHAIN2[cid],
2777
- transport: (0, import_viem6.custom)(provider)
3772
+ transport: (0, import_viem9.custom)(provider)
2778
3773
  })
2779
3774
  });
2780
3775
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -2820,14 +3815,14 @@ var OwneySDK = class {
2820
3815
  // Casts work around viem's chain-narrowed Client vs the generic
2821
3816
  // PublicClient/WalletClient param types — structurally identical at
2822
3817
  // runtime, but the two share a name TS treats as unrelated.
2823
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
3818
+ getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
2824
3819
  chain: VIEM_CHAIN2[cid],
2825
- transport: (0, import_viem6.custom)(provider)
3820
+ transport: (0, import_viem9.custom)(provider)
2826
3821
  }),
2827
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
3822
+ getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
2828
3823
  account: owner,
2829
3824
  chain: VIEM_CHAIN2[cid],
2830
- transport: (0, import_viem6.custom)(provider)
3825
+ transport: (0, import_viem9.custom)(provider)
2831
3826
  })
2832
3827
  });
2833
3828
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -2858,12 +3853,10 @@ var OwneySDK = class {
2858
3853
  this.orgAgentConfigPromise = fetchOrgAgentConfig(
2859
3854
  this.apiKey,
2860
3855
  this.routingApiBaseUrl
2861
- ).then(
2862
- (config) => {
2863
- this.orgAgentConfig = config;
2864
- return config;
2865
- }
2866
- );
3856
+ ).then((config) => {
3857
+ this.orgAgentConfig = config;
3858
+ return config;
3859
+ });
2867
3860
  }
2868
3861
  return this.orgAgentConfigPromise;
2869
3862
  }
@@ -2903,7 +3896,14 @@ var OwneySDK = class {
2903
3896
  this.routingApiBaseUrl
2904
3897
  );
2905
3898
  this.disabledAgents.clear();
2906
- 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;
2907
3907
  const agent = this.createAgent(agent_type, key2);
2908
3908
  if (!agent) continue;
2909
3909
  this.agents.set(agent_type, agent);
@@ -2927,10 +3927,47 @@ var OwneySDK = class {
2927
3927
  }
2928
3928
  createAgent(agentId, key2) {
2929
3929
  if (agentId === "zyfai") {
3930
+ if (!key2) return null;
2930
3931
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2931
3932
  }
3933
+ if (agentId === "yieldseeker") {
3934
+ return new YieldseekerAgent(this.apiKey, {
3935
+ baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
3936
+ });
3937
+ }
2932
3938
  return null;
2933
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
+ }
2934
3971
  // --- Account lifecycle ---
2935
3972
  /**
2936
3973
  * Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
@@ -2942,7 +3979,7 @@ var OwneySDK = class {
2942
3979
  * If provided, ALL specified agents must support the chainId or the call
2943
3980
  * throws before activating any agent.
2944
3981
  */
2945
- async activateAgent(chainId, agentId) {
3982
+ async activateAgent(chainId, agentId, asset) {
2946
3983
  const state = this.requireState();
2947
3984
  await this.ensureAgentsInitialized();
2948
3985
  if (agentId !== void 0) {
@@ -2978,9 +4015,7 @@ var OwneySDK = class {
2978
4015
  this.activeAgents.add(id);
2979
4016
  }
2980
4017
  state.chainId = chainId;
2981
- this.activateAgentsInTurn(agents, state, chainId).catch((error) => {
2982
- console.error("activateAgent background init failed:", error);
2983
- });
4018
+ await this.activateAgentsInTurn(agents, state, chainId, asset);
2984
4019
  return;
2985
4020
  }
2986
4021
  const compatible = [...this.agents.values()].filter(
@@ -3001,11 +4036,7 @@ var OwneySDK = class {
3001
4036
  const enabledCompatible = compatible.filter(
3002
4037
  (agent) => !this.isAgentDisabled(agent.id)
3003
4038
  );
3004
- this.activateAgentsInTurn(enabledCompatible, state, chainId).catch(
3005
- (error) => {
3006
- console.error("activateAgent background init failed:", error);
3007
- }
3008
- );
4039
+ await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
3009
4040
  }
3010
4041
  /**
3011
4042
  * Activate agents ONE AT A TIME, each followed by its org policy.
@@ -3025,11 +4056,11 @@ var OwneySDK = class {
3025
4056
  * rethrown (matching the previous `Promise.all` rejection) once all agents
3026
4057
  * have had a chance to activate.
3027
4058
  */
3028
- async activateAgentsInTurn(agents, state, chainId) {
4059
+ async activateAgentsInTurn(agents, state, chainId, asset) {
3029
4060
  let firstError = null;
3030
4061
  for (const agent of agents) {
3031
4062
  try {
3032
- await agent.activateAgent(state, chainId);
4063
+ await agent.activateAgent(state, chainId, asset);
3033
4064
  await this.applyOrgPolicyTo(agent, state, chainId);
3034
4065
  } catch (error) {
3035
4066
  if (firstError === null) {
@@ -3238,10 +4269,10 @@ var OwneySDK = class {
3238
4269
  try {
3239
4270
  const balance = await agent.getBalances(state, chainId);
3240
4271
  const target = asset.toLowerCase();
3241
- const token = balance.tokens.find(
4272
+ const token2 = balance.tokens.find(
3242
4273
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3243
4274
  );
3244
- return !!token && Number(token.amount) > 0;
4275
+ return !!token2 && Number(token2.amount) > 0;
3245
4276
  } catch {
3246
4277
  return false;
3247
4278
  }
@@ -3292,9 +4323,9 @@ var OwneySDK = class {
3292
4323
  const { asset, amount, agentId } = options;
3293
4324
  const state = this.requireState();
3294
4325
  const chainId = this.requireChainId();
3295
- const token = asset;
4326
+ const token2 = asset;
3296
4327
  const assetInfo = SupportedAssets.find(
3297
- (a) => a.chainId === chainId && a.symbol === token
4328
+ (a) => a.chainId === chainId && a.symbol === token2
3298
4329
  );
3299
4330
  if (!assetInfo) {
3300
4331
  throw new OwneyError(
@@ -3308,7 +4339,7 @@ var OwneySDK = class {
3308
4339
  return withFailureReporting(
3309
4340
  this.apiKey,
3310
4341
  agent.id,
3311
- () => agent.withdraw(state, chainId, token, amount),
4342
+ () => agent.withdraw(state, chainId, token2, amount),
3312
4343
  this.routingApiBaseUrl
3313
4344
  );
3314
4345
  }
@@ -3318,7 +4349,7 @@ var OwneySDK = class {
3318
4349
  const agentErrors2 = {};
3319
4350
  for (const agent of eligibleAgents) {
3320
4351
  try {
3321
- results2[agent.id] = await agent.withdraw(state, chainId, token);
4352
+ results2[agent.id] = await agent.withdraw(state, chainId, token2);
3322
4353
  } catch (err) {
3323
4354
  console.error(`withdraw failed for agent "${agent.id}":`, err);
3324
4355
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
@@ -3345,6 +4376,10 @@ var OwneySDK = class {
3345
4376
  }
3346
4377
  const requested = BigInt(amount);
3347
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);
3348
4383
  const balances = projectAgentBalancesForAsset(
3349
4384
  eligibleAgents,
3350
4385
  aggregated.agentBalances,
@@ -3353,7 +4388,18 @@ var OwneySDK = class {
3353
4388
  assetInfo.decimals
3354
4389
  );
3355
4390
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
3356
- 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) {
3357
4403
  throw new OwneyError(
3358
4404
  "WITHDRAW_INSUFFICIENT_BALANCE",
3359
4405
  `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
@@ -3364,6 +4410,7 @@ var OwneySDK = class {
3364
4410
  }
3365
4411
  );
3366
4412
  }
4413
+ const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
3367
4414
  const disabledBalances = balances.filter(
3368
4415
  (b) => this.isAgentDisabled(b.agent.id)
3369
4416
  );
@@ -3372,7 +4419,7 @@ var OwneySDK = class {
3372
4419
  );
3373
4420
  const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
3374
4421
  disabledBalances,
3375
- requested
4422
+ plannedTarget
3376
4423
  );
3377
4424
  const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
3378
4425
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
@@ -3382,7 +4429,9 @@ var OwneySDK = class {
3382
4429
  }));
3383
4430
  const plans = [...disabledPlans, ...enabledPlans];
3384
4431
  const results = {};
3385
- const agentErrors = {};
4432
+ const agentErrors = {
4433
+ ...aggregated.agentErrors ?? {}
4434
+ };
3386
4435
  for (let i = 0; i < plans.length; i++) {
3387
4436
  const p = plans[i];
3388
4437
  if (p.planned === 0n) continue;
@@ -3390,7 +4439,7 @@ var OwneySDK = class {
3390
4439
  results[p.agent.id] = await p.agent.withdraw(
3391
4440
  state,
3392
4441
  chainId,
3393
- token,
4442
+ token2,
3394
4443
  p.planned.toString()
3395
4444
  );
3396
4445
  } catch (err) {
@@ -3429,7 +4478,8 @@ var OwneySDK = class {
3429
4478
  requested: amount,
3430
4479
  withdrawn: withdrawn.toString(),
3431
4480
  partialResults: results,
3432
- agentErrors
4481
+ agentErrors,
4482
+ ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
3433
4483
  }
3434
4484
  );
3435
4485
  }
@@ -3470,7 +4520,12 @@ var OwneySDK = class {
3470
4520
  continue;
3471
4521
  }
3472
4522
  const reason = settledResult.reason;
3473
- 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
+ );
3474
4529
  }
3475
4530
  if (successCount === 0) {
3476
4531
  throw new OwneyError(
@@ -3482,7 +4537,8 @@ var OwneySDK = class {
3482
4537
  return {
3483
4538
  totalBalance: String(totalBalance),
3484
4539
  totalBalanceAsset: "usdc",
3485
- agentBalances: results
4540
+ agentBalances: results,
4541
+ ...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
3486
4542
  };
3487
4543
  }
3488
4544
  /**
@@ -3576,7 +4632,10 @@ var OwneySDK = class {
3576
4632
  Promise.all(
3577
4633
  entries.map(async ([id, agent]) => {
3578
4634
  const b = await agent.getBalances(state, chainId);
3579
- return [id, Number(b.totalBalance)];
4635
+ return [
4636
+ id,
4637
+ balanceForApyScope(b, chainId, tokenSymbol)
4638
+ ];
3580
4639
  })
3581
4640
  )
3582
4641
  ]);
@@ -3604,10 +4663,12 @@ var OwneySDK = class {
3604
4663
  }
3605
4664
  }
3606
4665
  const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
4666
+ const history = aggregateApyHistory(results, balances);
3607
4667
  return {
3608
4668
  totalApy: String(totalApy),
3609
4669
  agentApy: results,
3610
- apyByChainAndAsset
4670
+ apyByChainAndAsset,
4671
+ history
3611
4672
  };
3612
4673
  }
3613
4674
  /**
@@ -3729,30 +4790,30 @@ var OwneySDK = class {
3729
4790
  const state = this.requireState();
3730
4791
  const chainId = this.requireChainId();
3731
4792
  this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
3732
- const token = SPONSORED_WETH_BY_CHAIN[chainId];
3733
- if (!token) {
4793
+ const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
4794
+ if (!token2) {
3734
4795
  throw new OwneyError(
3735
4796
  "CHAIN_UNSUPPORTED",
3736
4797
  `No sponsored WETH on chain ${chainId}`
3737
4798
  );
3738
4799
  }
3739
4800
  const provider = this.requireConnectedProvider();
3740
- const wallet = (0, import_viem6.createWalletClient)({
4801
+ const wallet = (0, import_viem9.createWalletClient)({
3741
4802
  account: state.walletAddress,
3742
4803
  chain: VIEM_CHAIN2[chainId],
3743
- transport: (0, import_viem6.custom)(provider)
4804
+ transport: (0, import_viem9.custom)(provider)
3744
4805
  });
3745
4806
  const hash = await wallet.writeContract({
3746
- address: token,
4807
+ address: token2,
3747
4808
  abi: ERC20_ALLOWANCE_ABI,
3748
4809
  functionName: "approve",
3749
4810
  args: [PERMIT2_ADDRESS, MAX_UINT256],
3750
4811
  account: state.walletAddress,
3751
4812
  chain: VIEM_CHAIN2[chainId]
3752
4813
  });
3753
- const publicClient = (0, import_viem6.createPublicClient)({
4814
+ const publicClient = (0, import_viem9.createPublicClient)({
3754
4815
  chain: VIEM_CHAIN2[chainId],
3755
- transport: (0, import_viem6.custom)(provider)
4816
+ transport: (0, import_viem9.custom)(provider)
3756
4817
  });
3757
4818
  const receipt = await publicClient.waitForTransactionReceipt({
3758
4819
  hash,
@@ -3788,7 +4849,9 @@ var OwneySDK = class {
3788
4849
  return this.getAgent(agentId).getAgentApy(days, agentOptions);
3789
4850
  }
3790
4851
  const results = {};
3791
- const agentEntries = [...this.agents.entries()];
4852
+ const agentEntries = [...this.agents.entries()].filter(
4853
+ ([id]) => !this.isAgentDisabled(id)
4854
+ );
3792
4855
  const apyResults = await Promise.all(
3793
4856
  agentEntries.map(async ([id, agent]) => {
3794
4857
  const apy = await agent.getAgentApy(days, agentOptions);
@@ -3861,13 +4924,13 @@ var OwneySDK = class {
3861
4924
  };
3862
4925
 
3863
4926
  // src/agents/zyfai/zyfai.siwx.ts
3864
- var import_viem7 = require("viem");
3865
- var import_siwe = require("siwe");
4927
+ var import_viem10 = require("viem");
4928
+ var import_siwe2 = require("siwe");
3866
4929
  var import_sdk2 = require("@zyfai/sdk");
3867
4930
 
3868
4931
  // src/agents/zyfai/zyfai.siwx-cache.ts
3869
- var KEY_PREFIX2 = "owney.siwx.session";
3870
- var storage2 = () => {
4932
+ var KEY_PREFIX3 = "owney.siwx.session";
4933
+ var storage3 = () => {
3871
4934
  if (typeof window === "undefined") return null;
3872
4935
  try {
3873
4936
  return window.localStorage;
@@ -3875,8 +4938,8 @@ var storage2 = () => {
3875
4938
  return null;
3876
4939
  }
3877
4940
  };
3878
- var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
3879
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
4941
+ var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
4942
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
3880
4943
  var memorySiwxSessions = /* @__PURE__ */ new Map();
3881
4944
  var readLegacySiwxSession = (store, address) => {
3882
4945
  if (!store) return null;
@@ -3907,8 +4970,8 @@ var readLegacySiwxSession = (store, address) => {
3907
4970
  };
3908
4971
  var readSiwxSession = (address, chainId) => {
3909
4972
  if (typeof window === "undefined") return null;
3910
- const key2 = buildKey2(address);
3911
- const store = storage2();
4973
+ const key2 = buildKey3(address);
4974
+ const store = storage3();
3912
4975
  let raw = null;
3913
4976
  try {
3914
4977
  raw = store?.getItem(key2) ?? null;
@@ -3936,18 +4999,18 @@ var readSiwxSession = (address, chainId) => {
3936
4999
  };
3937
5000
  var writeSiwxSession = (address, _chainId, session) => {
3938
5001
  if (typeof window === "undefined") return;
3939
- const key2 = buildKey2(address);
5002
+ const key2 = buildKey3(address);
3940
5003
  memorySiwxSessions.set(key2, session);
3941
- const store = storage2();
5004
+ const store = storage3();
3942
5005
  try {
3943
5006
  store?.setItem(key2, JSON.stringify(session));
3944
5007
  } catch {
3945
5008
  }
3946
5009
  };
3947
5010
  var clearSiwxSession = (address, _chainId) => {
3948
- const key2 = buildKey2(address);
5011
+ const key2 = buildKey3(address);
3949
5012
  memorySiwxSessions.delete(key2);
3950
- const store = storage2();
5013
+ const store = storage3();
3951
5014
  try {
3952
5015
  store?.removeItem(key2);
3953
5016
  } catch {
@@ -3987,8 +5050,8 @@ function buildSIWXConfig(deps) {
3987
5050
  statement: STATEMENT,
3988
5051
  issuedAt,
3989
5052
  toString() {
3990
- return new import_siwe.SiweMessage({
3991
- address: (0, import_viem7.getAddress)(accountAddress),
5053
+ return new import_siwe2.SiweMessage({
5054
+ address: (0, import_viem10.getAddress)(accountAddress),
3992
5055
  chainId: numericChainId(chainId),
3993
5056
  domain,
3994
5057
  uri,
@@ -4030,7 +5093,7 @@ function buildSIWXConfig(deps) {
4030
5093
  const persistSession = async (session) => {
4031
5094
  const address = session.data.accountAddress;
4032
5095
  const id = numericChainId(session.data.chainId);
4033
- const message = new import_siwe.SiweMessage(session.message);
5096
+ const message = new import_siwe2.SiweMessage(session.message);
4034
5097
  const login = await post("/auth/login", {
4035
5098
  message,
4036
5099
  signature: session.signature,
@@ -4079,6 +5142,7 @@ function createOwneySIWX(config) {
4079
5142
  NotConnectedError,
4080
5143
  OwneyError,
4081
5144
  OwneySDK,
5145
+ YieldseekerAgent,
4082
5146
  createOwneySIWX,
4083
5147
  setOwneyDebug
4084
5148
  });