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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -50,23 +50,6 @@ var SupportedAssets = [
50
50
  }
51
51
  ];
52
52
 
53
- // src/lib/debug.ts
54
- var configuredDebug = false;
55
- function setOwneyDebug(enabled) {
56
- configuredDebug = enabled;
57
- }
58
- function isOwneyDebug() {
59
- return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
60
- }
61
- function debugLog(scope, message, data) {
62
- if (!isOwneyDebug()) return;
63
- if (data === void 0) {
64
- console.log(`[${scope}] ${message}`);
65
- } else {
66
- console.log(`[${scope}] ${message}`, data);
67
- }
68
- }
69
-
70
53
  // src/errors.ts
71
54
  var OwneyError = class extends Error {
72
55
  code;
@@ -266,7 +249,7 @@ function mapBalances(raw, _chainId, smartWallet) {
266
249
  )
267
250
  )
268
251
  ),
269
- apy: netApy(p.pool_apy_withFee, p.pool_apy, "pool_apy_withFee"),
252
+ apy: p.pool_apy,
270
253
  tvl: p.pool_tvl,
271
254
  // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
272
255
  // structural read keeps it undefined today and auto-populates later.
@@ -323,27 +306,14 @@ function mapWeightedApyByChain(raw) {
323
306
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
324
307
  const perAsset = {};
325
308
  for (const [symbol, apy] of Object.entries(tokenApy)) {
326
- const token = symbol;
327
- if (!SUPPORTED_TOKENS.includes(token)) continue;
328
- perAsset[token] = apy;
309
+ const token2 = symbol;
310
+ if (!SUPPORTED_TOKENS.includes(token2)) continue;
311
+ perAsset[token2] = apy;
329
312
  }
330
313
  if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
331
314
  }
332
315
  return Object.keys(out).length > 0 ? out : void 0;
333
316
  }
334
- var warnedGrossApyFallbacks = /* @__PURE__ */ new Set();
335
- function netApy(net, gross, field) {
336
- if (net !== void 0 && net !== null) return net;
337
- if (gross === void 0 || gross === null) return void 0;
338
- if (!warnedGrossApyFallbacks.has(field)) {
339
- warnedGrossApyFallbacks.add(field);
340
- console.warn(
341
- `[owney] @zyfai/sdk omitted "${field}"; falling back to the gross APY, which does not deduct Zyfai's performance fee and so reads high.`
342
- );
343
- }
344
- debugLog("zyfai:apy", `gross fallback for ${field}`, { gross });
345
- return gross;
346
- }
347
317
  function rawPoolApyForChain(entry, chainId, tokenSymbol) {
348
318
  let weightedSum = 0;
349
319
  let totalBalance = 0;
@@ -351,9 +321,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
351
321
  if (p.chainId !== chainId) continue;
352
322
  if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
353
323
  const balance = p.balance ?? 0;
354
- const apy = netApy(p.apy_withFee, p.apy, "apy_withFee");
355
- if (balance <= 0 || typeof apy !== "number") continue;
356
- weightedSum += apy * balance;
324
+ if (balance <= 0 || typeof p.apy !== "number") continue;
325
+ weightedSum += p.apy * balance;
357
326
  totalBalance += balance;
358
327
  }
359
328
  return totalBalance > 0 ? weightedSum / totalBalance : null;
@@ -401,12 +370,8 @@ function mapEntries(rawEntries, chainId) {
401
370
  const matched = positions.find(
402
371
  (p) => p.protocol_name ? rawLog.newOpportunity.includes(`${p.protocol_name} (${p.pool})`) : false
403
372
  );
404
- oldApy = Number(
405
- netApy(rawLog.oldApy_withFee, rawLog.oldApy, "oldApy_withFee")
406
- );
407
- newApy = Number(
408
- netApy(rawLog.newApy_withFee, rawLog.newApy, "newApy_withFee")
409
- );
373
+ oldApy = Number(rawLog.oldApy);
374
+ newApy = Number(rawLog.newApy);
410
375
  const from = splitOpportunity(rawLog.oldOpportunity);
411
376
  const to = splitOpportunity(rawLog.newOpportunity);
412
377
  rebalanceLog = [
@@ -486,11 +451,7 @@ function mapApyByStrategy(raw) {
486
451
  const chainKey = supported.chainId;
487
452
  const symbolKey = supported.symbol;
488
453
  const bucket = apyPerAsset[chainKey] ?? {};
489
- const apy = netApy(
490
- entry.average_apy_withFee,
491
- entry.average_apy,
492
- "average_apy_withFee"
493
- ) ?? entry.average_apy;
454
+ const apy = entry.average_apy_with_fee ?? entry.average_apy;
494
455
  bucket[symbolKey] = apy;
495
456
  apyPerAsset[chainKey] = bucket;
496
457
  apySum += apy;
@@ -516,23 +477,23 @@ function computeAllocationApy(positions) {
516
477
  weightedSum += apy * value;
517
478
  totalValue += value;
518
479
  const chainId = resolveChainId(p.chain);
519
- const token = p.asset;
480
+ const token2 = p.asset;
520
481
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
521
- if (!SUPPORTED_TOKENS.includes(token)) continue;
482
+ if (!SUPPORTED_TOKENS.includes(token2)) continue;
522
483
  const perAsset = buckets[chainId] ?? {};
523
- const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
484
+ const cell = perAsset[token2] ?? { weightedSum: 0, value: 0 };
524
485
  cell.weightedSum += apy * value;
525
486
  cell.value += value;
526
- perAsset[token] = cell;
487
+ perAsset[token2] = cell;
527
488
  buckets[chainId] = perAsset;
528
489
  }
529
490
  const apyByChainAndAsset = {};
530
491
  for (const [chainKey, perAsset] of Object.entries(buckets)) {
531
492
  const chainId = Number(chainKey);
532
493
  const out = {};
533
- for (const [token, cell] of Object.entries(perAsset ?? {})) {
494
+ for (const [token2, cell] of Object.entries(perAsset ?? {})) {
534
495
  if (cell && cell.value > 0) {
535
- out[token] = cell.weightedSum / cell.value;
496
+ out[token2] = cell.weightedSum / cell.value;
536
497
  }
537
498
  }
538
499
  if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
@@ -546,9 +507,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
546
507
  function topicToAddress(topic) {
547
508
  return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
548
509
  }
549
- function extractWithdrawnAmount(logs, recipient, token, decimals) {
510
+ function extractWithdrawnAmount(logs, recipient, token2, decimals) {
550
511
  const wantRecipient = recipient.toLowerCase();
551
- const wantToken = token.toLowerCase();
512
+ const wantToken = token2.toLowerCase();
552
513
  let total = 0n;
553
514
  let matched = false;
554
515
  for (const log of logs) {
@@ -574,11 +535,11 @@ var InvalidHistoryCursorError = class extends Error {
574
535
  function encodeHistoryCursor(payload) {
575
536
  return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
576
537
  }
577
- function decodeHistoryCursor(token) {
578
- if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
538
+ function decodeHistoryCursor(token2) {
539
+ if (!token2) throw new InvalidHistoryCursorError("Cursor is empty.");
579
540
  let parsed;
580
541
  try {
581
- const json = Buffer.from(token, "base64").toString("utf8");
542
+ const json = Buffer.from(token2, "base64").toString("utf8");
582
543
  parsed = JSON.parse(json);
583
544
  } catch {
584
545
  throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
@@ -601,8 +562,8 @@ var storage = () => {
601
562
  };
602
563
  var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
603
564
  var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
604
- var isJwtExpired = (token) => {
605
- const parts = token.split(".");
565
+ var isJwtExpired = (token2) => {
566
+ const parts = token2.split(".");
606
567
  if (parts.length !== 3) return false;
607
568
  try {
608
569
  const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
@@ -850,6 +811,23 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
850
811
  return !protocolListsEqual(current.protocols, desiredProtocols);
851
812
  }
852
813
 
814
+ // src/lib/debug.ts
815
+ var configuredDebug = false;
816
+ function setOwneyDebug(enabled) {
817
+ configuredDebug = enabled;
818
+ }
819
+ function isOwneyDebug() {
820
+ return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
821
+ }
822
+ function debugLog(scope, message, data) {
823
+ if (!isOwneyDebug()) return;
824
+ if (data === void 0) {
825
+ console.log(`[${scope}] ${message}`);
826
+ } else {
827
+ console.log(`[${scope}] ${message}`, data);
828
+ }
829
+ }
830
+
853
831
  // src/agents/zyfai/zyfai.agent.ts
854
832
  var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi([
855
833
  "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
@@ -1687,20 +1665,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
1687
1665
  }
1688
1666
  }
1689
1667
  // --- IAgent: Fund operations ---
1690
- async withdraw(state, chainId, token, amount) {
1668
+ async withdraw(state, chainId, token2, amount) {
1691
1669
  const validChainId = isValidChainId(chainId);
1692
1670
  await this.ensureConnected(state, validChainId);
1693
1671
  const raw = await this.sdk.withdrawFunds(
1694
1672
  this.getAddress(),
1695
1673
  validChainId,
1696
1674
  amount,
1697
- token
1675
+ token2
1698
1676
  );
1699
1677
  if (!raw.success) {
1700
1678
  throw new OwneyError(
1701
1679
  "WITHDRAW_FAILED",
1702
1680
  raw.message || "Zyfai withdraw failed.",
1703
- { chainId: validChainId, token, amount, response: raw },
1681
+ { chainId: validChainId, token: token2, amount, response: raw },
1704
1682
  this.id
1705
1683
  );
1706
1684
  }
@@ -1851,349 +1829,176 @@ var ZyfaiAgent = class _ZyfaiAgent {
1851
1829
  }
1852
1830
  };
1853
1831
 
1854
- // src/lib/routing-api.ts
1855
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1856
- async function fetchOrgAgentConfig(apiKey) {
1857
- const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
1832
+ // src/agents/yieldseeker/yieldseeker.agent.ts
1833
+ import {
1834
+ createPublicClient as createPublicClient3,
1835
+ createWalletClient as createWalletClient2,
1836
+ custom as custom2,
1837
+ getAddress as getAddress2,
1838
+ isAddress,
1839
+ parseUnits
1840
+ } from "viem";
1841
+ import { base as base3 } from "viem/chains";
1842
+
1843
+ // src/lib/chain-guard.ts
1844
+ var CHAIN_NAMES = {
1845
+ 1: "Ethereum",
1846
+ 8453: "Base",
1847
+ 42161: "Arbitrum"
1848
+ };
1849
+ function chainName(chainId) {
1850
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
1851
+ }
1852
+ async function ensureWalletOnChain(pub, wallet, expected) {
1853
+ const actual = await pub.getChainId();
1854
+ if (actual === expected) return;
1858
1855
  try {
1859
- const res = await fetch(url, {
1860
- method: "GET",
1861
- headers: {
1862
- "Content-Type": "application/json",
1863
- "x-owney-api-key": `${apiKey}`
1864
- }
1865
- });
1866
- if (!res.ok) {
1867
- if (res.status !== 404) {
1868
- console.warn(
1869
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
1870
- );
1856
+ await wallet.switchChain({ id: expected });
1857
+ } catch (error) {
1858
+ throw new OwneyError(
1859
+ "CHAIN_MISMATCH",
1860
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
1861
+ {
1862
+ expectedChainId: expected,
1863
+ actualChainId: actual,
1864
+ cause: error instanceof Error ? error.message : String(error)
1871
1865
  }
1872
- return null;
1873
- }
1874
- const json = await res.json();
1875
- const policy = json.success ? json.data ?? null : null;
1876
- debugLog(
1877
- "owney-sdk",
1878
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
1879
- policy ?? void 0
1880
1866
  );
1881
- return policy;
1882
- } catch (error) {
1883
- console.warn(
1884
- "[owney-sdk] Could not read org agent config (non-fatal):",
1885
- error instanceof Error ? error.message : String(error)
1867
+ }
1868
+ const after = await pub.getChainId();
1869
+ if (after !== expected) {
1870
+ throw new OwneyError(
1871
+ "CHAIN_MISMATCH",
1872
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
1873
+ { expectedChainId: expected, actualChainId: after }
1886
1874
  );
1887
- return null;
1888
1875
  }
1889
1876
  }
1890
- async function fetchAgentKeys(apiKey) {
1891
- const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
1892
- const res = await fetch(url, {
1893
- method: "GET",
1894
- headers: {
1895
- "Content-Type": "application/json",
1896
- "x-owney-api-key": `${apiKey}`
1897
- }
1898
- });
1899
- if (!res.ok) {
1900
- const text = await res.text().catch(() => "");
1877
+
1878
+ // src/lib/transfer-auth.ts
1879
+ import { bytesToHex } from "viem";
1880
+ var ERC20_META_ABI = [
1881
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
1882
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
1883
+ ];
1884
+ function buildTransferWithAuthorizationTypedData(input) {
1885
+ return {
1886
+ domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
1887
+ types: {
1888
+ TransferWithAuthorization: [
1889
+ { name: "from", type: "address" },
1890
+ { name: "to", type: "address" },
1891
+ { name: "value", type: "uint256" },
1892
+ { name: "validAfter", type: "uint256" },
1893
+ { name: "validBefore", type: "uint256" },
1894
+ { name: "nonce", type: "bytes32" }
1895
+ ]
1896
+ },
1897
+ primaryType: "TransferWithAuthorization",
1898
+ message: input.message
1899
+ };
1900
+ }
1901
+ async function readTokenMeta(publicClient, token2) {
1902
+ const [tokenName, tokenVersion] = await Promise.all([
1903
+ publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "name" }),
1904
+ publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1905
+ ]);
1906
+ return { tokenName, tokenVersion };
1907
+ }
1908
+ function randomAuthNonce() {
1909
+ const bytes = new Uint8Array(32);
1910
+ globalThis.crypto.getRandomValues(bytes);
1911
+ return bytesToHex(bytes);
1912
+ }
1913
+
1914
+ // src/lib/sponsor-client.ts
1915
+ var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1916
+ async function postSponsorTransferAuth(input) {
1917
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1918
+ let res;
1919
+ try {
1920
+ res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
1921
+ method: "POST",
1922
+ headers: {
1923
+ "content-type": "application/json",
1924
+ "x-owney-api-key": input.apiKey,
1925
+ ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
1926
+ },
1927
+ body: JSON.stringify(input.body)
1928
+ });
1929
+ } catch (networkError) {
1901
1930
  throw new OwneyError(
1902
- "API_ROUTING_ERROR",
1903
- `Routing API error ${res.status}: ${text}`,
1904
- { statusCode: res.status, responseBody: text }
1931
+ "SPONSOR_REQUEST_FAILED",
1932
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1933
+ { cause: String(networkError) }
1905
1934
  );
1906
1935
  }
1907
- const json = await res.json();
1908
- if (!json.success) {
1936
+ const text = await res.text();
1937
+ let parsed = null;
1938
+ try {
1939
+ parsed = JSON.parse(text);
1940
+ } catch {
1941
+ }
1942
+ if (!res.ok || !parsed?.success || !parsed.data) {
1909
1943
  throw new OwneyError(
1910
- "API_ROUTING_FAILED",
1911
- `Routing API request failed: ${json.message}`,
1912
- { message: json.message }
1944
+ "SPONSOR_REQUEST_FAILED",
1945
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1946
+ {
1947
+ statusCode: res.status,
1948
+ responseBody: text.slice(0, 500),
1949
+ // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
1950
+ // before broadcast, so it is safe to fall back to a user-paid deposit.
1951
+ safeToFallback: res.status === 503
1952
+ }
1913
1953
  );
1914
1954
  }
1915
- return json.data;
1955
+ return parsed.data;
1916
1956
  }
1917
-
1918
- // src/lib/health-report.ts
1919
- var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1920
- async function reportAgentFailure(apiKey, agentType, errorCode) {
1957
+ async function postSponsorPermit2Transfer(input) {
1958
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1959
+ let res;
1921
1960
  try {
1922
- await fetch(`${ROUTING_API_BASE_URL2}/api/v1/agent/health-report`, {
1961
+ res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
1923
1962
  method: "POST",
1924
1963
  headers: {
1925
- "Content-Type": "application/json",
1926
- "x-owney-api-key": apiKey
1964
+ "content-type": "application/json",
1965
+ "x-owney-api-key": input.apiKey,
1966
+ ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
1927
1967
  },
1928
- body: JSON.stringify({
1929
- agent_type: agentType,
1930
- error_code: errorCode,
1931
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1932
- })
1968
+ body: JSON.stringify(input.body)
1933
1969
  });
1934
- } catch (err) {
1935
- console.warn(
1936
- `[owney-sdk] health-report failed for agent "${agentType}":`,
1937
- err instanceof Error ? err.message : err
1970
+ } catch (networkError) {
1971
+ throw new OwneyError(
1972
+ "SPONSOR_REQUEST_FAILED",
1973
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1974
+ { cause: String(networkError), safeToFallback: false }
1938
1975
  );
1939
1976
  }
1940
- }
1941
- async function withFailureReporting(apiKey, agentType, fn) {
1977
+ const text = await res.text();
1978
+ let parsed = null;
1942
1979
  try {
1943
- return await fn();
1944
- } catch (err) {
1945
- const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
1946
- void reportAgentFailure(apiKey, agentType, errorCode);
1947
- throw err;
1980
+ parsed = JSON.parse(text);
1981
+ } catch {
1948
1982
  }
1949
- }
1950
-
1951
- // src/lib/helpers/withdraw-helper.ts
1952
- import { parseUnits } from "viem";
1953
- function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1954
- const target = asset.toUpperCase();
1955
- return agents.map((agent) => {
1956
- const agentBalance = aggregated[agent.id];
1957
- const tokenBalance = agentBalance?.tokens.find(
1958
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
1983
+ if (!res.ok || !parsed?.success || !parsed.data) {
1984
+ throw new OwneyError(
1985
+ "SPONSOR_REQUEST_FAILED",
1986
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1987
+ {
1988
+ statusCode: res.status,
1989
+ responseBody: text.slice(0, 500),
1990
+ safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
1991
+ }
1959
1992
  );
1960
- if (!tokenBalance) return { agent, balance: 0n };
1961
- return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
1962
- });
1963
- }
1964
- function planProportionalShares(balances, requested, totalAvailable) {
1965
- const plans = balances.map(({ agent, balance }) => ({
1966
- agent,
1967
- balance,
1968
- planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
1969
- }));
1970
- const assigned = plans.reduce((s, p) => s + p.planned, 0n);
1971
- let remainder = requested - assigned;
1972
- const byHeadroom = [...plans].sort((a, b) => {
1973
- const diff = b.balance - b.planned - (a.balance - a.planned);
1974
- return diff > 0n ? 1 : diff < 0n ? -1 : 0;
1975
- });
1976
- for (const p of byHeadroom) {
1977
- if (remainder === 0n) break;
1978
- const headroom = p.balance - p.planned;
1979
- if (headroom <= 0n) continue;
1980
- const take = headroom < remainder ? headroom : remainder;
1981
- p.planned += take;
1982
- remainder -= take;
1983
1993
  }
1984
- return plans;
1985
- }
1986
- function planDisabledDrain(disabled, requested) {
1987
- const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
1988
- const plans = [];
1989
- let remaining = requested;
1990
- for (const { agent, balance } of sorted) {
1991
- if (remaining === 0n) {
1992
- plans.push({ agent, balance, planned: 0n });
1993
- continue;
1994
- }
1995
- const take = balance < remaining ? balance : remaining;
1996
- plans.push({ agent, balance, planned: take });
1997
- remaining -= take;
1998
- }
1999
- return { plans, remaining };
2000
- }
2001
- function redistributeShare(plans, fromIndex, amount, candidatePool) {
2002
- const pool = candidatePool ?? plans.slice(fromIndex + 1);
2003
- const candidates = pool.filter((c) => c.balance - c.planned > 0n);
2004
- const totalHeadroom = candidates.reduce(
2005
- (s, c) => s + (c.balance - c.planned),
2006
- 0n
2007
- );
2008
- if (totalHeadroom === 0n) return;
2009
- let distributed = 0n;
2010
- for (const c of candidates) {
2011
- const headroom = c.balance - c.planned;
2012
- const proportional = headroom * amount / totalHeadroom;
2013
- const give = proportional > headroom ? headroom : proportional;
2014
- c.planned += give;
2015
- distributed += give;
2016
- }
2017
- let leftover = amount - distributed;
2018
- for (const c of candidates) {
2019
- if (leftover === 0n) break;
2020
- const headroom = c.balance - c.planned;
2021
- if (headroom <= 0n) continue;
2022
- const take = headroom < leftover ? headroom : leftover;
2023
- c.planned += take;
2024
- leftover -= take;
2025
- }
2026
- }
2027
- function sumWithdrawnAmount(results) {
2028
- return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
2029
- }
2030
-
2031
- // src/lib/helpers/account-apy-helper.ts
2032
- function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2033
- const sums = {};
2034
- const weights = {};
2035
- for (const id of Object.keys(agentApys)) {
2036
- const cells = agentApys[id].apyByChainAndAsset;
2037
- const balance = agentBalances[id] ?? 0;
2038
- if (!cells || balance <= 0) continue;
2039
- for (const [chainKey, perAsset] of Object.entries(cells)) {
2040
- if (!perAsset) continue;
2041
- const chainId = Number(chainKey);
2042
- for (const [asset, apyValue] of Object.entries(perAsset)) {
2043
- const apy = Number(apyValue ?? 0);
2044
- if (apy === 0) continue;
2045
- sums[chainId] ??= {};
2046
- weights[chainId] ??= {};
2047
- sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
2048
- weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2049
- }
2050
- }
2051
- }
2052
- const out = {};
2053
- for (const chainKey of Object.keys(sums)) {
2054
- const chainId = Number(chainKey);
2055
- const perAssetOut = {};
2056
- for (const asset of Object.keys(sums[chainId])) {
2057
- const w = weights[chainId][asset];
2058
- if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
2059
- }
2060
- if (Object.keys(perAssetOut).length > 0) {
2061
- out[chainId] = perAssetOut;
2062
- }
2063
- }
2064
- return out;
2065
- }
2066
-
2067
- // src/client.ts
2068
- import {
2069
- createPublicClient as createPublicClient2,
2070
- createWalletClient,
2071
- custom
2072
- } from "viem";
2073
- import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
2074
-
2075
- // src/lib/transfer-auth.ts
2076
- import { bytesToHex } from "viem";
2077
- var ERC20_META_ABI = [
2078
- { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
2079
- { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
2080
- ];
2081
- function buildTransferWithAuthorizationTypedData(input) {
2082
- return {
2083
- domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
2084
- types: {
2085
- TransferWithAuthorization: [
2086
- { name: "from", type: "address" },
2087
- { name: "to", type: "address" },
2088
- { name: "value", type: "uint256" },
2089
- { name: "validAfter", type: "uint256" },
2090
- { name: "validBefore", type: "uint256" },
2091
- { name: "nonce", type: "bytes32" }
2092
- ]
2093
- },
2094
- primaryType: "TransferWithAuthorization",
2095
- message: input.message
2096
- };
2097
- }
2098
- async function readTokenMeta(publicClient, token) {
2099
- const [tokenName, tokenVersion] = await Promise.all([
2100
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
2101
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
2102
- ]);
2103
- return { tokenName, tokenVersion };
2104
- }
2105
- function randomAuthNonce() {
2106
- const bytes = new Uint8Array(32);
2107
- globalThis.crypto.getRandomValues(bytes);
2108
- return bytesToHex(bytes);
2109
- }
2110
-
2111
- // src/lib/sponsor-client.ts
2112
- var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2113
- async function postSponsorTransferAuth(input) {
2114
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2115
- let res;
2116
- try {
2117
- res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
2118
- method: "POST",
2119
- headers: {
2120
- "content-type": "application/json",
2121
- "x-owney-api-key": input.apiKey
2122
- },
2123
- body: JSON.stringify(input.body)
2124
- });
2125
- } catch (networkError) {
2126
- throw new OwneyError(
2127
- "SPONSOR_REQUEST_FAILED",
2128
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2129
- { cause: String(networkError) }
2130
- );
2131
- }
2132
- const text = await res.text();
2133
- let parsed = null;
2134
- try {
2135
- parsed = JSON.parse(text);
2136
- } catch {
2137
- }
2138
- if (!res.ok || !parsed?.success || !parsed.data) {
2139
- throw new OwneyError(
2140
- "SPONSOR_REQUEST_FAILED",
2141
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2142
- {
2143
- statusCode: res.status,
2144
- responseBody: text.slice(0, 500),
2145
- // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
2146
- // before broadcast, so it is safe to fall back to a user-paid deposit.
2147
- safeToFallback: res.status === 503
2148
- }
2149
- );
2150
- }
2151
- return parsed.data;
2152
- }
2153
- async function postSponsorPermit2Transfer(input) {
2154
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2155
- let res;
2156
- try {
2157
- res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
2158
- method: "POST",
2159
- headers: {
2160
- "content-type": "application/json",
2161
- "x-owney-api-key": input.apiKey
2162
- },
2163
- body: JSON.stringify(input.body)
2164
- });
2165
- } catch (networkError) {
2166
- throw new OwneyError(
2167
- "SPONSOR_REQUEST_FAILED",
2168
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2169
- { cause: String(networkError), safeToFallback: false }
2170
- );
2171
- }
2172
- const text = await res.text();
2173
- let parsed = null;
2174
- try {
2175
- parsed = JSON.parse(text);
2176
- } catch {
2177
- }
2178
- if (!res.ok || !parsed?.success || !parsed.data) {
2179
- throw new OwneyError(
2180
- "SPONSOR_REQUEST_FAILED",
2181
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2182
- {
2183
- statusCode: res.status,
2184
- responseBody: text.slice(0, 500),
2185
- safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
2186
- }
2187
- );
2188
- }
2189
- return parsed.data;
1994
+ return parsed.data;
2190
1995
  }
2191
1996
  async function getSponsorRelayerAddress(input) {
2192
- const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1997
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2193
1998
  let res;
2194
1999
  try {
2195
2000
  res = await fetch(
2196
- `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2001
+ `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2197
2002
  {
2198
2003
  headers: { "x-owney-api-key": input.apiKey }
2199
2004
  }
@@ -2286,217 +2091,1394 @@ function randomPermit2Nonce() {
2286
2091
  globalThis.crypto.getRandomValues(bytes);
2287
2092
  return BigInt(bytesToHex2(bytes));
2288
2093
  }
2289
- async function readPermit2Allowance(publicClient, token, owner) {
2094
+ async function readPermit2Allowance(publicClient, token2, owner) {
2290
2095
  return publicClient.readContract({
2291
- address: token,
2096
+ address: token2,
2292
2097
  abi: ERC20_ALLOWANCE_ABI,
2293
2098
  functionName: "allowance",
2294
2099
  args: [owner, PERMIT2_ADDRESS]
2295
2100
  });
2296
2101
  }
2297
- async function readErc20Balance(publicClient, token, owner) {
2298
- return publicClient.readContract({
2299
- address: token,
2300
- abi: ERC20_ALLOWANCE_ABI,
2301
- functionName: "balanceOf",
2302
- args: [owner]
2102
+ async function readErc20Balance(publicClient, token2, owner) {
2103
+ return publicClient.readContract({
2104
+ address: token2,
2105
+ abi: ERC20_ALLOWANCE_ABI,
2106
+ functionName: "balanceOf",
2107
+ args: [owner]
2108
+ });
2109
+ }
2110
+
2111
+ // src/lib/sponsored-deposit.ts
2112
+ var AUTH_WINDOW_SECONDS = 15 * 60;
2113
+ var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
2114
+ function provideDepositVerificationContext(callback, context) {
2115
+ callback[verificationSetter]?.(context);
2116
+ }
2117
+ function makeVerificationAwareDepositCallback(implementation) {
2118
+ let nextVerification;
2119
+ const callback = async (smartWallet, chainId, amount) => {
2120
+ const verification = nextVerification;
2121
+ nextVerification = void 0;
2122
+ return implementation(smartWallet, chainId, amount, verification);
2123
+ };
2124
+ Object.defineProperty(callback, verificationSetter, {
2125
+ value: (context) => {
2126
+ nextVerification = context;
2127
+ }
2128
+ });
2129
+ return callback;
2130
+ }
2131
+ function makeSponsoredDepositCallback(deps) {
2132
+ const post = deps.httpPost ?? postSponsorTransferAuth;
2133
+ return makeVerificationAwareDepositCallback(
2134
+ async (smartWallet, chainId, amount, verification) => {
2135
+ const cid = chainId;
2136
+ const token2 = deps.tokenAddressByChain[cid];
2137
+ if (!token2) {
2138
+ throw new OwneyError(
2139
+ "CHAIN_UNSUPPORTED",
2140
+ `No sponsored token configured for chain ${chainId}`
2141
+ );
2142
+ }
2143
+ const pub = deps.getPublicClient(cid);
2144
+ const wallet = deps.getWalletClient(cid);
2145
+ await ensureWalletOnChain(pub, wallet, cid);
2146
+ try {
2147
+ const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
2148
+ if (balance < BigInt(amount)) {
2149
+ throw new OwneyError(
2150
+ "DEPOSIT_INSUFFICIENT_BALANCE",
2151
+ "Insufficient balance for this deposit.",
2152
+ { token: token2, chainId: cid, balance: balance.toString(), amount }
2153
+ );
2154
+ }
2155
+ } catch (err) {
2156
+ if (err instanceof OwneyError) throw err;
2157
+ console.warn(
2158
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2159
+ err instanceof Error ? err.message : String(err)
2160
+ );
2161
+ }
2162
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token2);
2163
+ const validAfter = 0n;
2164
+ const validBefore = BigInt(
2165
+ Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2166
+ );
2167
+ const nonce = randomAuthNonce();
2168
+ const typedData = buildTransferWithAuthorizationTypedData({
2169
+ token: token2,
2170
+ chainId: cid,
2171
+ tokenName,
2172
+ tokenVersion,
2173
+ message: {
2174
+ from: deps.ownerAddress,
2175
+ to: smartWallet,
2176
+ value: BigInt(amount),
2177
+ validAfter,
2178
+ validBefore,
2179
+ nonce
2180
+ }
2181
+ });
2182
+ const authSignature = await wallet.signTypedData({
2183
+ account: deps.ownerAddress,
2184
+ ...typedData
2185
+ });
2186
+ deps.onApproved?.();
2187
+ const result = await post({
2188
+ baseUrl: deps.baseUrl,
2189
+ apiKey: deps.apiKey,
2190
+ ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
2191
+ body: {
2192
+ chainId: cid,
2193
+ token: token2,
2194
+ from: deps.ownerAddress,
2195
+ to: smartWallet,
2196
+ value: amount,
2197
+ validAfter: validAfter.toString(),
2198
+ validBefore: validBefore.toString(),
2199
+ nonce,
2200
+ authSignature,
2201
+ tokenName,
2202
+ tokenVersion
2203
+ }
2204
+ });
2205
+ return result.txHash;
2206
+ }
2207
+ );
2208
+ }
2209
+
2210
+ // src/agents/yieldseeker/yieldseeker.auth.ts
2211
+ import { SiweMessage, generateNonce } from "siwe";
2212
+ import {
2213
+ createPublicClient as createPublicClient2,
2214
+ createWalletClient,
2215
+ custom,
2216
+ getAddress
2217
+ } from "viem";
2218
+ import { base as base2 } from "viem/chains";
2219
+
2220
+ // src/agents/yieldseeker/yieldseeker.auth-cache.ts
2221
+ var KEY_PREFIX2 = "owney.yieldseeker.session";
2222
+ var storage2 = () => {
2223
+ if (typeof window === "undefined") return null;
2224
+ try {
2225
+ return window.localStorage;
2226
+ } catch {
2227
+ return null;
2228
+ }
2229
+ };
2230
+ var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2231
+ var memorySessions2 = /* @__PURE__ */ new Map();
2232
+ var isValidSession = (session) => {
2233
+ if (!session?.token) return false;
2234
+ try {
2235
+ const parsed = JSON.parse(atob(session.token));
2236
+ return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
2237
+ } catch {
2238
+ return false;
2239
+ }
2240
+ };
2241
+ var readYieldseekerSession = (address, chainId) => {
2242
+ if (typeof window === "undefined") return null;
2243
+ const key2 = buildKey2(address, chainId);
2244
+ const store = storage2();
2245
+ let raw = null;
2246
+ try {
2247
+ raw = store?.getItem(key2) ?? null;
2248
+ } catch {
2249
+ raw = null;
2250
+ }
2251
+ if (raw) {
2252
+ try {
2253
+ const parsed = JSON.parse(raw);
2254
+ if (isValidSession(parsed)) return parsed.token;
2255
+ } catch {
2256
+ }
2257
+ memorySessions2.delete(key2);
2258
+ try {
2259
+ store?.removeItem(key2);
2260
+ } catch {
2261
+ }
2262
+ return null;
2263
+ }
2264
+ const cached = memorySessions2.get(key2);
2265
+ if (isValidSession(cached)) return cached.token;
2266
+ if (cached) memorySessions2.delete(key2);
2267
+ return null;
2268
+ };
2269
+ var writeYieldseekerSession = (address, chainId, token2) => {
2270
+ if (typeof window === "undefined") return;
2271
+ const session = { token: token2 };
2272
+ if (!isValidSession(session)) return;
2273
+ const key2 = buildKey2(address, chainId);
2274
+ memorySessions2.set(key2, session);
2275
+ const store = storage2();
2276
+ try {
2277
+ store?.setItem(key2, JSON.stringify(session));
2278
+ } catch {
2279
+ }
2280
+ };
2281
+ var clearYieldseekerSession = (address, chainId) => {
2282
+ const key2 = buildKey2(address, chainId);
2283
+ memorySessions2.delete(key2);
2284
+ const store = storage2();
2285
+ try {
2286
+ store?.removeItem(key2);
2287
+ } catch {
2288
+ }
2289
+ };
2290
+
2291
+ // src/agents/yieldseeker/yieldseeker.auth.ts
2292
+ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2293
+ return new SiweMessage({
2294
+ domain: "yieldseeker.xyz",
2295
+ address: getAddress(address),
2296
+ uri: "https://yieldseeker.xyz",
2297
+ version: "1",
2298
+ chainId,
2299
+ nonce: (dependencies.nonce ?? generateNonce)(),
2300
+ issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
2301
+ }).prepareMessage();
2302
+ }
2303
+ function encodeYieldseekerAuthToken(token2) {
2304
+ const bytes = new TextEncoder().encode(JSON.stringify(token2));
2305
+ let binary = "";
2306
+ for (const byte of bytes) binary += String.fromCharCode(byte);
2307
+ return btoa(binary);
2308
+ }
2309
+ var YieldseekerAuth = class {
2310
+ constructor(dependencies = {}) {
2311
+ this.dependencies = dependencies;
2312
+ }
2313
+ dependencies;
2314
+ tokens = /* @__PURE__ */ new Map();
2315
+ pending = /* @__PURE__ */ new Map();
2316
+ scopes = /* @__PURE__ */ new Map();
2317
+ key(state, chainId) {
2318
+ return `${state.walletAddress.toLowerCase()}:${chainId}`;
2319
+ }
2320
+ async getToken(state, chainId) {
2321
+ const key2 = this.key(state, chainId);
2322
+ const scope = { address: state.walletAddress, chainId };
2323
+ this.scopes.set(key2, scope);
2324
+ const cached = this.tokens.get(key2);
2325
+ if (cached) return cached;
2326
+ const persisted = readYieldseekerSession(scope.address, scope.chainId);
2327
+ if (persisted) {
2328
+ this.tokens.set(key2, persisted);
2329
+ return persisted;
2330
+ }
2331
+ const inFlight = this.pending.get(key2);
2332
+ if (inFlight) return inFlight;
2333
+ const request = this.sign(state, chainId).then((token2) => {
2334
+ this.tokens.set(key2, token2);
2335
+ writeYieldseekerSession(scope.address, scope.chainId, token2);
2336
+ return token2;
2337
+ });
2338
+ this.pending.set(key2, request);
2339
+ try {
2340
+ return await request;
2341
+ } finally {
2342
+ this.pending.delete(key2);
2343
+ }
2344
+ }
2345
+ clear(state, chainId) {
2346
+ if (!state || chainId === void 0) {
2347
+ for (const scope of this.scopes.values()) {
2348
+ clearYieldseekerSession(scope.address, scope.chainId);
2349
+ }
2350
+ this.tokens.clear();
2351
+ this.pending.clear();
2352
+ this.scopes.clear();
2353
+ return;
2354
+ }
2355
+ const key2 = this.key(state, chainId);
2356
+ this.tokens.delete(key2);
2357
+ this.pending.delete(key2);
2358
+ this.scopes.delete(key2);
2359
+ clearYieldseekerSession(state.walletAddress, chainId);
2360
+ }
2361
+ async sign(state, chainId) {
2362
+ const account = getAddress(state.walletAddress);
2363
+ const publicClient = createPublicClient2({
2364
+ chain: base2,
2365
+ transport: custom(state.provider)
2366
+ });
2367
+ const walletClient = createWalletClient({
2368
+ account,
2369
+ chain: base2,
2370
+ transport: custom(state.provider)
2371
+ });
2372
+ await ensureWalletOnChain(
2373
+ publicClient,
2374
+ walletClient,
2375
+ 8453
2376
+ );
2377
+ const message = createYieldseekerSiweMessage(
2378
+ account,
2379
+ chainId,
2380
+ this.dependencies
2381
+ );
2382
+ const signature = await walletClient.signMessage({ account, message });
2383
+ return encodeYieldseekerAuthToken({ message, signature });
2384
+ }
2385
+ };
2386
+
2387
+ // src/agents/yieldseeker/yieldseeker.client.ts
2388
+ var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2389
+ function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
2390
+ return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
2391
+ }
2392
+ var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
2393
+ var YieldseekerApiError = class extends Error {
2394
+ constructor(status, providerCode, responseFields) {
2395
+ super(`Yieldseeker request failed (${status}): ${providerCode}`);
2396
+ this.status = status;
2397
+ this.providerCode = providerCode;
2398
+ this.responseFields = responseFields;
2399
+ this.name = "YieldseekerApiError";
2400
+ }
2401
+ status;
2402
+ providerCode;
2403
+ responseFields;
2404
+ get isAuthenticationError() {
2405
+ return this.status === 401 || this.status === 403;
2406
+ }
2407
+ };
2408
+ function providerError(body, fallback) {
2409
+ if (!body || typeof body !== "object") return { code: fallback };
2410
+ const record = body;
2411
+ return {
2412
+ code: typeof record.message === "string" ? record.message : fallback,
2413
+ fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
2414
+ };
2415
+ }
2416
+ var YieldseekerApiClient = class {
2417
+ constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
2418
+ this.owneyApiKey = owneyApiKey;
2419
+ this.baseUrl = baseUrl;
2420
+ this.fetchFn = fetchFn;
2421
+ }
2422
+ owneyApiKey;
2423
+ baseUrl;
2424
+ fetchFn;
2425
+ async request(path, options = {}) {
2426
+ const controller = new AbortController();
2427
+ const timer = setTimeout(
2428
+ () => controller.abort(),
2429
+ options.timeoutMs ?? 15e3
2430
+ );
2431
+ try {
2432
+ const response = await this.fetchFn(`${this.baseUrl}${path}`, {
2433
+ method: options.method ?? "GET",
2434
+ headers: {
2435
+ "Content-Type": "application/json",
2436
+ "x-owney-api-key": this.owneyApiKey,
2437
+ ...options.signature ? { "X-Signature": options.signature } : {}
2438
+ },
2439
+ body: options.body ? JSON.stringify(options.body) : void 0,
2440
+ signal: controller.signal
2441
+ });
2442
+ const payload = await response.json().catch(() => null);
2443
+ if (!response.ok) {
2444
+ const error = providerError(payload, `HTTP_${response.status}`);
2445
+ throw new YieldseekerApiError(
2446
+ response.status,
2447
+ error.code,
2448
+ error.fields
2449
+ );
2450
+ }
2451
+ if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
2452
+ return payload.data;
2453
+ }
2454
+ return payload;
2455
+ } catch (error) {
2456
+ if (error instanceof YieldseekerApiError) throw error;
2457
+ if (error instanceof DOMException && error.name === "AbortError") {
2458
+ throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
2459
+ }
2460
+ throw new YieldseekerApiError(0, "NETWORK_ERROR", {
2461
+ cause: error instanceof Error ? error.message : String(error)
2462
+ });
2463
+ } finally {
2464
+ clearTimeout(timer);
2465
+ }
2466
+ }
2467
+ };
2468
+
2469
+ // src/agents/yieldseeker/yieldseeker.mapper.ts
2470
+ import { formatUnits } from "viem";
2471
+ var REBALANCE_AMOUNT_DECIMALS = {
2472
+ USDC: 6,
2473
+ WETH: 18
2474
+ };
2475
+ function rebalanceAmount(value, tokenSymbol) {
2476
+ const decimals = REBALANCE_AMOUNT_DECIMALS[tokenSymbol.toUpperCase()];
2477
+ if (decimals === void 0) {
2478
+ return invalid(
2479
+ "history",
2480
+ `unsupported rebalance token ${tokenSymbol || "<empty>"}`
2481
+ );
2482
+ }
2483
+ try {
2484
+ return formatUnits(BigInt(value), decimals);
2485
+ } catch {
2486
+ return invalid("history", `invalid rebalance amount ${value}`);
2487
+ }
2488
+ }
2489
+ function invalid(endpoint, detail) {
2490
+ throw new OwneyError(
2491
+ "AGENT_INVALID_RESPONSE",
2492
+ `Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
2493
+ { endpoint, detail },
2494
+ "yieldseeker"
2495
+ );
2496
+ }
2497
+ function requireObject(value, endpoint) {
2498
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2499
+ return invalid(endpoint, "expected an object");
2500
+ }
2501
+ return value;
2502
+ }
2503
+ function requireEnvelope(value, key2, endpoint) {
2504
+ const root = requireObject(value, endpoint);
2505
+ return requireObject(root[key2], endpoint);
2506
+ }
2507
+ function token(value) {
2508
+ return {
2509
+ chain: value.chain,
2510
+ chainId: value.chainId,
2511
+ asset: value.asset,
2512
+ amount: String(value.amount)
2513
+ };
2514
+ }
2515
+ function position(value) {
2516
+ return {
2517
+ chain: String(value.chain),
2518
+ protocol: String(value.protocol),
2519
+ ...value.protocolId ? { protocolId: value.protocolId } : {},
2520
+ ...value.pool ? { pool: value.pool } : {},
2521
+ asset: String(value.asset),
2522
+ amount: String(value.amount),
2523
+ ...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
2524
+ ...typeof value.apy === "number" ? { apy: value.apy } : {},
2525
+ ...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
2526
+ ...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
2527
+ };
2528
+ }
2529
+ function mapApyMatrix(matrix) {
2530
+ const mapped = {};
2531
+ for (const [chain, assets] of Object.entries(matrix ?? {})) {
2532
+ const chainId = Number(chain);
2533
+ if (chainId !== 8453 || !assets || typeof assets !== "object") continue;
2534
+ mapped[8453] = {};
2535
+ for (const [asset, apy] of Object.entries(assets)) {
2536
+ if ((asset === "USDC" || asset === "WETH") && typeof apy === "number") {
2537
+ mapped[8453][asset] = apy;
2538
+ }
2539
+ }
2540
+ }
2541
+ return mapped;
2542
+ }
2543
+ function mapYieldseekerProfile(value) {
2544
+ const profile = requireEnvelope(
2545
+ value,
2546
+ "profile",
2547
+ "profile"
2548
+ );
2549
+ if (typeof profile.address !== "string" || typeof profile.smartWallet !== "string" || !Array.isArray(profile.chains) || typeof profile.hasActiveSessionKey !== "boolean" || !Array.isArray(profile.protocols)) {
2550
+ return invalid("profile", "missing required fields");
2551
+ }
2552
+ return {
2553
+ address: profile.address,
2554
+ smartWallet: profile.smartWallet,
2555
+ chains: profile.chains.map(Number),
2556
+ hasActiveSessionKey: profile.hasActiveSessionKey,
2557
+ protocols: profile.protocols.map(String)
2558
+ };
2559
+ }
2560
+ function mapYieldseekerBalances(value) {
2561
+ const balances = requireEnvelope(
2562
+ value,
2563
+ "balances",
2564
+ "balances"
2565
+ );
2566
+ if (typeof balances.smartWallet !== "string" || typeof balances.totalBalance !== "string" || typeof balances.totalBalanceAsset !== "string" || !Array.isArray(balances.tokens) || !Array.isArray(balances.positions)) {
2567
+ return invalid("balances", "missing required fields");
2568
+ }
2569
+ return {
2570
+ smartWallet: balances.smartWallet,
2571
+ totalBalance: balances.totalBalance,
2572
+ totalBalanceAsset: balances.totalBalanceAsset.toLowerCase(),
2573
+ tokens: balances.tokens.map(token),
2574
+ positions: balances.positions.map(position)
2575
+ };
2576
+ }
2577
+ function mapYieldseekerEarnings(value) {
2578
+ const earnings = requireEnvelope(
2579
+ value,
2580
+ "earnings",
2581
+ "earnings"
2582
+ );
2583
+ if (typeof earnings.smartWallet !== "string" || typeof earnings.lifetimeEarnings !== "number" || !Array.isArray(earnings.tokens)) {
2584
+ return invalid("earnings", "missing required fields");
2585
+ }
2586
+ return {
2587
+ smartWallet: earnings.smartWallet,
2588
+ lifetimeEarnings: earnings.lifetimeEarnings,
2589
+ tokens: earnings.tokens.map(token)
2590
+ };
2591
+ }
2592
+ function mapYieldseekerApy(value) {
2593
+ const apy = requireEnvelope(
2594
+ value,
2595
+ "apy",
2596
+ "apy"
2597
+ );
2598
+ if (typeof apy.walletAddress !== "string" || typeof apy.weightedApyAfterFee !== "number" || !apy.apyByChainAndAsset || typeof apy.apyByChainAndAsset !== "object" || !Array.isArray(apy.history)) {
2599
+ return invalid("apy", "missing required fields");
2600
+ }
2601
+ return {
2602
+ walletAddress: apy.walletAddress,
2603
+ weightedApyAfterFee: apy.weightedApyAfterFee,
2604
+ apyByChainAndAsset: mapApyMatrix(apy.apyByChainAndAsset),
2605
+ history: apy.history.map((point) => ({
2606
+ date: String(point.date),
2607
+ apy: Number(point.apy)
2608
+ }))
2609
+ };
2610
+ }
2611
+ function historyAction(value) {
2612
+ const normalized = value.trim().toLowerCase();
2613
+ if (normalized === "rebalance") return "Rebalance";
2614
+ if (normalized === "deposit") return "Deposit";
2615
+ if (normalized === "top up" || normalized === "topup") return "Top up";
2616
+ if (normalized === "withdraw" || normalized === "withdrawal")
2617
+ return "Withdraw";
2618
+ if (normalized === "earned" || normalized === "earnings") return "Earned";
2619
+ return invalid("history", `unsupported action ${value}`);
2620
+ }
2621
+ function historyItem(value) {
2622
+ return {
2623
+ agent: "yieldseeker",
2624
+ action: historyAction(value.action),
2625
+ date: String(value.date),
2626
+ oldApy: value.oldApy === null ? null : String(value.oldApy),
2627
+ newApy: value.newApy === null ? null : String(value.newApy),
2628
+ transactions: (value.transactions ?? []).map((transaction) => ({
2629
+ txHashes: transaction.txHashes.map(String),
2630
+ ...typeof transaction.chainId === "number" ? { chainId: transaction.chainId } : {},
2631
+ ...typeof transaction.tokenSymbol === "string" ? { tokenSymbol: transaction.tokenSymbol } : {},
2632
+ ...typeof transaction.amount === "string" ? { amount: transaction.amount } : {}
2633
+ })),
2634
+ rebalanceLog: (value.rebalanceLog ?? []).map((entry) => ({
2635
+ fromProtocol: String(entry.fromProtocol),
2636
+ toProtocol: String(entry.toProtocol),
2637
+ ...entry.fromPool ? { fromPool: entry.fromPool } : {},
2638
+ ...entry.toPool ? { toPool: entry.toPool } : {},
2639
+ tokenSymbol: String(entry.tokenSymbol),
2640
+ // Unlike Yieldseeker's other read-response amounts, live history
2641
+ // rebalance amounts are returned in the token's smallest unit. Normalize
2642
+ // them at the adapter boundary so every Owney consumer receives the
2643
+ // shared human-readable decimal shape.
2644
+ amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
2645
+ status: entry.status
2646
+ }))
2647
+ };
2648
+ }
2649
+ function mapYieldseekerHistory(value) {
2650
+ const history = requireEnvelope(
2651
+ value,
2652
+ "history",
2653
+ "history"
2654
+ );
2655
+ if (!Array.isArray(history.data) || typeof history.hasMore !== "boolean") {
2656
+ return invalid("history", "missing required fields");
2657
+ }
2658
+ return {
2659
+ data: history.data.map(historyItem),
2660
+ hasMore: history.hasMore,
2661
+ ...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
2662
+ };
2663
+ }
2664
+ function mapYieldseekerAgentApy(value) {
2665
+ const agentApy = requireEnvelope(
2666
+ value,
2667
+ "agentApy",
2668
+ "agent APY"
2669
+ );
2670
+ if (typeof agentApy.averageApy !== "number") {
2671
+ return invalid("agent APY", "missing averageApy");
2672
+ }
2673
+ return {
2674
+ averageApy: agentApy.averageApy,
2675
+ ...agentApy.detailedApys ? {
2676
+ detailedApys: {
2677
+ apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
2678
+ }
2679
+ } : {}
2680
+ };
2681
+ }
2682
+
2683
+ // src/agents/yieldseeker/yieldseeker.agent.ts
2684
+ function query(params) {
2685
+ const search = new URLSearchParams();
2686
+ for (const [key2, value] of Object.entries(params)) {
2687
+ if (value !== void 0) search.set(key2, String(value));
2688
+ }
2689
+ const encoded = search.toString();
2690
+ return encoded ? `?${encoded}` : "";
2691
+ }
2692
+ var YieldseekerAgent = class {
2693
+ id = "yieldseeker";
2694
+ balanceComposition = "tokens-plus-positions";
2695
+ supportedChainIds = [8453];
2696
+ supportedAssets = [
2697
+ {
2698
+ chainId: 8453,
2699
+ chain: "BASE",
2700
+ assets: [
2701
+ { symbol: "USDC", minDepositAmount: "1" },
2702
+ { symbol: "WETH", minDepositAmount: "1" }
2703
+ ]
2704
+ }
2705
+ ];
2706
+ api;
2707
+ auth;
2708
+ transactionExecutor;
2709
+ unwindReceiptWaiter;
2710
+ activated = /* @__PURE__ */ new Set();
2711
+ constructor(owneyApiKey, options = {}) {
2712
+ this.api = new YieldseekerApiClient(
2713
+ owneyApiKey,
2714
+ options.baseUrl ?? getYieldseekerProxyBaseUrl(),
2715
+ options.fetchFn
2716
+ );
2717
+ this.auth = new YieldseekerAuth(options.auth);
2718
+ this.transactionExecutor = options.transactionExecutor;
2719
+ this.unwindReceiptWaiter = options.unwindReceiptWaiter;
2720
+ }
2721
+ async disconnect() {
2722
+ this.auth.clear();
2723
+ this.activated.clear();
2724
+ }
2725
+ async activateAgent(state, chainId, asset) {
2726
+ this.assertChain(chainId);
2727
+ await this.auth.getToken(state, chainId);
2728
+ if (asset === "USDC" || asset === "WETH") {
2729
+ await this.ensureActivated(state, chainId, asset);
2730
+ }
2731
+ }
2732
+ async deposit(state, chainId, amount, asset, depositCallback) {
2733
+ this.assertChain(chainId);
2734
+ this.assertAsset(asset);
2735
+ if (BigInt(amount) <= 0n) {
2736
+ throw new OwneyError(
2737
+ "DEPOSIT_AMOUNT_BELOW_MINIMUM",
2738
+ "Yieldseeker deposits must be greater than zero.",
2739
+ { amount, minDepositAmount: "1" },
2740
+ this.id
2741
+ );
2742
+ }
2743
+ await this.ensureActivated(state, chainId, asset);
2744
+ const response = await this.walletRequest(
2745
+ state,
2746
+ chainId,
2747
+ "/deposits",
2748
+ { method: "POST", body: { chainId, asset, amount } }
2749
+ );
2750
+ const deposit = response?.deposit;
2751
+ if (!deposit?.transaction || typeof deposit.amount !== "string" || deposit.amount !== amount || typeof deposit.smartWallet !== "string" || !isAddress(deposit.smartWallet)) {
2752
+ throw this.invalidResponse("deposit");
2753
+ }
2754
+ if (depositCallback) {
2755
+ provideDepositVerificationContext(depositCallback, {
2756
+ agentId: "yieldseeker",
2757
+ // Re-read after /deposits so an authentication retry cannot forward
2758
+ // the stale token that Yieldseeker just rejected.
2759
+ signature: await this.auth.getToken(state, chainId)
2760
+ });
2761
+ }
2762
+ const txHash = depositCallback ? await depositCallback(deposit.smartWallet, chainId, amount) : await this.submitTransaction(state, chainId, deposit.transaction);
2763
+ const confirmation = await this.walletRequest(
2764
+ state,
2765
+ chainId,
2766
+ "/deposits/confirm",
2767
+ { method: "POST", body: { chainId, asset } }
2768
+ );
2769
+ if (typeof confirmation?.confirmation?.autoseekQueued !== "boolean") {
2770
+ throw this.invalidResponse("deposit confirmation", {
2771
+ transactionHash: txHash,
2772
+ transactionConfirmed: true
2773
+ });
2774
+ }
2775
+ return {
2776
+ txHash,
2777
+ smartWallet: deposit.smartWallet,
2778
+ amount: deposit.amount
2779
+ };
2780
+ }
2781
+ async withdraw(state, chainId, asset, amount) {
2782
+ this.assertChain(chainId);
2783
+ this.assertAsset(asset);
2784
+ if (amount !== void 0 && BigInt(amount) <= 0n) {
2785
+ throw new OwneyError(
2786
+ "WITHDRAW_FAILED",
2787
+ "Yieldseeker withdrawals must be greater than zero.",
2788
+ { amount },
2789
+ this.id
2790
+ );
2791
+ }
2792
+ const balances = await this.getBalances(state, chainId);
2793
+ const decimals = asset === "USDC" ? 6 : 18;
2794
+ const targetAsset = asset.toUpperCase();
2795
+ const idle = balances.tokens.filter(
2796
+ (token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
2797
+ ).reduce((total, token2) => total + parseUnits(token2.amount, decimals), 0n);
2798
+ const positions = (balances.positions ?? []).filter(
2799
+ (position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
2800
+ ).map((position2) => ({
2801
+ position: position2,
2802
+ amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : parseUnits(position2.amount, decimals)
2803
+ }));
2804
+ const deployed = positions.reduce(
2805
+ (total, position2) => total + position2.amount,
2806
+ 0n
2807
+ );
2808
+ const totalAvailable = idle + deployed;
2809
+ const requested = amount === void 0 ? totalAvailable : BigInt(amount);
2810
+ if (requested > totalAvailable) {
2811
+ throw new OwneyError(
2812
+ "WITHDRAW_INSUFFICIENT_BALANCE",
2813
+ `Requested withdrawal "${requested.toString()}" exceeds available Yieldseeker balance "${totalAvailable.toString()}" for asset "${asset}".`,
2814
+ {
2815
+ asset,
2816
+ requested: requested.toString(),
2817
+ available: totalAvailable.toString()
2818
+ },
2819
+ this.id
2820
+ );
2821
+ }
2822
+ const requiredFromPositions = requested > idle ? requested - idle : 0n;
2823
+ const unwindPlans = [];
2824
+ let remainingToUnwind = requiredFromPositions;
2825
+ for (const { position: position2, amount: positionAmount } of positions) {
2826
+ if (remainingToUnwind === 0n) break;
2827
+ if (positionAmount <= 0n) continue;
2828
+ if (!position2.protocolId || !isAddress(position2.protocolId)) {
2829
+ throw this.invalidResponse("position", {
2830
+ reason: "A deployed position is missing its vault address.",
2831
+ protocol: position2.protocol,
2832
+ pool: position2.pool
2833
+ });
2834
+ }
2835
+ const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
2836
+ unwindPlans.push({
2837
+ vaultAddress: getAddress2(position2.protocolId),
2838
+ amount: unwindAmount
2839
+ });
2840
+ remainingToUnwind -= unwindAmount;
2841
+ }
2842
+ if (remainingToUnwind > 0n) {
2843
+ throw this.invalidResponse("balances", {
2844
+ reason: "Deployed position balances could not cover the unwind.",
2845
+ required: requiredFromPositions.toString(),
2846
+ planned: (requiredFromPositions - remainingToUnwind).toString()
2847
+ });
2848
+ }
2849
+ for (const plan of unwindPlans) {
2850
+ const response2 = await this.walletRequest(
2851
+ state,
2852
+ chainId,
2853
+ "/positions/unwind",
2854
+ {
2855
+ method: "POST",
2856
+ body: {
2857
+ chainId,
2858
+ asset,
2859
+ vaultAddress: plan.vaultAddress,
2860
+ amount: plan.amount.toString()
2861
+ }
2862
+ }
2863
+ );
2864
+ const transactionHash = response2?.unwind?.transactionHash;
2865
+ if (!this.isTransactionHash(transactionHash)) {
2866
+ throw this.invalidResponse("position unwind");
2867
+ }
2868
+ await this.waitForUnwindReceipt(state, chainId, transactionHash);
2869
+ }
2870
+ const response = await this.walletRequest(
2871
+ state,
2872
+ chainId,
2873
+ "/withdrawals",
2874
+ {
2875
+ method: "POST",
2876
+ body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
2877
+ }
2878
+ );
2879
+ const withdrawal = response?.withdrawal;
2880
+ if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
2881
+ throw this.invalidResponse("withdrawal");
2882
+ }
2883
+ const txHash = await this.submitTransaction(
2884
+ state,
2885
+ chainId,
2886
+ withdrawal.transaction
2887
+ );
2888
+ return {
2889
+ txHash,
2890
+ type: amount === void 0 ? "full" : "partial",
2891
+ amount: withdrawal.amount
2892
+ };
2893
+ }
2894
+ async getBalances(state, chainId) {
2895
+ return mapYieldseekerBalances(
2896
+ await this.walletRequest(
2897
+ state,
2898
+ chainId,
2899
+ `/balances${query({ chainId })}`
2900
+ )
2901
+ );
2902
+ }
2903
+ async getEarnings(state, chainId) {
2904
+ return mapYieldseekerEarnings(
2905
+ await this.walletRequest(
2906
+ state,
2907
+ chainId,
2908
+ `/earnings${query({ chainId })}`
2909
+ )
2910
+ );
2911
+ }
2912
+ async getAccountApy(state, chainId, days, tokenSymbol) {
2913
+ return mapYieldseekerApy(
2914
+ await this.walletRequest(
2915
+ state,
2916
+ chainId,
2917
+ `/apy${query({ chainId, days, tokenSymbol })}`
2918
+ )
2919
+ );
2920
+ }
2921
+ async getHistory(state, chainId, options) {
2922
+ return mapYieldseekerHistory(
2923
+ await this.walletRequest(
2924
+ state,
2925
+ chainId,
2926
+ `/history${query({
2927
+ chainId,
2928
+ limit: options?.limit ?? 10,
2929
+ cursor: options?.cursor,
2930
+ fromDate: options?.fromDate,
2931
+ toDate: options?.toDate
2932
+ })}`
2933
+ )
2934
+ );
2935
+ }
2936
+ async getUserProfile(state, chainId) {
2937
+ return mapYieldseekerProfile(
2938
+ await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
2939
+ );
2940
+ }
2941
+ async getAgentApy(days, options) {
2942
+ this.assertOptionalChain(options?.chainId);
2943
+ return mapYieldseekerAgentApy(
2944
+ await this.api.request(
2945
+ `/agent/apy${query({
2946
+ days,
2947
+ tokenSymbol: options?.tokenSymbol,
2948
+ chainId: options?.chainId
2949
+ })}`
2950
+ )
2951
+ );
2952
+ }
2953
+ async ensureActivated(state, chainId, asset) {
2954
+ const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
2955
+ if (this.activated.has(key2)) return;
2956
+ const response = await this.walletRequest(
2957
+ state,
2958
+ chainId,
2959
+ "/activation",
2960
+ { method: "POST", body: { chainId, asset } }
2961
+ );
2962
+ if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
2963
+ throw this.invalidResponse("activation");
2964
+ }
2965
+ this.activated.add(key2);
2966
+ }
2967
+ async walletRequest(state, chainId, path, options = {}) {
2968
+ this.assertChain(chainId);
2969
+ let signature = await this.auth.getToken(state, chainId);
2970
+ try {
2971
+ return await this.api.request(path, { ...options, signature });
2972
+ } catch (error) {
2973
+ if (!(error instanceof YieldseekerApiError)) throw error;
2974
+ if (error.isAuthenticationError) {
2975
+ this.auth.clear(state, chainId);
2976
+ signature = await this.auth.getToken(state, chainId);
2977
+ try {
2978
+ return await this.api.request(path, { ...options, signature });
2979
+ } catch (retryError) {
2980
+ throw this.mapApiError(retryError);
2981
+ }
2982
+ }
2983
+ throw this.mapApiError(error);
2984
+ }
2985
+ }
2986
+ mapApiError(error) {
2987
+ if (!(error instanceof YieldseekerApiError)) {
2988
+ return new OwneyError(
2989
+ "AGENT_API_ERROR",
2990
+ "Yieldseeker request failed.",
2991
+ { cause: error instanceof Error ? error.message : String(error) },
2992
+ this.id
2993
+ );
2994
+ }
2995
+ const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
2996
+ return new OwneyError(
2997
+ code,
2998
+ `Yieldseeker request failed: ${error.providerCode}.`,
2999
+ {
3000
+ statusCode: error.status,
3001
+ providerCode: error.providerCode,
3002
+ ...error.responseFields ? { fields: error.responseFields } : {}
3003
+ },
3004
+ this.id
3005
+ );
3006
+ }
3007
+ async submitTransaction(state, chainId, transaction) {
3008
+ if (this.transactionExecutor) {
3009
+ return this.transactionExecutor(state, chainId, transaction);
3010
+ }
3011
+ if (typeof transaction.from !== "string" || typeof transaction.to !== "string" || typeof transaction.data !== "string" || typeof transaction.value !== "string" || transaction.chainId !== chainId) {
3012
+ throw this.invalidResponse("transaction");
3013
+ }
3014
+ const account = getAddress2(state.walletAddress);
3015
+ if (getAddress2(transaction.from) !== account) {
3016
+ throw this.invalidResponse("transaction", {
3017
+ reason: "transaction.from does not match the connected wallet"
3018
+ });
3019
+ }
3020
+ const walletClient = createWalletClient2({
3021
+ account,
3022
+ chain: base3,
3023
+ transport: custom2(state.provider)
3024
+ });
3025
+ const publicClient = createPublicClient3({
3026
+ chain: base3,
3027
+ transport: custom2(state.provider)
3028
+ });
3029
+ await ensureWalletOnChain(
3030
+ publicClient,
3031
+ walletClient,
3032
+ 8453
3033
+ );
3034
+ const hash = await walletClient.sendTransaction({
3035
+ account,
3036
+ chain: base3,
3037
+ to: getAddress2(transaction.to),
3038
+ data: transaction.data,
3039
+ value: BigInt(transaction.value)
3040
+ });
3041
+ const receipt = await publicClient.waitForTransactionReceipt({
3042
+ hash,
3043
+ confirmations: 1
3044
+ });
3045
+ if (receipt.status !== "success") {
3046
+ throw new OwneyError(
3047
+ "AGENT_TRANSACTION_REVERTED",
3048
+ `Yieldseeker transaction reverted (${hash}).`,
3049
+ { transactionHash: hash },
3050
+ this.id
3051
+ );
3052
+ }
3053
+ return hash;
3054
+ }
3055
+ async waitForUnwindReceipt(state, chainId, transactionHash) {
3056
+ if (this.unwindReceiptWaiter) {
3057
+ await this.unwindReceiptWaiter(state, chainId, transactionHash);
3058
+ return;
3059
+ }
3060
+ const publicClient = createPublicClient3({
3061
+ chain: base3,
3062
+ transport: custom2(state.provider)
3063
+ });
3064
+ const receipt = await publicClient.waitForTransactionReceipt({
3065
+ hash: transactionHash,
3066
+ confirmations: 1
3067
+ });
3068
+ if (receipt.status !== "success") {
3069
+ throw new OwneyError(
3070
+ "AGENT_TRANSACTION_REVERTED",
3071
+ `Yieldseeker position unwind reverted (${transactionHash}).`,
3072
+ { transactionHash },
3073
+ this.id
3074
+ );
3075
+ }
3076
+ }
3077
+ positionMatchesChain(chain, chainId) {
3078
+ const normalized = chain.trim().toUpperCase();
3079
+ return normalized === String(chainId) || normalized === "BASE";
3080
+ }
3081
+ isTransactionHash(value) {
3082
+ return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
3083
+ }
3084
+ assertChain(chainId) {
3085
+ if (chainId !== 8453) {
3086
+ throw new OwneyError(
3087
+ "CHAIN_UNSUPPORTED",
3088
+ `Yieldseeker does not support chain ${chainId}.`,
3089
+ { chainId, supportedChainIds: [8453] },
3090
+ this.id
3091
+ );
3092
+ }
3093
+ }
3094
+ assertOptionalChain(chainId) {
3095
+ if (chainId !== void 0) this.assertChain(chainId);
3096
+ }
3097
+ assertAsset(asset) {
3098
+ if (asset !== "USDC" && asset !== "WETH") {
3099
+ throw new OwneyError(
3100
+ "ASSET_UNSUPPORTED",
3101
+ `Yieldseeker does not support asset ${asset}.`,
3102
+ { asset, supportedAssets: ["USDC", "WETH"] },
3103
+ this.id
3104
+ );
3105
+ }
3106
+ }
3107
+ invalidResponse(operation, details = {}) {
3108
+ return new OwneyError(
3109
+ "AGENT_INVALID_RESPONSE",
3110
+ `Yieldseeker returned an invalid ${operation} response.`,
3111
+ details,
3112
+ this.id
3113
+ );
3114
+ }
3115
+ };
3116
+
3117
+ // src/lib/routing-api.ts
3118
+ var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3119
+ async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3120
+ const url = `${baseUrl}/api/v1/agent/org-config`;
3121
+ try {
3122
+ const res = await fetch(url, {
3123
+ method: "GET",
3124
+ headers: {
3125
+ "Content-Type": "application/json",
3126
+ "x-owney-api-key": `${apiKey}`
3127
+ }
3128
+ });
3129
+ if (!res.ok) {
3130
+ if (res.status !== 404) {
3131
+ console.warn(
3132
+ `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
3133
+ );
3134
+ }
3135
+ return null;
3136
+ }
3137
+ const json = await res.json();
3138
+ const policy = json.success ? json.data ?? null : null;
3139
+ debugLog(
3140
+ "owney-sdk",
3141
+ policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
3142
+ policy ?? void 0
3143
+ );
3144
+ return policy;
3145
+ } catch (error) {
3146
+ console.warn(
3147
+ "[owney-sdk] Could not read org agent config (non-fatal):",
3148
+ error instanceof Error ? error.message : String(error)
3149
+ );
3150
+ return null;
3151
+ }
3152
+ }
3153
+ async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
3154
+ const url = `${baseUrl}/api/v1/agent/keys`;
3155
+ const res = await fetch(url, {
3156
+ method: "GET",
3157
+ headers: {
3158
+ "Content-Type": "application/json",
3159
+ "x-owney-api-key": `${apiKey}`
3160
+ }
3161
+ });
3162
+ if (!res.ok) {
3163
+ const text = await res.text().catch(() => "");
3164
+ throw new OwneyError(
3165
+ "API_ROUTING_ERROR",
3166
+ `Routing API error ${res.status}: ${text}`,
3167
+ { statusCode: res.status, responseBody: text }
3168
+ );
3169
+ }
3170
+ const json = await res.json();
3171
+ if (!json.success) {
3172
+ throw new OwneyError(
3173
+ "API_ROUTING_FAILED",
3174
+ `Routing API request failed: ${json.message}`,
3175
+ { message: json.message }
3176
+ );
3177
+ }
3178
+ return json.data;
3179
+ }
3180
+
3181
+ // src/lib/health-report.ts
3182
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
3183
+ async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
3184
+ try {
3185
+ await fetch(`${baseUrl}/api/v1/agent/health-report`, {
3186
+ method: "POST",
3187
+ headers: {
3188
+ "Content-Type": "application/json",
3189
+ "x-owney-api-key": apiKey
3190
+ },
3191
+ body: JSON.stringify({
3192
+ agent_type: agentType,
3193
+ error_code: errorCode,
3194
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3195
+ })
3196
+ });
3197
+ } catch (err) {
3198
+ console.warn(
3199
+ `[owney-sdk] health-report failed for agent "${agentType}":`,
3200
+ err instanceof Error ? err.message : err
3201
+ );
3202
+ }
3203
+ }
3204
+ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
3205
+ try {
3206
+ return await fn();
3207
+ } catch (err) {
3208
+ const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3209
+ void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
3210
+ throw err;
3211
+ }
3212
+ }
3213
+
3214
+ // src/lib/helpers/withdraw-helper.ts
3215
+ import { parseUnits as parseUnits2 } from "viem";
3216
+ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
3217
+ const target = asset.toUpperCase();
3218
+ return agents.map((agent) => {
3219
+ const agentBalance = aggregated[agent.id];
3220
+ const tokenBalance = agentBalance?.tokens.find(
3221
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
3222
+ );
3223
+ let balance = tokenBalance ? parseUnits2(tokenBalance.amount, decimals) : 0n;
3224
+ if (agent.balanceComposition === "tokens-plus-positions") {
3225
+ const chainNameById = {
3226
+ 1: "ETHEREUM",
3227
+ 8453: "BASE",
3228
+ 42161: "ARBITRUM"
3229
+ };
3230
+ const targetChain = chainNameById[chainId];
3231
+ for (const position2 of agentBalance?.positions ?? []) {
3232
+ const positionChain = position2.chain.trim().toUpperCase();
3233
+ const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
3234
+ if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
3235
+ if (position2.amountRaw !== void 0) {
3236
+ try {
3237
+ balance += BigInt(position2.amountRaw);
3238
+ continue;
3239
+ } catch {
3240
+ }
3241
+ }
3242
+ balance += parseUnits2(position2.amount, decimals);
3243
+ }
3244
+ }
3245
+ return { agent, balance };
3246
+ });
3247
+ }
3248
+ function planProportionalShares(balances, requested, totalAvailable) {
3249
+ const plans = balances.map(({ agent, balance }) => ({
3250
+ agent,
3251
+ balance,
3252
+ planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
3253
+ }));
3254
+ const assigned = plans.reduce((s, p) => s + p.planned, 0n);
3255
+ let remainder = requested - assigned;
3256
+ const byHeadroom = [...plans].sort((a, b) => {
3257
+ const diff = b.balance - b.planned - (a.balance - a.planned);
3258
+ return diff > 0n ? 1 : diff < 0n ? -1 : 0;
2303
3259
  });
3260
+ for (const p of byHeadroom) {
3261
+ if (remainder === 0n) break;
3262
+ const headroom = p.balance - p.planned;
3263
+ if (headroom <= 0n) continue;
3264
+ const take = headroom < remainder ? headroom : remainder;
3265
+ p.planned += take;
3266
+ remainder -= take;
3267
+ }
3268
+ return plans;
2304
3269
  }
2305
-
2306
- // src/lib/chain-guard.ts
2307
- var CHAIN_NAMES = {
2308
- 1: "Ethereum",
2309
- 8453: "Base",
2310
- 42161: "Arbitrum"
2311
- };
2312
- function chainName(chainId) {
2313
- return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
3270
+ function planDisabledDrain(disabled, requested) {
3271
+ const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
3272
+ (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
3273
+ );
3274
+ const plans = [];
3275
+ let remaining = requested;
3276
+ for (const { agent, balance } of sorted) {
3277
+ if (remaining === 0n) {
3278
+ plans.push({ agent, balance, planned: 0n });
3279
+ continue;
3280
+ }
3281
+ const take = balance < remaining ? balance : remaining;
3282
+ plans.push({ agent, balance, planned: take });
3283
+ remaining -= take;
3284
+ }
3285
+ return { plans, remaining };
2314
3286
  }
2315
- async function ensureWalletOnChain(pub, wallet, expected) {
2316
- const actual = await pub.getChainId();
2317
- if (actual === expected) return;
2318
- try {
2319
- await wallet.switchChain({ id: expected });
2320
- } catch (error) {
2321
- throw new OwneyError(
2322
- "CHAIN_MISMATCH",
2323
- `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2324
- {
2325
- expectedChainId: expected,
2326
- actualChainId: actual,
2327
- cause: error instanceof Error ? error.message : String(error)
2328
- }
2329
- );
3287
+ function redistributeShare(plans, fromIndex, amount, candidatePool) {
3288
+ const pool = candidatePool ?? plans.slice(fromIndex + 1);
3289
+ const candidates = pool.filter((c) => c.balance - c.planned > 0n);
3290
+ const totalHeadroom = candidates.reduce(
3291
+ (s, c) => s + (c.balance - c.planned),
3292
+ 0n
3293
+ );
3294
+ if (totalHeadroom === 0n) return;
3295
+ let distributed = 0n;
3296
+ for (const c of candidates) {
3297
+ const headroom = c.balance - c.planned;
3298
+ const proportional = headroom * amount / totalHeadroom;
3299
+ const give = proportional > headroom ? headroom : proportional;
3300
+ c.planned += give;
3301
+ distributed += give;
2330
3302
  }
2331
- const after = await pub.getChainId();
2332
- if (after !== expected) {
2333
- throw new OwneyError(
2334
- "CHAIN_MISMATCH",
2335
- `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2336
- { expectedChainId: expected, actualChainId: after }
2337
- );
3303
+ let leftover = amount - distributed;
3304
+ for (const c of candidates) {
3305
+ if (leftover === 0n) break;
3306
+ const headroom = c.balance - c.planned;
3307
+ if (headroom <= 0n) continue;
3308
+ const take = headroom < leftover ? headroom : leftover;
3309
+ c.planned += take;
3310
+ leftover -= take;
2338
3311
  }
2339
3312
  }
3313
+ function sumWithdrawnAmount(results) {
3314
+ return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
3315
+ }
2340
3316
 
2341
- // src/lib/sponsored-deposit.ts
2342
- var AUTH_WINDOW_SECONDS = 15 * 60;
2343
- function makeSponsoredDepositCallback(deps) {
2344
- const post = deps.httpPost ?? postSponsorTransferAuth;
2345
- return async (smartWallet, chainId, amount) => {
2346
- const cid = chainId;
2347
- const token = deps.tokenAddressByChain[cid];
2348
- if (!token) {
2349
- throw new OwneyError(
2350
- "CHAIN_UNSUPPORTED",
2351
- `No sponsored token configured for chain ${chainId}`
2352
- );
3317
+ // src/lib/helpers/account-apy-helper.ts
3318
+ function balanceForApyScope(balance, chainId, tokenSymbol) {
3319
+ if (!tokenSymbol) {
3320
+ const total = Number(balance.totalBalance);
3321
+ return Number.isFinite(total) && total > 0 ? total : 0;
3322
+ }
3323
+ const normalizedToken = tokenSymbol.toUpperCase();
3324
+ return balance.tokens.reduce((total, token2) => {
3325
+ if (Number(token2.chainId) !== chainId || String(token2.asset).toUpperCase() !== normalizedToken) {
3326
+ return total;
2353
3327
  }
2354
- const pub = deps.getPublicClient(cid);
2355
- const wallet = deps.getWalletClient(cid);
2356
- await ensureWalletOnChain(pub, wallet, cid);
2357
- try {
2358
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2359
- if (balance < BigInt(amount)) {
2360
- throw new OwneyError(
2361
- "DEPOSIT_INSUFFICIENT_BALANCE",
2362
- "Insufficient balance for this deposit.",
2363
- { token, chainId: cid, balance: balance.toString(), amount }
2364
- );
2365
- }
2366
- } catch (err) {
2367
- if (err instanceof OwneyError) throw err;
2368
- console.warn(
2369
- "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2370
- err instanceof Error ? err.message : String(err)
2371
- );
2372
- }
2373
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2374
- const validAfter = 0n;
2375
- const validBefore = BigInt(
2376
- Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2377
- );
2378
- const nonce = randomAuthNonce();
2379
- const typedData = buildTransferWithAuthorizationTypedData({
2380
- token,
2381
- chainId: cid,
2382
- tokenName,
2383
- tokenVersion,
2384
- message: {
2385
- from: deps.ownerAddress,
2386
- to: smartWallet,
2387
- value: BigInt(amount),
2388
- validAfter,
2389
- validBefore,
2390
- nonce
2391
- }
2392
- });
2393
- const authSignature = await wallet.signTypedData({
2394
- account: deps.ownerAddress,
2395
- ...typedData
2396
- });
2397
- deps.onApproved?.();
2398
- const result = await post({
2399
- baseUrl: deps.baseUrl,
2400
- apiKey: deps.apiKey,
2401
- body: {
2402
- chainId: cid,
2403
- token,
2404
- from: deps.ownerAddress,
2405
- to: smartWallet,
2406
- value: amount,
2407
- validAfter: validAfter.toString(),
2408
- validBefore: validBefore.toString(),
2409
- nonce,
2410
- authSignature,
2411
- tokenName,
2412
- tokenVersion
3328
+ const amount = Number(token2.amount);
3329
+ return Number.isFinite(amount) && amount > 0 ? total + amount : total;
3330
+ }, 0);
3331
+ }
3332
+ function aggregateApyHistory(agentApys, agentBalances) {
3333
+ const byDate = /* @__PURE__ */ new Map();
3334
+ for (const [id, accountApy] of Object.entries(agentApys)) {
3335
+ const balance = agentBalances[id] ?? 0;
3336
+ if (!Number.isFinite(balance) || balance <= 0) continue;
3337
+ for (const point of accountApy.history ?? []) {
3338
+ const apy = Number(point.apy);
3339
+ if (!point.date || !Number.isFinite(apy)) continue;
3340
+ const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
3341
+ current.weightedSum += apy * balance;
3342
+ current.weight += balance;
3343
+ byDate.set(point.date, current);
3344
+ }
3345
+ }
3346
+ return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
3347
+ date,
3348
+ apy: value.weightedSum / value.weight
3349
+ }));
3350
+ }
3351
+ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
3352
+ const sums = {};
3353
+ const weights = {};
3354
+ for (const id of Object.keys(agentApys)) {
3355
+ const cells = agentApys[id].apyByChainAndAsset;
3356
+ const balance = agentBalances[id] ?? 0;
3357
+ if (!cells || balance <= 0) continue;
3358
+ for (const [chainKey, perAsset] of Object.entries(cells)) {
3359
+ if (!perAsset) continue;
3360
+ const chainId = Number(chainKey);
3361
+ for (const [asset, apyValue] of Object.entries(perAsset)) {
3362
+ const apy = Number(apyValue ?? 0);
3363
+ if (apy === 0) continue;
3364
+ sums[chainId] ??= {};
3365
+ weights[chainId] ??= {};
3366
+ sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
3367
+ weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
2413
3368
  }
2414
- });
2415
- return result.txHash;
2416
- };
3369
+ }
3370
+ }
3371
+ const out = {};
3372
+ for (const chainKey of Object.keys(sums)) {
3373
+ const chainId = Number(chainKey);
3374
+ const perAssetOut = {};
3375
+ for (const asset of Object.keys(sums[chainId])) {
3376
+ const w = weights[chainId][asset];
3377
+ if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
3378
+ }
3379
+ if (Object.keys(perAssetOut).length > 0) {
3380
+ out[chainId] = perAssetOut;
3381
+ }
3382
+ }
3383
+ return out;
2417
3384
  }
2418
3385
 
3386
+ // src/client.ts
3387
+ import {
3388
+ createPublicClient as createPublicClient4,
3389
+ createWalletClient as createWalletClient3,
3390
+ custom as custom3
3391
+ } from "viem";
3392
+ import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
3393
+
2419
3394
  // src/lib/sponsored-weth-deposit.ts
2420
3395
  var PERMIT_WINDOW_SECONDS = 15 * 60;
2421
3396
  function makeSponsoredWethCallback(deps) {
2422
3397
  const get = deps.httpGet ?? getSponsorRelayerAddress;
2423
3398
  const post = deps.httpPost ?? postSponsorPermit2Transfer;
2424
- return async (smartWallet, chainId, amount) => {
2425
- const cid = chainId;
2426
- const token = deps.tokenAddressByChain[cid];
2427
- if (!token) {
2428
- throw new OwneyError(
2429
- "CHAIN_UNSUPPORTED",
2430
- `No sponsored WETH configured for chain ${chainId}`
2431
- );
2432
- }
2433
- const amountWei = BigInt(amount);
2434
- const pub = deps.getPublicClient(cid);
2435
- const wallet = deps.getWalletClient(cid);
2436
- await ensureWalletOnChain(pub, wallet, cid);
2437
- try {
2438
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2439
- if (balance < amountWei) {
3399
+ return makeVerificationAwareDepositCallback(
3400
+ async (smartWallet, chainId, amount, verification) => {
3401
+ const cid = chainId;
3402
+ const token2 = deps.tokenAddressByChain[cid];
3403
+ if (!token2) {
2440
3404
  throw new OwneyError(
2441
- "DEPOSIT_INSUFFICIENT_BALANCE",
2442
- "Insufficient WETH balance for this deposit.",
2443
- { token, chainId: cid, balance: balance.toString(), amount }
3405
+ "CHAIN_UNSUPPORTED",
3406
+ `No sponsored WETH configured for chain ${chainId}`
2444
3407
  );
2445
3408
  }
2446
- } catch (err) {
2447
- if (err instanceof OwneyError) throw err;
2448
- console.warn(
2449
- "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2450
- err instanceof Error ? err.message : String(err)
2451
- );
2452
- }
2453
- const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2454
- if (allowance < amountWei) {
2455
- throw new OwneyError(
2456
- "PERMIT2_APPROVAL_REQUIRED",
2457
- "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
2458
- { token, chainId: cid, allowance: allowance.toString(), amount }
3409
+ const amountWei = BigInt(amount);
3410
+ const pub = deps.getPublicClient(cid);
3411
+ const wallet = deps.getWalletClient(cid);
3412
+ await ensureWalletOnChain(pub, wallet, cid);
3413
+ try {
3414
+ const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
3415
+ if (balance < amountWei) {
3416
+ throw new OwneyError(
3417
+ "DEPOSIT_INSUFFICIENT_BALANCE",
3418
+ "Insufficient WETH balance for this deposit.",
3419
+ { token: token2, chainId: cid, balance: balance.toString(), amount }
3420
+ );
3421
+ }
3422
+ } catch (err) {
3423
+ if (err instanceof OwneyError) throw err;
3424
+ console.warn(
3425
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
3426
+ err instanceof Error ? err.message : String(err)
3427
+ );
3428
+ }
3429
+ const allowance = await readPermit2Allowance(
3430
+ pub,
3431
+ token2,
3432
+ deps.ownerAddress
2459
3433
  );
2460
- }
2461
- const relayer = await get({
2462
- baseUrl: deps.baseUrl,
2463
- apiKey: deps.apiKey,
2464
- chainId: cid
2465
- });
2466
- const nonce = randomPermit2Nonce();
2467
- const deadline = BigInt(
2468
- Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
2469
- );
2470
- const typedData = buildPermitTransferFromTypedData({
2471
- chainId: cid,
2472
- message: {
2473
- permitted: { token, amount: amountWei },
2474
- spender: relayer,
2475
- nonce,
2476
- deadline
3434
+ if (allowance < amountWei) {
3435
+ throw new OwneyError(
3436
+ "PERMIT2_APPROVAL_REQUIRED",
3437
+ "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3438
+ { token: token2, chainId: cid, allowance: allowance.toString(), amount }
3439
+ );
2477
3440
  }
2478
- });
2479
- const signature = await wallet.signTypedData({
2480
- account: deps.ownerAddress,
2481
- ...typedData
2482
- });
2483
- deps.onApproved?.();
2484
- const result = await post({
2485
- baseUrl: deps.baseUrl,
2486
- apiKey: deps.apiKey,
2487
- body: {
3441
+ const relayer = await get({
3442
+ baseUrl: deps.baseUrl,
3443
+ apiKey: deps.apiKey,
3444
+ chainId: cid
3445
+ });
3446
+ const nonce = randomPermit2Nonce();
3447
+ const deadline = BigInt(
3448
+ Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
3449
+ );
3450
+ const typedData = buildPermitTransferFromTypedData({
2488
3451
  chainId: cid,
2489
- token,
2490
- from: deps.ownerAddress,
2491
- to: smartWallet,
2492
- amount,
2493
- nonce: nonce.toString(),
2494
- deadline: deadline.toString(),
2495
- signature
2496
- }
2497
- });
2498
- return result.txHash;
2499
- };
3452
+ message: {
3453
+ permitted: { token: token2, amount: amountWei },
3454
+ spender: relayer,
3455
+ nonce,
3456
+ deadline
3457
+ }
3458
+ });
3459
+ const signature = await wallet.signTypedData({
3460
+ account: deps.ownerAddress,
3461
+ ...typedData
3462
+ });
3463
+ deps.onApproved?.();
3464
+ const result = await post({
3465
+ baseUrl: deps.baseUrl,
3466
+ apiKey: deps.apiKey,
3467
+ ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
3468
+ body: {
3469
+ chainId: cid,
3470
+ token: token2,
3471
+ from: deps.ownerAddress,
3472
+ to: smartWallet,
3473
+ amount,
3474
+ nonce: nonce.toString(),
3475
+ deadline: deadline.toString(),
3476
+ signature
3477
+ }
3478
+ });
3479
+ return result.txHash;
3480
+ }
3481
+ );
2500
3482
  }
2501
3483
 
2502
3484
  // src/lib/sponsored-calls-deposit.ts
@@ -2528,8 +3510,8 @@ function makeSponsoredCallsCallback(deps) {
2528
3510
  };
2529
3511
  return async (smartWallet, chainId, amount) => {
2530
3512
  const cid = chainId;
2531
- const token = deps.tokenAddressByChain[cid];
2532
- if (!token) {
3513
+ const token2 = deps.tokenAddressByChain[cid];
3514
+ if (!token2) {
2533
3515
  throw new OwneyError(
2534
3516
  "CHAIN_UNSUPPORTED",
2535
3517
  `No sponsored token configured for chain ${chainId}`
@@ -2555,7 +3537,7 @@ function makeSponsoredCallsCallback(deps) {
2555
3537
  from: deps.ownerAddress,
2556
3538
  chainId: toHex(chainId),
2557
3539
  atomicRequired: false,
2558
- calls: [{ to: token, value: "0x0", data }],
3540
+ calls: [{ to: token2, value: "0x0", data }],
2559
3541
  capabilities: {
2560
3542
  paymasterService: { url: absolutePaymasterUrl() }
2561
3543
  }
@@ -2594,10 +3576,10 @@ function makeSponsoredCallsCallback(deps) {
2594
3576
  function encodeMultiAgentCursor(map) {
2595
3577
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
2596
3578
  }
2597
- function decodeMultiAgentCursor(token) {
3579
+ function decodeMultiAgentCursor(token2) {
2598
3580
  let parsed;
2599
3581
  try {
2600
- const json = Buffer.from(token, "base64").toString("utf8");
3582
+ const json = Buffer.from(token2, "base64").toString("utf8");
2601
3583
  parsed = JSON.parse(json);
2602
3584
  } catch {
2603
3585
  throw new InvalidHistoryCursorError(
@@ -2617,7 +3599,7 @@ var SPONSORED_USDC_BY_CHAIN = {
2617
3599
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
2618
3600
  };
2619
3601
  var VIEM_CHAIN2 = {
2620
- 8453: base2,
3602
+ 8453: base4,
2621
3603
  42161: arbitrum2,
2622
3604
  1: mainnet2
2623
3605
  };
@@ -2647,6 +3629,7 @@ var OwneySDK = class {
2647
3629
  orgAgentConfig;
2648
3630
  orgAgentConfigPromise = null;
2649
3631
  zyfaiRpcUrls;
3632
+ yieldseekerApiBaseUrl;
2650
3633
  routingApiBaseUrl;
2651
3634
  referralSource;
2652
3635
  cachedSponsoredCallback = null;
@@ -2658,6 +3641,7 @@ var OwneySDK = class {
2658
3641
  this.apiKey = config.apiKey;
2659
3642
  if (config.debug) setOwneyDebug(true);
2660
3643
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
3644
+ this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
2661
3645
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2662
3646
  this.paymasterServiceUrl = config.paymasterServiceUrl;
2663
3647
  this.referralSource = config.referralSource;
@@ -2760,14 +3744,14 @@ var OwneySDK = class {
2760
3744
  // Casts work around viem's chain-narrowed Client vs the generic
2761
3745
  // PublicClient/WalletClient param types — structurally identical at
2762
3746
  // runtime, but the two share a name TS treats as unrelated.
2763
- getPublicClient: (cid) => createPublicClient2({
3747
+ getPublicClient: (cid) => createPublicClient4({
2764
3748
  chain: VIEM_CHAIN2[cid],
2765
- transport: custom(provider)
3749
+ transport: custom3(provider)
2766
3750
  }),
2767
- getWalletClient: (cid) => createWalletClient({
3751
+ getWalletClient: (cid) => createWalletClient3({
2768
3752
  account: owner,
2769
3753
  chain: VIEM_CHAIN2[cid],
2770
- transport: custom(provider)
3754
+ transport: custom3(provider)
2771
3755
  })
2772
3756
  });
2773
3757
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -2813,14 +3797,14 @@ var OwneySDK = class {
2813
3797
  // Casts work around viem's chain-narrowed Client vs the generic
2814
3798
  // PublicClient/WalletClient param types — structurally identical at
2815
3799
  // runtime, but the two share a name TS treats as unrelated.
2816
- getPublicClient: (cid) => createPublicClient2({
3800
+ getPublicClient: (cid) => createPublicClient4({
2817
3801
  chain: VIEM_CHAIN2[cid],
2818
- transport: custom(provider)
3802
+ transport: custom3(provider)
2819
3803
  }),
2820
- getWalletClient: (cid) => createWalletClient({
3804
+ getWalletClient: (cid) => createWalletClient3({
2821
3805
  account: owner,
2822
3806
  chain: VIEM_CHAIN2[cid],
2823
- transport: custom(provider)
3807
+ transport: custom3(provider)
2824
3808
  })
2825
3809
  });
2826
3810
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -2848,12 +3832,13 @@ var OwneySDK = class {
2848
3832
  async getOrgAgentConfig() {
2849
3833
  if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
2850
3834
  if (!this.orgAgentConfigPromise) {
2851
- this.orgAgentConfigPromise = fetchOrgAgentConfig(this.apiKey).then(
2852
- (config) => {
2853
- this.orgAgentConfig = config;
2854
- return config;
2855
- }
2856
- );
3835
+ this.orgAgentConfigPromise = fetchOrgAgentConfig(
3836
+ this.apiKey,
3837
+ this.routingApiBaseUrl
3838
+ ).then((config) => {
3839
+ this.orgAgentConfig = config;
3840
+ return config;
3841
+ });
2857
3842
  }
2858
3843
  return this.orgAgentConfigPromise;
2859
3844
  }
@@ -2888,9 +3873,19 @@ var OwneySDK = class {
2888
3873
  if (this.agents.size > 0) return;
2889
3874
  if (!this.initializingAgentsPromise) {
2890
3875
  this.initializingAgentsPromise = (async () => {
2891
- const agentKeys = await fetchAgentKeys(this.apiKey);
3876
+ const agentKeys = await fetchAgentKeys(
3877
+ this.apiKey,
3878
+ this.routingApiBaseUrl
3879
+ );
2892
3880
  this.disabledAgents.clear();
2893
- for (const { key: key2, agent_type, is_enabled } of agentKeys) {
3881
+ for (const {
3882
+ key: key2,
3883
+ agent_type,
3884
+ is_enabled,
3885
+ is_configured
3886
+ } of agentKeys) {
3887
+ const configured = is_configured ?? Boolean(key2);
3888
+ if (!configured) continue;
2894
3889
  const agent = this.createAgent(agent_type, key2);
2895
3890
  if (!agent) continue;
2896
3891
  this.agents.set(agent_type, agent);
@@ -2914,10 +3909,47 @@ var OwneySDK = class {
2914
3909
  }
2915
3910
  createAgent(agentId, key2) {
2916
3911
  if (agentId === "zyfai") {
3912
+ if (!key2) return null;
2917
3913
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2918
3914
  }
3915
+ if (agentId === "yieldseeker") {
3916
+ return new YieldseekerAgent(this.apiKey, {
3917
+ baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
3918
+ });
3919
+ }
2919
3920
  return null;
2920
3921
  }
3922
+ /**
3923
+ * Discover the agents actually returned by routing for this organization.
3924
+ * Consumers should use this instead of hardcoding a global agent roster.
3925
+ */
3926
+ async getAvailableAgents(options = {}) {
3927
+ await this.ensureAgentsInitialized();
3928
+ const { chainId, asset, includeDisabled = false } = options;
3929
+ const available = [];
3930
+ for (const [id, agent] of this.agents) {
3931
+ const isEnabled = !this.isAgentDisabled(id);
3932
+ if (!includeDisabled && !isEnabled) continue;
3933
+ if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
3934
+ continue;
3935
+ }
3936
+ if (asset !== void 0) {
3937
+ const supportsAsset = agent.supportedAssets.some(
3938
+ (entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
3939
+ );
3940
+ if (!supportsAsset) {
3941
+ continue;
3942
+ }
3943
+ }
3944
+ available.push({
3945
+ id,
3946
+ isEnabled,
3947
+ supportedChainIds: agent.supportedChainIds,
3948
+ supportedAssets: agent.supportedAssets
3949
+ });
3950
+ }
3951
+ return available;
3952
+ }
2921
3953
  // --- Account lifecycle ---
2922
3954
  /**
2923
3955
  * Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
@@ -2929,7 +3961,7 @@ var OwneySDK = class {
2929
3961
  * If provided, ALL specified agents must support the chainId or the call
2930
3962
  * throws before activating any agent.
2931
3963
  */
2932
- async activateAgent(chainId, agentId) {
3964
+ async activateAgent(chainId, agentId, asset) {
2933
3965
  const state = this.requireState();
2934
3966
  await this.ensureAgentsInitialized();
2935
3967
  if (agentId !== void 0) {
@@ -2965,9 +3997,7 @@ var OwneySDK = class {
2965
3997
  this.activeAgents.add(id);
2966
3998
  }
2967
3999
  state.chainId = chainId;
2968
- this.activateAgentsInTurn(agents, state, chainId).catch((error) => {
2969
- console.error("activateAgent background init failed:", error);
2970
- });
4000
+ await this.activateAgentsInTurn(agents, state, chainId, asset);
2971
4001
  return;
2972
4002
  }
2973
4003
  const compatible = [...this.agents.values()].filter(
@@ -2988,11 +4018,7 @@ var OwneySDK = class {
2988
4018
  const enabledCompatible = compatible.filter(
2989
4019
  (agent) => !this.isAgentDisabled(agent.id)
2990
4020
  );
2991
- this.activateAgentsInTurn(enabledCompatible, state, chainId).catch(
2992
- (error) => {
2993
- console.error("activateAgent background init failed:", error);
2994
- }
2995
- );
4021
+ await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
2996
4022
  }
2997
4023
  /**
2998
4024
  * Activate agents ONE AT A TIME, each followed by its org policy.
@@ -3012,11 +4038,11 @@ var OwneySDK = class {
3012
4038
  * rethrown (matching the previous `Promise.all` rejection) once all agents
3013
4039
  * have had a chance to activate.
3014
4040
  */
3015
- async activateAgentsInTurn(agents, state, chainId) {
4041
+ async activateAgentsInTurn(agents, state, chainId, asset) {
3016
4042
  let firstError = null;
3017
4043
  for (const agent of agents) {
3018
4044
  try {
3019
- await agent.activateAgent(state, chainId);
4045
+ await agent.activateAgent(state, chainId, asset);
3020
4046
  await this.applyOrgPolicyTo(agent, state, chainId);
3021
4047
  } catch (error) {
3022
4048
  if (firstError === null) {
@@ -3144,7 +4170,8 @@ var OwneySDK = class {
3144
4170
  amount,
3145
4171
  asset,
3146
4172
  callback
3147
- )
4173
+ ),
4174
+ this.routingApiBaseUrl
3148
4175
  );
3149
4176
  let approvalAttempted = false;
3150
4177
  for (; ; ) {
@@ -3224,10 +4251,10 @@ var OwneySDK = class {
3224
4251
  try {
3225
4252
  const balance = await agent.getBalances(state, chainId);
3226
4253
  const target = asset.toLowerCase();
3227
- const token = balance.tokens.find(
4254
+ const token2 = balance.tokens.find(
3228
4255
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3229
4256
  );
3230
- return !!token && Number(token.amount) > 0;
4257
+ return !!token2 && Number(token2.amount) > 0;
3231
4258
  } catch {
3232
4259
  return false;
3233
4260
  }
@@ -3278,9 +4305,9 @@ var OwneySDK = class {
3278
4305
  const { asset, amount, agentId } = options;
3279
4306
  const state = this.requireState();
3280
4307
  const chainId = this.requireChainId();
3281
- const token = asset;
4308
+ const token2 = asset;
3282
4309
  const assetInfo = SupportedAssets.find(
3283
- (a) => a.chainId === chainId && a.symbol === token
4310
+ (a) => a.chainId === chainId && a.symbol === token2
3284
4311
  );
3285
4312
  if (!assetInfo) {
3286
4313
  throw new OwneyError(
@@ -3294,7 +4321,8 @@ var OwneySDK = class {
3294
4321
  return withFailureReporting(
3295
4322
  this.apiKey,
3296
4323
  agent.id,
3297
- () => agent.withdraw(state, chainId, token, amount)
4324
+ () => agent.withdraw(state, chainId, token2, amount),
4325
+ this.routingApiBaseUrl
3298
4326
  );
3299
4327
  }
3300
4328
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
@@ -3303,12 +4331,17 @@ var OwneySDK = class {
3303
4331
  const agentErrors2 = {};
3304
4332
  for (const agent of eligibleAgents) {
3305
4333
  try {
3306
- results2[agent.id] = await agent.withdraw(state, chainId, token);
4334
+ results2[agent.id] = await agent.withdraw(state, chainId, token2);
3307
4335
  } catch (err) {
3308
4336
  console.error(`withdraw failed for agent "${agent.id}":`, err);
3309
4337
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
3310
4338
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3311
- void reportAgentFailure(this.apiKey, agent.id, code);
4339
+ void reportAgentFailure(
4340
+ this.apiKey,
4341
+ agent.id,
4342
+ code,
4343
+ this.routingApiBaseUrl
4344
+ );
3312
4345
  }
3313
4346
  }
3314
4347
  if (Object.keys(results2).length === 0) {
@@ -3325,6 +4358,10 @@ var OwneySDK = class {
3325
4358
  }
3326
4359
  const requested = BigInt(amount);
3327
4360
  const aggregated = await this.getBalances();
4361
+ const unavailableAgents = eligibleAgents.filter(
4362
+ (agent) => !(agent.id in aggregated.agentBalances)
4363
+ );
4364
+ const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
3328
4365
  const balances = projectAgentBalancesForAsset(
3329
4366
  eligibleAgents,
3330
4367
  aggregated.agentBalances,
@@ -3333,7 +4370,18 @@ var OwneySDK = class {
3333
4370
  assetInfo.decimals
3334
4371
  );
3335
4372
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
3336
- if (totalAvailable < requested) {
4373
+ if (totalAvailable === 0n && unavailableAgents.length > 0) {
4374
+ throw new OwneyError(
4375
+ "WITHDRAW_BALANCE_UNAVAILABLE",
4376
+ `Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
4377
+ {
4378
+ asset,
4379
+ unavailableAgents: unavailableAgentIds,
4380
+ agentErrors: aggregated.agentErrors
4381
+ }
4382
+ );
4383
+ }
4384
+ if (totalAvailable < requested && unavailableAgents.length === 0) {
3337
4385
  throw new OwneyError(
3338
4386
  "WITHDRAW_INSUFFICIENT_BALANCE",
3339
4387
  `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
@@ -3344,6 +4392,7 @@ var OwneySDK = class {
3344
4392
  }
3345
4393
  );
3346
4394
  }
4395
+ const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
3347
4396
  const disabledBalances = balances.filter(
3348
4397
  (b) => this.isAgentDisabled(b.agent.id)
3349
4398
  );
@@ -3352,7 +4401,7 @@ var OwneySDK = class {
3352
4401
  );
3353
4402
  const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
3354
4403
  disabledBalances,
3355
- requested
4404
+ plannedTarget
3356
4405
  );
3357
4406
  const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
3358
4407
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
@@ -3362,7 +4411,9 @@ var OwneySDK = class {
3362
4411
  }));
3363
4412
  const plans = [...disabledPlans, ...enabledPlans];
3364
4413
  const results = {};
3365
- const agentErrors = {};
4414
+ const agentErrors = {
4415
+ ...aggregated.agentErrors ?? {}
4416
+ };
3366
4417
  for (let i = 0; i < plans.length; i++) {
3367
4418
  const p = plans[i];
3368
4419
  if (p.planned === 0n) continue;
@@ -3370,14 +4421,19 @@ var OwneySDK = class {
3370
4421
  results[p.agent.id] = await p.agent.withdraw(
3371
4422
  state,
3372
4423
  chainId,
3373
- token,
4424
+ token2,
3374
4425
  p.planned.toString()
3375
4426
  );
3376
4427
  } catch (err) {
3377
4428
  console.error(`withdraw failed for agent "${p.agent.id}":`, err);
3378
4429
  agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
3379
4430
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
3380
- void reportAgentFailure(this.apiKey, p.agent.id, code);
4431
+ void reportAgentFailure(
4432
+ this.apiKey,
4433
+ p.agent.id,
4434
+ code,
4435
+ this.routingApiBaseUrl
4436
+ );
3381
4437
  const failedAmount = p.planned;
3382
4438
  p.planned = 0n;
3383
4439
  redistributeShare(plans, i, failedAmount);
@@ -3404,7 +4460,8 @@ var OwneySDK = class {
3404
4460
  requested: amount,
3405
4461
  withdrawn: withdrawn.toString(),
3406
4462
  partialResults: results,
3407
- agentErrors
4463
+ agentErrors,
4464
+ ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
3408
4465
  }
3409
4466
  );
3410
4467
  }
@@ -3445,7 +4502,12 @@ var OwneySDK = class {
3445
4502
  continue;
3446
4503
  }
3447
4504
  const reason = settledResult.reason;
3448
- agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
4505
+ const message = reason instanceof Error ? reason.message : String(reason);
4506
+ agentErrors[agentId2] = message;
4507
+ console.error(
4508
+ `[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
4509
+ reason
4510
+ );
3449
4511
  }
3450
4512
  if (successCount === 0) {
3451
4513
  throw new OwneyError(
@@ -3457,7 +4519,8 @@ var OwneySDK = class {
3457
4519
  return {
3458
4520
  totalBalance: String(totalBalance),
3459
4521
  totalBalanceAsset: "usdc",
3460
- agentBalances: results
4522
+ agentBalances: results,
4523
+ ...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
3461
4524
  };
3462
4525
  }
3463
4526
  /**
@@ -3551,7 +4614,10 @@ var OwneySDK = class {
3551
4614
  Promise.all(
3552
4615
  entries.map(async ([id, agent]) => {
3553
4616
  const b = await agent.getBalances(state, chainId);
3554
- return [id, Number(b.totalBalance)];
4617
+ return [
4618
+ id,
4619
+ balanceForApyScope(b, chainId, tokenSymbol)
4620
+ ];
3555
4621
  })
3556
4622
  )
3557
4623
  ]);
@@ -3579,10 +4645,12 @@ var OwneySDK = class {
3579
4645
  }
3580
4646
  }
3581
4647
  const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
4648
+ const history = aggregateApyHistory(results, balances);
3582
4649
  return {
3583
4650
  totalApy: String(totalApy),
3584
4651
  agentApy: results,
3585
- apyByChainAndAsset
4652
+ apyByChainAndAsset,
4653
+ history
3586
4654
  };
3587
4655
  }
3588
4656
  /**
@@ -3704,30 +4772,30 @@ var OwneySDK = class {
3704
4772
  const state = this.requireState();
3705
4773
  const chainId = this.requireChainId();
3706
4774
  this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
3707
- const token = SPONSORED_WETH_BY_CHAIN[chainId];
3708
- if (!token) {
4775
+ const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
4776
+ if (!token2) {
3709
4777
  throw new OwneyError(
3710
4778
  "CHAIN_UNSUPPORTED",
3711
4779
  `No sponsored WETH on chain ${chainId}`
3712
4780
  );
3713
4781
  }
3714
4782
  const provider = this.requireConnectedProvider();
3715
- const wallet = createWalletClient({
4783
+ const wallet = createWalletClient3({
3716
4784
  account: state.walletAddress,
3717
4785
  chain: VIEM_CHAIN2[chainId],
3718
- transport: custom(provider)
4786
+ transport: custom3(provider)
3719
4787
  });
3720
4788
  const hash = await wallet.writeContract({
3721
- address: token,
4789
+ address: token2,
3722
4790
  abi: ERC20_ALLOWANCE_ABI,
3723
4791
  functionName: "approve",
3724
4792
  args: [PERMIT2_ADDRESS, MAX_UINT256],
3725
4793
  account: state.walletAddress,
3726
4794
  chain: VIEM_CHAIN2[chainId]
3727
4795
  });
3728
- const publicClient = createPublicClient2({
4796
+ const publicClient = createPublicClient4({
3729
4797
  chain: VIEM_CHAIN2[chainId],
3730
- transport: custom(provider)
4798
+ transport: custom3(provider)
3731
4799
  });
3732
4800
  const receipt = await publicClient.waitForTransactionReceipt({
3733
4801
  hash,
@@ -3763,7 +4831,9 @@ var OwneySDK = class {
3763
4831
  return this.getAgent(agentId).getAgentApy(days, agentOptions);
3764
4832
  }
3765
4833
  const results = {};
3766
- const agentEntries = [...this.agents.entries()];
4834
+ const agentEntries = [...this.agents.entries()].filter(
4835
+ ([id]) => !this.isAgentDisabled(id)
4836
+ );
3767
4837
  const apyResults = await Promise.all(
3768
4838
  agentEntries.map(async ([id, agent]) => {
3769
4839
  const apy = await agent.getAgentApy(days, agentOptions);
@@ -3836,13 +4906,13 @@ var OwneySDK = class {
3836
4906
  };
3837
4907
 
3838
4908
  // src/agents/zyfai/zyfai.siwx.ts
3839
- import { getAddress } from "viem";
3840
- import { SiweMessage } from "siwe";
4909
+ import { getAddress as getAddress3 } from "viem";
4910
+ import { SiweMessage as SiweMessage2 } from "siwe";
3841
4911
  import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
3842
4912
 
3843
4913
  // src/agents/zyfai/zyfai.siwx-cache.ts
3844
- var KEY_PREFIX2 = "owney.siwx.session";
3845
- var storage2 = () => {
4914
+ var KEY_PREFIX3 = "owney.siwx.session";
4915
+ var storage3 = () => {
3846
4916
  if (typeof window === "undefined") return null;
3847
4917
  try {
3848
4918
  return window.localStorage;
@@ -3850,8 +4920,8 @@ var storage2 = () => {
3850
4920
  return null;
3851
4921
  }
3852
4922
  };
3853
- var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
3854
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
4923
+ var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
4924
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
3855
4925
  var memorySiwxSessions = /* @__PURE__ */ new Map();
3856
4926
  var readLegacySiwxSession = (store, address) => {
3857
4927
  if (!store) return null;
@@ -3882,8 +4952,8 @@ var readLegacySiwxSession = (store, address) => {
3882
4952
  };
3883
4953
  var readSiwxSession = (address, chainId) => {
3884
4954
  if (typeof window === "undefined") return null;
3885
- const key2 = buildKey2(address);
3886
- const store = storage2();
4955
+ const key2 = buildKey3(address);
4956
+ const store = storage3();
3887
4957
  let raw = null;
3888
4958
  try {
3889
4959
  raw = store?.getItem(key2) ?? null;
@@ -3911,18 +4981,18 @@ var readSiwxSession = (address, chainId) => {
3911
4981
  };
3912
4982
  var writeSiwxSession = (address, _chainId, session) => {
3913
4983
  if (typeof window === "undefined") return;
3914
- const key2 = buildKey2(address);
4984
+ const key2 = buildKey3(address);
3915
4985
  memorySiwxSessions.set(key2, session);
3916
- const store = storage2();
4986
+ const store = storage3();
3917
4987
  try {
3918
4988
  store?.setItem(key2, JSON.stringify(session));
3919
4989
  } catch {
3920
4990
  }
3921
4991
  };
3922
4992
  var clearSiwxSession = (address, _chainId) => {
3923
- const key2 = buildKey2(address);
4993
+ const key2 = buildKey3(address);
3924
4994
  memorySiwxSessions.delete(key2);
3925
- const store = storage2();
4995
+ const store = storage3();
3926
4996
  try {
3927
4997
  store?.removeItem(key2);
3928
4998
  } catch {
@@ -3962,8 +5032,8 @@ function buildSIWXConfig(deps) {
3962
5032
  statement: STATEMENT,
3963
5033
  issuedAt,
3964
5034
  toString() {
3965
- return new SiweMessage({
3966
- address: getAddress(accountAddress),
5035
+ return new SiweMessage2({
5036
+ address: getAddress3(accountAddress),
3967
5037
  chainId: numericChainId(chainId),
3968
5038
  domain,
3969
5039
  uri,
@@ -4005,7 +5075,7 @@ function buildSIWXConfig(deps) {
4005
5075
  const persistSession = async (session) => {
4006
5076
  const address = session.data.accountAddress;
4007
5077
  const id = numericChainId(session.data.chainId);
4008
- const message = new SiweMessage(session.message);
5078
+ const message = new SiweMessage2(session.message);
4009
5079
  const login = await post("/auth/login", {
4010
5080
  message,
4011
5081
  signature: session.signature,
@@ -4053,6 +5123,7 @@ export {
4053
5123
  NotConnectedError,
4054
5124
  OwneyError,
4055
5125
  OwneySDK,
5126
+ YieldseekerAgent,
4056
5127
  createOwneySIWX,
4057
5128
  setOwneyDebug
4058
5129
  };