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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -181,18 +181,18 @@ function tokenDecimals(symbol, explicit) {
181
181
  return explicit;
182
182
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
183
183
  }
184
- function mapDeposit(raw) {
184
+ function mapDeposit(raw2) {
185
185
  return {
186
- txHash: raw.txHash,
187
- smartWallet: raw.smartWallet,
188
- amount: raw.amount
186
+ txHash: raw2.txHash,
187
+ smartWallet: raw2.smartWallet,
188
+ amount: raw2.amount
189
189
  };
190
190
  }
191
- function mapWithdraw(raw) {
191
+ function mapWithdraw(raw2) {
192
192
  return {
193
- txHash: raw.txHash,
194
- type: raw.type,
195
- amount: raw.amount
193
+ txHash: raw2.txHash,
194
+ type: raw2.type,
195
+ amount: raw2.amount
196
196
  };
197
197
  }
198
198
  var CHAIN_ID_TO_NAME = {
@@ -211,10 +211,10 @@ function resolveChainId(chain) {
211
211
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
212
212
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
213
213
  }
214
- function mapPendingAllocations(raw) {
215
- if (!Array.isArray(raw)) return void 0;
214
+ function mapPendingAllocations(raw2) {
215
+ if (!Array.isArray(raw2)) return void 0;
216
216
  const pending = [];
217
- for (const entry of raw) {
217
+ for (const entry of raw2) {
218
218
  if (typeof entry !== "object" || entry === null) continue;
219
219
  const e = entry;
220
220
  if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
@@ -237,8 +237,8 @@ function mapPendingAllocations(raw) {
237
237
  }
238
238
  return pending.length > 0 ? pending : void 0;
239
239
  }
240
- function mapBalances(raw, _chainId, smartWallet) {
241
- const portfolio = raw.portfolio;
240
+ function mapBalances(raw2, _chainId, smartWallet) {
241
+ const portfolio = raw2.portfolio;
242
242
  const portfolioByChain = portfolio.portfolioByChain ?? {};
243
243
  let totalBalance = 0;
244
244
  const tokens = [];
@@ -307,8 +307,8 @@ function sumTokenValues(tokens) {
307
307
  function sumTokenEarnings(tokens) {
308
308
  return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
309
309
  }
310
- function mapEarnings(raw, smartWallet) {
311
- const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
310
+ function mapEarnings(raw2, smartWallet) {
311
+ const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
312
312
  const tokens = [];
313
313
  for (const [chainIdKey, tokensBySymbol] of Object.entries(
314
314
  totalEarningsByChain
@@ -327,22 +327,22 @@ function mapEarnings(raw, smartWallet) {
327
327
  return {
328
328
  smartWallet,
329
329
  lifetimeEarnings: sumTokenEarnings(
330
- raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
330
+ raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
331
331
  ),
332
332
  tokens
333
333
  };
334
334
  }
335
- function mapWeightedApyByChain(raw) {
336
- if (!raw) return void 0;
335
+ function mapWeightedApyByChain(raw2) {
336
+ if (!raw2) return void 0;
337
337
  const out = {};
338
- for (const [chainKey, tokenApy] of Object.entries(raw)) {
338
+ for (const [chainKey, tokenApy] of Object.entries(raw2)) {
339
339
  const chainId = Number(chainKey);
340
340
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
341
341
  const perAsset = {};
342
342
  for (const [symbol, apy] of Object.entries(tokenApy)) {
343
- const token2 = symbol;
344
- if (!SUPPORTED_TOKENS.includes(token2)) continue;
345
- perAsset[token2] = apy;
343
+ const token = symbol;
344
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
345
+ perAsset[token] = apy;
346
346
  }
347
347
  if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
348
348
  }
@@ -361,15 +361,15 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
361
361
  }
362
362
  return totalBalance > 0 ? weightedSum / totalBalance : null;
363
363
  }
364
- function mapApyHistory(raw, chainId, tokenSymbol) {
365
- const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
364
+ function mapApyHistory(raw2, chainId, tokenSymbol) {
365
+ const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
366
366
  date,
367
367
  apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
368
368
  })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
369
369
  return {
370
- walletAddress: raw.walletAddress,
371
- weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
372
- apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
370
+ walletAddress: raw2.walletAddress,
371
+ weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
372
+ apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
373
373
  history
374
374
  };
375
375
  }
@@ -461,23 +461,23 @@ function mapEntries(rawEntries, chainId) {
461
461
  };
462
462
  });
463
463
  }
464
- function mapUserProfile(raw, userAddress) {
464
+ function mapUserProfile(raw2, userAddress) {
465
465
  return {
466
466
  address: userAddress,
467
- smartWallet: raw.smartWallet || "",
468
- chains: raw.chains || [],
469
- strategy: raw.strategy,
470
- hasActiveSessionKey: raw.hasActiveSessionKey || false,
471
- protocols: raw.protocols || [],
472
- splitting: raw.splitting,
473
- minSplits: raw.minSplits
467
+ smartWallet: raw2.smartWallet || "",
468
+ chains: raw2.chains || [],
469
+ strategy: raw2.strategy,
470
+ hasActiveSessionKey: raw2.hasActiveSessionKey || false,
471
+ protocols: raw2.protocols || [],
472
+ splitting: raw2.splitting,
473
+ minSplits: raw2.minSplits
474
474
  };
475
475
  }
476
- function mapApyByStrategy(raw) {
476
+ function mapApyByStrategy(raw2) {
477
477
  const apyPerAsset = {};
478
478
  let apySum = 0;
479
479
  let apyCount = 0;
480
- for (const entry of raw.data) {
480
+ for (const entry of raw2.data) {
481
481
  const supported = SupportedAssets.find(
482
482
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
483
483
  );
@@ -511,23 +511,23 @@ function computeAllocationApy(positions) {
511
511
  weightedSum += apy * value;
512
512
  totalValue += value;
513
513
  const chainId = resolveChainId(p.chain);
514
- const token2 = p.asset;
514
+ const token = p.asset;
515
515
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
516
- if (!SUPPORTED_TOKENS.includes(token2)) continue;
516
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
517
517
  const perAsset = buckets[chainId] ?? {};
518
- const cell = perAsset[token2] ?? { weightedSum: 0, value: 0 };
518
+ const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
519
519
  cell.weightedSum += apy * value;
520
520
  cell.value += value;
521
- perAsset[token2] = cell;
521
+ perAsset[token] = cell;
522
522
  buckets[chainId] = perAsset;
523
523
  }
524
524
  const apyByChainAndAsset = {};
525
525
  for (const [chainKey, perAsset] of Object.entries(buckets)) {
526
526
  const chainId = Number(chainKey);
527
527
  const out = {};
528
- for (const [token2, cell] of Object.entries(perAsset ?? {})) {
528
+ for (const [token, cell] of Object.entries(perAsset ?? {})) {
529
529
  if (cell && cell.value > 0) {
530
- out[token2] = cell.weightedSum / cell.value;
530
+ out[token] = cell.weightedSum / cell.value;
531
531
  }
532
532
  }
533
533
  if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
@@ -541,9 +541,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
541
541
  function topicToAddress(topic) {
542
542
  return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
543
543
  }
544
- function extractWithdrawnAmount(logs, recipient, token2, decimals) {
544
+ function extractWithdrawnAmount(logs, recipient, token, decimals) {
545
545
  const wantRecipient = recipient.toLowerCase();
546
- const wantToken = token2.toLowerCase();
546
+ const wantToken = token.toLowerCase();
547
547
  let total = 0n;
548
548
  let matched = false;
549
549
  for (const log of logs) {
@@ -569,11 +569,11 @@ var InvalidHistoryCursorError = class extends Error {
569
569
  function encodeHistoryCursor(payload) {
570
570
  return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
571
571
  }
572
- function decodeHistoryCursor(token2) {
573
- if (!token2) throw new InvalidHistoryCursorError("Cursor is empty.");
572
+ function decodeHistoryCursor(token) {
573
+ if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
574
574
  let parsed;
575
575
  try {
576
- const json = Buffer.from(token2, "base64").toString("utf8");
576
+ const json = Buffer.from(token, "base64").toString("utf8");
577
577
  parsed = JSON.parse(json);
578
578
  } catch {
579
579
  throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
@@ -596,8 +596,8 @@ var storage = () => {
596
596
  };
597
597
  var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
598
598
  var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
599
- var isJwtExpired = (token2) => {
600
- const parts = token2.split(".");
599
+ var isJwtExpired = (token) => {
600
+ const parts = token.split(".");
601
601
  if (parts.length !== 3) return false;
602
602
  try {
603
603
  const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
@@ -644,15 +644,15 @@ var readSession = (address, _chainId) => {
644
644
  if (typeof window === "undefined") return null;
645
645
  const key2 = buildKey(address);
646
646
  const store = storage();
647
- let raw = null;
647
+ let raw2 = null;
648
648
  try {
649
- raw = store?.getItem(key2) ?? null;
649
+ raw2 = store?.getItem(key2) ?? null;
650
650
  } catch {
651
- raw = null;
651
+ raw2 = null;
652
652
  }
653
- if (raw) {
653
+ if (raw2) {
654
654
  try {
655
- const parsed = JSON.parse(raw);
655
+ const parsed = JSON.parse(raw2);
656
656
  if (isFreshSession(parsed)) return parsed;
657
657
  } catch {
658
658
  }
@@ -820,8 +820,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
820
820
  }
821
821
  return result;
822
822
  }
823
- function flattenAvailablePools(raw) {
824
- const byChain = raw ?? {};
823
+ function flattenAvailablePools(raw2) {
824
+ const byChain = raw2 ?? {};
825
825
  const names = [];
826
826
  for (const byToken of Object.values(byChain ?? {})) {
827
827
  for (const entry of Object.values(byToken ?? {})) {
@@ -1332,8 +1332,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1332
1332
  const poolResults = await Promise.all(
1333
1333
  universe.map(async (protocol) => {
1334
1334
  try {
1335
- const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1336
- return [protocol.id, flattenAvailablePools(raw)];
1335
+ const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
1336
+ return [protocol.id, flattenAvailablePools(raw2)];
1337
1337
  } catch (error) {
1338
1338
  console.warn(
1339
1339
  `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
@@ -1399,14 +1399,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1399
1399
  async readWalletState(ownerAddress) {
1400
1400
  try {
1401
1401
  const { portfolio } = await this.sdk.getPositions(ownerAddress);
1402
- const raw = portfolio;
1402
+ const raw2 = portfolio;
1403
1403
  debugLog("zyfai:onboard", "wallet state from getPositions", {
1404
- predeployed: raw?.predeployed,
1405
- hasActiveSessionKey: raw?.hasActiveSessionKey
1404
+ predeployed: raw2?.predeployed,
1405
+ hasActiveSessionKey: raw2?.hasActiveSessionKey
1406
1406
  });
1407
1407
  return {
1408
- predeployed: raw?.predeployed,
1409
- hasActiveSessionKey: raw?.hasActiveSessionKey
1408
+ predeployed: raw2?.predeployed,
1409
+ hasActiveSessionKey: raw2?.hasActiveSessionKey
1410
1410
  };
1411
1411
  } catch (error) {
1412
1412
  console.warn(
@@ -1686,48 +1686,48 @@ var ZyfaiAgent = class _ZyfaiAgent {
1686
1686
  return { txHash, smartWallet, amount };
1687
1687
  }
1688
1688
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
1689
- const raw = await this.sdk.depositFunds(
1689
+ const raw2 = await this.sdk.depositFunds(
1690
1690
  this.getAddress(),
1691
1691
  validChainId,
1692
1692
  amount,
1693
1693
  asset,
1694
1694
  "aggressive"
1695
1695
  );
1696
- return mapDeposit(raw);
1696
+ return mapDeposit(raw2);
1697
1697
  } catch (error) {
1698
1698
  throw error;
1699
1699
  }
1700
1700
  }
1701
1701
  // --- IAgent: Fund operations ---
1702
- async withdraw(state, chainId, token2, amount) {
1702
+ async withdraw(state, chainId, token, amount) {
1703
1703
  const validChainId = isValidChainId(chainId);
1704
1704
  await this.ensureConnected(state, validChainId);
1705
- const raw = await this.sdk.withdrawFunds(
1705
+ const raw2 = await this.sdk.withdrawFunds(
1706
1706
  this.getAddress(),
1707
1707
  validChainId,
1708
1708
  amount,
1709
- token2
1709
+ token
1710
1710
  );
1711
- if (!raw.success) {
1711
+ if (!raw2.success) {
1712
1712
  throw new OwneyError(
1713
1713
  "WITHDRAW_FAILED",
1714
- raw.message || "Zyfai withdraw failed.",
1715
- { chainId: validChainId, token: token2, amount, response: raw },
1714
+ raw2.message || "Zyfai withdraw failed.",
1715
+ { chainId: validChainId, token, amount, response: raw2 },
1716
1716
  this.id
1717
1717
  );
1718
1718
  }
1719
- return mapWithdraw(raw);
1719
+ return mapWithdraw(raw2);
1720
1720
  }
1721
1721
  // --- IAgent: Portfolio reads ---
1722
1722
  async getBalances(state, chainId) {
1723
1723
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
1724
- const raw = await this.sdk.getPortfolio(this.getAddress());
1725
- return mapBalances(raw, validChainId, smartWallet);
1724
+ const raw2 = await this.sdk.getPortfolio(this.getAddress());
1725
+ return mapBalances(raw2, validChainId, smartWallet);
1726
1726
  }
1727
1727
  async getEarnings(state, chainId) {
1728
1728
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1729
- const raw = await this.sdk.getOnchainEarnings(smartWallet);
1730
- return mapEarnings(raw, smartWallet);
1729
+ const raw2 = await this.sdk.getOnchainEarnings(smartWallet);
1730
+ return mapEarnings(raw2, smartWallet);
1731
1731
  }
1732
1732
  async refreshEarnings(state, chainId) {
1733
1733
  if (this.earningsRefreshInFlight) return this.earningsRefreshInFlight;
@@ -1747,8 +1747,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1747
1747
  }
1748
1748
  async getAccountApy(state, chainId, days, tokenSymbol) {
1749
1749
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1750
- const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
1751
- return mapApyHistory(raw, chainId, tokenSymbol);
1750
+ const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
1751
+ return mapApyHistory(raw2, chainId, tokenSymbol);
1752
1752
  }
1753
1753
  /**
1754
1754
  * Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
@@ -1783,7 +1783,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1783
1783
  const matched = [];
1784
1784
  let backendExhausted = false;
1785
1785
  for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
1786
- const raw = await this.sdk.getHistory(smartWallet, validChainId, {
1786
+ const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
1787
1787
  limit: backendPageSize,
1788
1788
  offset,
1789
1789
  fromDate: options?.fromDate,
@@ -1794,13 +1794,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
1794
1794
  // asset's rows and handing back a page that filters to nothing.
1795
1795
  assetType
1796
1796
  });
1797
- raw.data.forEach((entry, idx) => {
1797
+ raw2.data.forEach((entry, idx) => {
1798
1798
  if (entry.chainId === validChainId) {
1799
1799
  matched.push({ entry, rawIdx: offset + idx });
1800
1800
  }
1801
1801
  });
1802
- offset += raw.data.length;
1803
- if (raw.data.length < backendPageSize) {
1802
+ offset += raw2.data.length;
1803
+ if (raw2.data.length < backendPageSize) {
1804
1804
  backendExhausted = true;
1805
1805
  break;
1806
1806
  }
@@ -1822,18 +1822,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
1822
1822
  }
1823
1823
  async getUserProfile(state, chainId) {
1824
1824
  await this.connectAuth(state, chainId);
1825
- const raw = await this.sdk.getUserDetails();
1825
+ const raw2 = await this.sdk.getUserDetails();
1826
1826
  debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
1827
1827
  asset: "USDC (default \u2014 no asset passed)",
1828
- splitting: raw.splitting,
1829
- minSplits: raw.minSplits,
1830
- strategy: raw.strategy,
1831
- chains: raw.chains,
1832
- protocolCount: raw.protocols?.length,
1833
- hasActiveSessionKey: raw.hasActiveSessionKey,
1834
- smartWallet: raw.smartWallet
1828
+ splitting: raw2.splitting,
1829
+ minSplits: raw2.minSplits,
1830
+ strategy: raw2.strategy,
1831
+ chains: raw2.chains,
1832
+ protocolCount: raw2.protocols?.length,
1833
+ hasActiveSessionKey: raw2.hasActiveSessionKey,
1834
+ smartWallet: raw2.smartWallet
1835
1835
  });
1836
- return mapUserProfile(raw, this.connectedAddress);
1836
+ return mapUserProfile(raw2, this.connectedAddress);
1837
1837
  }
1838
1838
  async ensureAutoSelectProtocols(state, chainId, asset) {
1839
1839
  await this.connectAuth(state, chainId);
@@ -1852,14 +1852,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1852
1852
  }
1853
1853
  // --- IAgent: Discovery (no wallet required) ---
1854
1854
  async getAgentApy(days, options) {
1855
- const raw = await this.sdk.getAPYPerStrategy(
1855
+ const raw2 = await this.sdk.getAPYPerStrategy(
1856
1856
  false,
1857
1857
  DayFilterMapping[days],
1858
1858
  "aggressive",
1859
1859
  options?.chainId,
1860
1860
  options?.tokenSymbol
1861
1861
  );
1862
- return mapApyByStrategy(raw);
1862
+ return mapApyByStrategy(raw2);
1863
1863
  }
1864
1864
  };
1865
1865
 
@@ -1925,10 +1925,10 @@ function buildTransferWithAuthorizationTypedData(input) {
1925
1925
  message: input.message
1926
1926
  };
1927
1927
  }
1928
- async function readTokenMeta(publicClient, token2) {
1928
+ async function readTokenMeta(publicClient, token) {
1929
1929
  const [tokenName, tokenVersion] = await Promise.all([
1930
- publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "name" }),
1931
- publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1930
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
1931
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1932
1932
  ]);
1933
1933
  return { tokenName, tokenVersion };
1934
1934
  }
@@ -1940,6 +1940,45 @@ function randomAuthNonce() {
1940
1940
 
1941
1941
  // src/lib/sponsor-client.ts
1942
1942
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1943
+ async function postPaymasterIntent(input) {
1944
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1945
+ let res;
1946
+ try {
1947
+ res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
1948
+ method: "POST",
1949
+ headers: {
1950
+ "content-type": "application/json",
1951
+ "x-owney-api-key": input.apiKey,
1952
+ Authorization: `Signature ${input.yieldseekerSignature}`
1953
+ },
1954
+ body: JSON.stringify(input.body)
1955
+ });
1956
+ } catch (networkError) {
1957
+ throw new OwneyError(
1958
+ "SPONSOR_REQUEST_FAILED",
1959
+ `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1960
+ { cause: String(networkError), safeToFallback: true }
1961
+ );
1962
+ }
1963
+ const text = await res.text();
1964
+ let parsed = null;
1965
+ try {
1966
+ parsed = JSON.parse(text);
1967
+ } catch {
1968
+ }
1969
+ if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
1970
+ throw new OwneyError(
1971
+ "SPONSOR_REQUEST_FAILED",
1972
+ `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1973
+ {
1974
+ statusCode: res.status,
1975
+ responseBody: text.slice(0, 500),
1976
+ safeToFallback: true
1977
+ }
1978
+ );
1979
+ }
1980
+ return parsed.data;
1981
+ }
1943
1982
  async function postSponsorTransferAuth(input) {
1944
1983
  const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1945
1984
  let res;
@@ -1949,7 +1988,7 @@ async function postSponsorTransferAuth(input) {
1949
1988
  headers: {
1950
1989
  "content-type": "application/json",
1951
1990
  "x-owney-api-key": input.apiKey,
1952
- ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
1991
+ ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
1953
1992
  },
1954
1993
  body: JSON.stringify(input.body)
1955
1994
  });
@@ -1990,7 +2029,7 @@ async function postSponsorPermit2Transfer(input) {
1990
2029
  headers: {
1991
2030
  "content-type": "application/json",
1992
2031
  "x-owney-api-key": input.apiKey,
1993
- ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
2032
+ ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
1994
2033
  },
1995
2034
  body: JSON.stringify(input.body)
1996
2035
  });
@@ -2118,17 +2157,17 @@ function randomPermit2Nonce() {
2118
2157
  globalThis.crypto.getRandomValues(bytes);
2119
2158
  return BigInt((0, import_viem3.bytesToHex)(bytes));
2120
2159
  }
2121
- async function readPermit2Allowance(publicClient, token2, owner) {
2160
+ async function readPermit2Allowance(publicClient, token, owner) {
2122
2161
  return publicClient.readContract({
2123
- address: token2,
2162
+ address: token,
2124
2163
  abi: ERC20_ALLOWANCE_ABI,
2125
2164
  functionName: "allowance",
2126
2165
  args: [owner, PERMIT2_ADDRESS]
2127
2166
  });
2128
2167
  }
2129
- async function readErc20Balance(publicClient, token2, owner) {
2168
+ async function readErc20Balance(publicClient, token, owner) {
2130
2169
  return publicClient.readContract({
2131
- address: token2,
2170
+ address: token,
2132
2171
  abi: ERC20_ALLOWANCE_ABI,
2133
2172
  functionName: "balanceOf",
2134
2173
  args: [owner]
@@ -2160,8 +2199,8 @@ function makeSponsoredDepositCallback(deps) {
2160
2199
  return makeVerificationAwareDepositCallback(
2161
2200
  async (smartWallet, chainId, amount, verification) => {
2162
2201
  const cid = chainId;
2163
- const token2 = deps.tokenAddressByChain[cid];
2164
- if (!token2) {
2202
+ const token = deps.tokenAddressByChain[cid];
2203
+ if (!token) {
2165
2204
  throw new OwneyError(
2166
2205
  "CHAIN_UNSUPPORTED",
2167
2206
  `No sponsored token configured for chain ${chainId}`
@@ -2171,12 +2210,12 @@ function makeSponsoredDepositCallback(deps) {
2171
2210
  const wallet = deps.getWalletClient(cid);
2172
2211
  await ensureWalletOnChain(pub, wallet, cid);
2173
2212
  try {
2174
- const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
2213
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2175
2214
  if (balance < BigInt(amount)) {
2176
2215
  throw new OwneyError(
2177
2216
  "DEPOSIT_INSUFFICIENT_BALANCE",
2178
2217
  "Insufficient balance for this deposit.",
2179
- { token: token2, chainId: cid, balance: balance.toString(), amount }
2218
+ { token, chainId: cid, balance: balance.toString(), amount }
2180
2219
  );
2181
2220
  }
2182
2221
  } catch (err) {
@@ -2186,14 +2225,14 @@ function makeSponsoredDepositCallback(deps) {
2186
2225
  err instanceof Error ? err.message : String(err)
2187
2226
  );
2188
2227
  }
2189
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token2);
2228
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2190
2229
  const validAfter = 0n;
2191
2230
  const validBefore = BigInt(
2192
2231
  Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2193
2232
  );
2194
2233
  const nonce = randomAuthNonce();
2195
2234
  const typedData = buildTransferWithAuthorizationTypedData({
2196
- token: token2,
2235
+ token,
2197
2236
  chainId: cid,
2198
2237
  tokenName,
2199
2238
  tokenVersion,
@@ -2217,7 +2256,7 @@ function makeSponsoredDepositCallback(deps) {
2217
2256
  ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
2218
2257
  body: {
2219
2258
  chainId: cid,
2220
- token: token2,
2259
+ token,
2221
2260
  from: deps.ownerAddress,
2222
2261
  to: smartWallet,
2223
2262
  value: amount,
@@ -2226,7 +2265,11 @@ function makeSponsoredDepositCallback(deps) {
2226
2265
  nonce,
2227
2266
  authSignature,
2228
2267
  tokenName,
2229
- tokenVersion
2268
+ tokenVersion,
2269
+ ...verification?.agentId === "yieldseeker" ? {
2270
+ yieldseekerUserId: verification.userId,
2271
+ yieldseekerAgentId: verification.yieldseekerAgentId
2272
+ } : {}
2230
2273
  }
2231
2274
  });
2232
2275
  return result.txHash;
@@ -2240,7 +2283,11 @@ var import_viem4 = require("viem");
2240
2283
  var import_chains2 = require("viem/chains");
2241
2284
 
2242
2285
  // src/agents/yieldseeker/yieldseeker.auth-cache.ts
2243
- var KEY_PREFIX2 = "owney.yieldseeker.session";
2286
+ var KEY_PREFIX2 = "owney.yieldseeker.session.v4";
2287
+ var INVALIDATED_KEY_PREFIXES = [
2288
+ "owney.yieldseeker.session",
2289
+ "owney.yieldseeker.session.v3"
2290
+ ];
2244
2291
  var storage2 = () => {
2245
2292
  if (typeof window === "undefined") return null;
2246
2293
  try {
@@ -2250,6 +2297,18 @@ var storage2 = () => {
2250
2297
  }
2251
2298
  };
2252
2299
  var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2300
+ var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
2301
+ (prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
2302
+ );
2303
+ var clearInvalidatedSessions = (store, address, chainId) => {
2304
+ for (const key2 of invalidatedKeys(address, chainId)) {
2305
+ memorySessions2.delete(key2);
2306
+ try {
2307
+ store?.removeItem(key2);
2308
+ } catch {
2309
+ }
2310
+ }
2311
+ };
2253
2312
  var memorySessions2 = /* @__PURE__ */ new Map();
2254
2313
  var isValidSession = (session) => {
2255
2314
  if (!session?.token) return false;
@@ -2264,15 +2323,16 @@ var readYieldseekerSession = (address, chainId) => {
2264
2323
  if (typeof window === "undefined") return null;
2265
2324
  const key2 = buildKey2(address, chainId);
2266
2325
  const store = storage2();
2267
- let raw = null;
2326
+ clearInvalidatedSessions(store, address, chainId);
2327
+ let raw2 = null;
2268
2328
  try {
2269
- raw = store?.getItem(key2) ?? null;
2329
+ raw2 = store?.getItem(key2) ?? null;
2270
2330
  } catch {
2271
- raw = null;
2331
+ raw2 = null;
2272
2332
  }
2273
- if (raw) {
2333
+ if (raw2) {
2274
2334
  try {
2275
- const parsed = JSON.parse(raw);
2335
+ const parsed = JSON.parse(raw2);
2276
2336
  if (isValidSession(parsed)) return parsed.token;
2277
2337
  } catch {
2278
2338
  }
@@ -2288,9 +2348,9 @@ var readYieldseekerSession = (address, chainId) => {
2288
2348
  if (cached) memorySessions2.delete(key2);
2289
2349
  return null;
2290
2350
  };
2291
- var writeYieldseekerSession = (address, chainId, token2) => {
2351
+ var writeYieldseekerSession = (address, chainId, token) => {
2292
2352
  if (typeof window === "undefined") return;
2293
- const session = { token: token2 };
2353
+ const session = { token };
2294
2354
  if (!isValidSession(session)) return;
2295
2355
  const key2 = buildKey2(address, chainId);
2296
2356
  memorySessions2.set(key2, session);
@@ -2304,6 +2364,7 @@ var clearYieldseekerSession = (address, chainId) => {
2304
2364
  const key2 = buildKey2(address, chainId);
2305
2365
  memorySessions2.delete(key2);
2306
2366
  const store = storage2();
2367
+ clearInvalidatedSessions(store, address, chainId);
2307
2368
  try {
2308
2369
  store?.removeItem(key2);
2309
2370
  } catch {
@@ -2322,8 +2383,8 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2322
2383
  issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
2323
2384
  }).prepareMessage();
2324
2385
  }
2325
- function encodeYieldseekerAuthToken(token2) {
2326
- const bytes = new TextEncoder().encode(JSON.stringify(token2));
2386
+ function encodeYieldseekerAuthToken(token) {
2387
+ const bytes = new TextEncoder().encode(JSON.stringify(token));
2327
2388
  let binary = "";
2328
2389
  for (const byte of bytes) binary += String.fromCharCode(byte);
2329
2390
  return btoa(binary);
@@ -2352,10 +2413,10 @@ var YieldseekerAuth = class {
2352
2413
  }
2353
2414
  const inFlight = this.pending.get(key2);
2354
2415
  if (inFlight) return inFlight;
2355
- const request = this.sign(state, chainId).then((token2) => {
2356
- this.tokens.set(key2, token2);
2357
- writeYieldseekerSession(scope.address, scope.chainId, token2);
2358
- return token2;
2416
+ const request = this.sign(state, chainId).then((token) => {
2417
+ this.tokens.set(key2, token);
2418
+ writeYieldseekerSession(scope.address, scope.chainId, token);
2419
+ return token;
2359
2420
  });
2360
2421
  this.pending.set(key2, request);
2361
2422
  try {
@@ -2406,6 +2467,72 @@ var YieldseekerAuth = class {
2406
2467
  }
2407
2468
  };
2408
2469
 
2470
+ // src/agents/yieldseeker/yieldseeker.identity-cache.ts
2471
+ var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
2472
+ var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
2473
+ var memoryIdentities = /* @__PURE__ */ new Map();
2474
+ var storage3 = () => {
2475
+ if (typeof window === "undefined") return null;
2476
+ try {
2477
+ return window.localStorage;
2478
+ } catch {
2479
+ return null;
2480
+ }
2481
+ };
2482
+ var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
2483
+ function valid(value, walletAddress, chainId, now) {
2484
+ return Boolean(
2485
+ value && typeof value.userId === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(value.userId) && value.walletAddress.toLowerCase() === walletAddress.toLowerCase() && value.chainId === chainId && Number.isSafeInteger(value.expiresAt) && value.expiresAt > now
2486
+ );
2487
+ }
2488
+ function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
2489
+ if (typeof window === "undefined") return null;
2490
+ const key2 = keyFor(walletAddress, chainId);
2491
+ const store = storage3();
2492
+ let parsed = null;
2493
+ try {
2494
+ const raw2 = store?.getItem(key2);
2495
+ parsed = raw2 ? JSON.parse(raw2) : null;
2496
+ } catch {
2497
+ parsed = null;
2498
+ }
2499
+ const candidate = parsed ?? memoryIdentities.get(key2);
2500
+ if (valid(candidate, walletAddress, chainId, now)) {
2501
+ memoryIdentities.set(key2, candidate);
2502
+ return { userId: candidate.userId };
2503
+ }
2504
+ memoryIdentities.delete(key2);
2505
+ try {
2506
+ store?.removeItem(key2);
2507
+ } catch {
2508
+ }
2509
+ return null;
2510
+ }
2511
+ function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
2512
+ if (typeof window === "undefined") return;
2513
+ const identity = {
2514
+ userId,
2515
+ walletAddress,
2516
+ chainId,
2517
+ expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
2518
+ };
2519
+ if (!valid(identity, walletAddress, chainId, now)) return;
2520
+ const key2 = keyFor(walletAddress, chainId);
2521
+ memoryIdentities.set(key2, identity);
2522
+ try {
2523
+ storage3()?.setItem(key2, JSON.stringify(identity));
2524
+ } catch {
2525
+ }
2526
+ }
2527
+ function clearYieldseekerIdentity(walletAddress, chainId) {
2528
+ const key2 = keyFor(walletAddress, chainId);
2529
+ memoryIdentities.delete(key2);
2530
+ try {
2531
+ storage3()?.removeItem(key2);
2532
+ } catch {
2533
+ }
2534
+ }
2535
+
2409
2536
  // src/agents/yieldseeker/yieldseeker.client.ts
2410
2537
  var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2411
2538
  function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
@@ -2456,7 +2583,7 @@ var YieldseekerApiClient = class {
2456
2583
  headers: {
2457
2584
  "Content-Type": "application/json",
2458
2585
  "x-owney-api-key": this.owneyApiKey,
2459
- ...options.signature ? { "X-Signature": options.signature } : {}
2586
+ ...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
2460
2587
  },
2461
2588
  body: options.body ? JSON.stringify(options.body) : void 0,
2462
2589
  signal: controller.signal
@@ -2490,24 +2617,20 @@ var YieldseekerApiClient = class {
2490
2617
 
2491
2618
  // src/agents/yieldseeker/yieldseeker.mapper.ts
2492
2619
  var import_viem5 = require("viem");
2493
- var REBALANCE_AMOUNT_DECIMALS = {
2494
- USDC: 6,
2495
- WETH: 18
2496
- };
2497
- function rebalanceAmount(value, tokenSymbol) {
2498
- const decimals = REBALANCE_AMOUNT_DECIMALS[tokenSymbol.toUpperCase()];
2499
- if (decimals === void 0) {
2500
- return invalid(
2501
- "history",
2502
- `unsupported rebalance token ${tokenSymbol || "<empty>"}`
2503
- );
2504
- }
2505
- try {
2506
- return (0, import_viem5.formatUnits)(BigInt(value), decimals);
2507
- } catch {
2508
- return invalid("history", `invalid rebalance amount ${value}`);
2620
+
2621
+ // src/agents/yieldseeker/yieldseeker.types.ts
2622
+ var YIELDSEEKER_ASSET_METADATA = {
2623
+ USDC: {
2624
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2625
+ decimals: 6
2626
+ },
2627
+ WETH: {
2628
+ address: "0x4200000000000000000000000000000000000006",
2629
+ decimals: 18
2509
2630
  }
2510
- }
2631
+ };
2632
+
2633
+ // src/agents/yieldseeker/yieldseeker.mapper.ts
2511
2634
  function invalid(endpoint, detail) {
2512
2635
  throw new OwneyError(
2513
2636
  "AGENT_INVALID_RESPONSE",
@@ -2516,193 +2639,346 @@ function invalid(endpoint, detail) {
2516
2639
  "yieldseeker"
2517
2640
  );
2518
2641
  }
2519
- function requireObject(value, endpoint) {
2520
- if (!value || typeof value !== "object" || Array.isArray(value)) {
2521
- return invalid(endpoint, "expected an object");
2642
+ function raw(value, endpoint) {
2643
+ if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
2644
+ return invalid(endpoint, "expected a base-10 integer string");
2522
2645
  }
2523
- return value;
2646
+ return BigInt(value);
2524
2647
  }
2525
- function requireEnvelope(value, key2, endpoint) {
2526
- const root = requireObject(value, endpoint);
2527
- return requireObject(root[key2], endpoint);
2648
+ function decimal(value, decimals, endpoint) {
2649
+ return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
2528
2650
  }
2529
- function token(value) {
2530
- return {
2531
- chain: value.chain,
2532
- chainId: value.chainId,
2533
- asset: value.asset,
2534
- amount: String(value.amount)
2535
- };
2651
+ function usd(rawAmount, decimals, price) {
2652
+ return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
2536
2653
  }
2537
- function position(value) {
2538
- return {
2539
- chain: String(value.chain),
2540
- protocol: String(value.protocol),
2541
- ...value.protocolId ? { protocolId: value.protocolId } : {},
2542
- ...value.pool ? { pool: value.pool } : {},
2543
- asset: String(value.asset),
2544
- amount: String(value.amount),
2545
- ...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
2546
- ...typeof value.apy === "number" ? { apy: value.apy } : {},
2547
- ...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
2548
- ...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
2549
- };
2654
+ function percent(value) {
2655
+ const result = Number(value);
2656
+ return Number.isFinite(result) ? result * 100 : 0;
2550
2657
  }
2551
- function mapApyMatrix(matrix) {
2552
- const mapped = {};
2553
- for (const [chain, assets] of Object.entries(matrix ?? {})) {
2554
- const chainId = Number(chain);
2555
- if (chainId !== 8453 || !assets || typeof assets !== "object") continue;
2556
- mapped[8453] = {};
2557
- for (const [asset, apy] of Object.entries(assets)) {
2558
- if ((asset === "USDC" || asset === "WETH") && typeof apy === "number") {
2559
- mapped[8453][asset] = apy;
2560
- }
2561
- }
2562
- }
2563
- return mapped;
2658
+ var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
2659
+ function publicApyAfterYieldseekerFee(value) {
2660
+ const grossPercent = percent(value);
2661
+ if (grossPercent <= 0) return grossPercent;
2662
+ const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
2663
+ return Math.round(netPercent * 1e12) / 1e12;
2664
+ }
2665
+ function riskAdjustedApyForDays(option, days) {
2666
+ if (days === "7D") return option.riskAdjustedApy7dAverage;
2667
+ if (days === "30D") return option.riskAdjustedApy30dAverage;
2668
+ return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
2564
2669
  }
2565
- function mapYieldseekerProfile(value) {
2566
- const profile = requireEnvelope(
2567
- value,
2568
- "profile",
2569
- "profile"
2670
+ function assetAddressValue(record, address) {
2671
+ const entry = Object.entries(record).find(
2672
+ ([key2]) => key2.toLowerCase() === address.toLowerCase()
2570
2673
  );
2571
- if (typeof profile.address !== "string" || typeof profile.smartWallet !== "string" || !Array.isArray(profile.chains) || typeof profile.hasActiveSessionKey !== "boolean" || !Array.isArray(profile.protocols)) {
2572
- return invalid("profile", "missing required fields");
2674
+ return entry?.[1] ?? "0";
2675
+ }
2676
+ function position(value, asset) {
2677
+ const option = value?.yieldOption;
2678
+ if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
2679
+ return invalid("yield positions", "missing vault metadata");
2573
2680
  }
2681
+ const decimals = YIELDSEEKER_ASSET_METADATA[asset].decimals;
2574
2682
  return {
2575
- address: profile.address,
2576
- smartWallet: profile.smartWallet,
2577
- chains: profile.chains.map(Number),
2578
- hasActiveSessionKey: profile.hasActiveSessionKey,
2579
- protocols: profile.protocols.map(String)
2683
+ chain: "BASE",
2684
+ protocol: option.provider,
2685
+ protocolId: option.address,
2686
+ pool: option.name,
2687
+ asset,
2688
+ amount: decimal(value.assetsRaw, decimals, "yield positions"),
2689
+ amountRaw: String(value.assetsRaw),
2690
+ apy: percent(option.riskAdjustedApy),
2691
+ tvl: Number(option.totalDepositsUsd),
2692
+ liquidity: Number(option.withdrawableDepositsUsd)
2580
2693
  };
2581
2694
  }
2582
- function mapYieldseekerBalances(value) {
2583
- const balances = requireEnvelope(
2584
- value,
2585
- "balances",
2586
- "balances"
2587
- );
2588
- if (typeof balances.smartWallet !== "string" || typeof balances.totalBalance !== "string" || typeof balances.totalBalanceAsset !== "string" || !Array.isArray(balances.tokens) || !Array.isArray(balances.positions)) {
2589
- return invalid("balances", "missing required fields");
2695
+ function mapYieldseekerBalances(contexts) {
2696
+ const tokens = [];
2697
+ const positions = [];
2698
+ let totalUsd = 0;
2699
+ for (const context of contexts) {
2700
+ const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
2701
+ const idle = assetAddressValue(
2702
+ context.snapshot.tokenBalances,
2703
+ metadata.address
2704
+ );
2705
+ tokens.push({
2706
+ chain: "BASE",
2707
+ chainId: 8453,
2708
+ asset: context.asset,
2709
+ amount: decimal(idle, metadata.decimals, "snapshot")
2710
+ });
2711
+ positions.push(
2712
+ ...context.positions.map((entry) => position(entry, context.asset))
2713
+ );
2714
+ totalUsd += usd(
2715
+ raw(context.snapshot.totalValueBase, "snapshot"),
2716
+ context.snapshot.baseAssetDecimals,
2717
+ context.snapshot.baseAssetPriceUsd
2718
+ );
2590
2719
  }
2591
2720
  return {
2592
- smartWallet: balances.smartWallet,
2593
- totalBalance: balances.totalBalance,
2594
- totalBalanceAsset: balances.totalBalanceAsset.toLowerCase(),
2595
- tokens: balances.tokens.map(token),
2596
- positions: balances.positions.map(position)
2721
+ ...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
2722
+ totalBalance: String(totalUsd),
2723
+ totalBalanceAsset: "usdc",
2724
+ tokens,
2725
+ positions
2597
2726
  };
2598
2727
  }
2599
- function mapYieldseekerEarnings(value) {
2600
- const earnings = requireEnvelope(
2601
- value,
2602
- "earnings",
2603
- "earnings"
2604
- );
2605
- if (typeof earnings.smartWallet !== "string" || typeof earnings.lifetimeEarnings !== "number" || !Array.isArray(earnings.tokens)) {
2606
- return invalid("earnings", "missing required fields");
2728
+ function mapYieldseekerEarnings(contexts) {
2729
+ const tokens = [];
2730
+ let lifetimeEarnings = 0;
2731
+ for (const context of contexts) {
2732
+ const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
2733
+ tokens.push({
2734
+ chain: "BASE",
2735
+ chainId: 8453,
2736
+ asset: context.asset,
2737
+ amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
2738
+ });
2739
+ lifetimeEarnings += usd(
2740
+ amount,
2741
+ context.snapshot.baseAssetDecimals,
2742
+ context.snapshot.baseAssetPriceUsd
2743
+ );
2607
2744
  }
2608
2745
  return {
2609
- smartWallet: earnings.smartWallet,
2610
- lifetimeEarnings: earnings.lifetimeEarnings,
2611
- tokens: earnings.tokens.map(token)
2746
+ smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2747
+ lifetimeEarnings,
2748
+ tokens
2612
2749
  };
2613
2750
  }
2614
- function mapYieldseekerApy(value) {
2615
- const apy = requireEnvelope(
2616
- value,
2617
- "apy",
2618
- "apy"
2619
- );
2620
- if (typeof apy.walletAddress !== "string" || typeof apy.weightedApyAfterFee !== "number" || !apy.apyByChainAndAsset || typeof apy.apyByChainAndAsset !== "object" || !Array.isArray(apy.history)) {
2621
- return invalid("apy", "missing required fields");
2751
+ function apyForDays(snapshot, days) {
2752
+ if (days === "7D") return percent(snapshot.apy7d);
2753
+ if (days === "30D") return percent(snapshot.apy30d);
2754
+ return percent((snapshot.apy7d + snapshot.apy30d) / 2);
2755
+ }
2756
+ function dailyApy(point) {
2757
+ const total = raw(point.totalValueBase, "historic position");
2758
+ const earned = raw(point.dailyYieldBase, "historic position");
2759
+ const principal = total - earned;
2760
+ if (principal <= 0n || earned === 0n) return 0;
2761
+ return Number(earned) / Number(principal) * 365 * 100;
2762
+ }
2763
+ function aggregateHistory(contexts, dayCount) {
2764
+ const byDate = /* @__PURE__ */ new Map();
2765
+ for (const context of contexts) {
2766
+ const points = context.historic?.dailyYieldSnapshots ?? [];
2767
+ for (const point of points.slice(-dayCount)) {
2768
+ const valueUsd = usd(
2769
+ raw(point.totalValueBase, "historic position"),
2770
+ point.baseAssetDecimals,
2771
+ point.baseAssetPriceUsd
2772
+ );
2773
+ const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
2774
+ current.weighted += dailyApy(point) * valueUsd;
2775
+ current.valueUsd += valueUsd;
2776
+ byDate.set(point.date, current);
2777
+ }
2622
2778
  }
2779
+ return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
2780
+ date,
2781
+ apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
2782
+ }));
2783
+ }
2784
+ function mapYieldseekerApy(walletAddress, contexts, days) {
2785
+ let weighted = 0;
2786
+ let totalUsd = 0;
2787
+ const byAsset = {};
2788
+ for (const context of contexts) {
2789
+ const valueUsd = usd(
2790
+ raw(context.snapshot.totalValueBase, "snapshot"),
2791
+ context.snapshot.baseAssetDecimals,
2792
+ context.snapshot.baseAssetPriceUsd
2793
+ );
2794
+ const apy = apyForDays(context.snapshot, days);
2795
+ weighted += apy * valueUsd;
2796
+ totalUsd += valueUsd;
2797
+ byAsset[context.asset] = apy;
2798
+ }
2799
+ const dayCount = Number(days.slice(0, -1));
2623
2800
  return {
2624
- walletAddress: apy.walletAddress,
2625
- weightedApyAfterFee: apy.weightedApyAfterFee,
2626
- apyByChainAndAsset: mapApyMatrix(apy.apyByChainAndAsset),
2627
- history: apy.history.map((point) => ({
2628
- date: String(point.date),
2629
- apy: Number(point.apy)
2630
- }))
2801
+ walletAddress,
2802
+ weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
2803
+ apyByChainAndAsset: { 8453: byAsset },
2804
+ history: aggregateHistory(contexts, dayCount)
2631
2805
  };
2632
2806
  }
2633
- function historyAction(value) {
2634
- const normalized = value.trim().toLowerCase();
2635
- if (normalized === "rebalance") return "Rebalance";
2636
- if (normalized === "deposit") return "Deposit";
2637
- if (normalized === "top up" || normalized === "topup") return "Top up";
2638
- if (normalized === "withdraw" || normalized === "withdrawal")
2639
- return "Withdraw";
2640
- if (normalized === "earned" || normalized === "earnings") return "Earned";
2641
- return invalid("history", `unsupported action ${value}`);
2807
+ function actionType(value) {
2808
+ const normalized = value.toLowerCase();
2809
+ if (normalized.includes("deposit")) return "Deposit";
2810
+ if (normalized.includes("withdraw")) return "Withdraw";
2811
+ if (normalized.includes("yield") || normalized.includes("earn"))
2812
+ return "Earned";
2813
+ return "Rebalance";
2814
+ }
2815
+ function transactionHashes(details) {
2816
+ if (!details) return [];
2817
+ const values = [
2818
+ details.transactionHash,
2819
+ details.txHash,
2820
+ ...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
2821
+ ...Array.isArray(details.txHashes) ? details.txHashes : []
2822
+ ];
2823
+ return values.filter(
2824
+ (value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
2825
+ ).filter((value, index, all) => all.indexOf(value) === index);
2642
2826
  }
2643
- function historyItem(value) {
2827
+ function actionEntry(action) {
2828
+ if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
2644
2829
  return {
2645
2830
  agent: "yieldseeker",
2646
- action: historyAction(value.action),
2647
- date: String(value.date),
2648
- oldApy: value.oldApy === null ? null : String(value.oldApy),
2649
- newApy: value.newApy === null ? null : String(value.newApy),
2650
- transactions: (value.transactions ?? []).map((transaction) => ({
2651
- txHashes: transaction.txHashes.map(String),
2652
- ...typeof transaction.chainId === "number" ? { chainId: transaction.chainId } : {},
2653
- ...typeof transaction.tokenSymbol === "string" ? { tokenSymbol: transaction.tokenSymbol } : {},
2654
- ...typeof transaction.amount === "string" ? { amount: transaction.amount } : {}
2655
- })),
2656
- rebalanceLog: (value.rebalanceLog ?? []).map((entry) => ({
2657
- fromProtocol: String(entry.fromProtocol),
2658
- toProtocol: String(entry.toProtocol),
2659
- ...entry.fromPool ? { fromPool: entry.fromPool } : {},
2660
- ...entry.toPool ? { toPool: entry.toPool } : {},
2661
- tokenSymbol: String(entry.tokenSymbol),
2662
- // Unlike Yieldseeker's other read-response amounts, live history
2663
- // rebalance amounts are returned in the token's smallest unit. Normalize
2664
- // them at the adapter boundary so every Owney consumer receives the
2665
- // shared human-readable decimal shape.
2666
- amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
2667
- status: entry.status
2668
- }))
2831
+ action: actionType(action.actionType),
2832
+ date: action.createdDate,
2833
+ oldApy: null,
2834
+ newApy: null,
2835
+ transactions: [
2836
+ {
2837
+ txHashes: transactionHashes(action.details),
2838
+ chainId: 8453
2839
+ }
2840
+ ],
2841
+ rebalanceLog: []
2669
2842
  };
2670
2843
  }
2671
- function mapYieldseekerHistory(value) {
2672
- const history = requireEnvelope(
2673
- value,
2674
- "history",
2675
- "history"
2676
- );
2677
- if (!Array.isArray(history.data) || typeof history.hasMore !== "boolean") {
2678
- return invalid("history", "missing required fields");
2844
+ function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses) {
2845
+ const from = movement.fromAddress.toLowerCase();
2846
+ const to = movement.toAddress.toLowerCase();
2847
+ const owner = ownerAddress.toLowerCase();
2848
+ const agentWallet = wallet.walletAddress.toLowerCase();
2849
+ const baseAsset = agent.assetAddress.toLowerCase();
2850
+ if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
2851
+ return void 0;
2852
+ }
2853
+ let action;
2854
+ if (from === owner && to === agentWallet) {
2855
+ action = "Top up";
2856
+ } else if (from === agentWallet && to === owner) {
2857
+ action = "Withdraw";
2858
+ } else if (from === agentWallet && vaultAddresses.has(to)) {
2859
+ action = "Deposit";
2860
+ }
2861
+ if (!action) return void 0;
2862
+ return {
2863
+ agent: "yieldseeker",
2864
+ action,
2865
+ date: movement.blockDate,
2866
+ oldApy: null,
2867
+ newApy: null,
2868
+ transactions: [
2869
+ {
2870
+ txHashes: [movement.transactionHash],
2871
+ chainId: agent.chainId,
2872
+ tokenSymbol: asset,
2873
+ amount: decimal(
2874
+ movement.assetAmount,
2875
+ YIELDSEEKER_ASSET_METADATA[asset].decimals,
2876
+ "historic position"
2877
+ )
2878
+ }
2879
+ ],
2880
+ rebalanceLog: []
2881
+ };
2882
+ }
2883
+ function mapYieldseekerHistory(contexts, options) {
2884
+ const entries = contexts.flatMap((context) => {
2885
+ const vaultAddresses = new Set(
2886
+ context.positions.map(
2887
+ (position2) => position2.yieldOption.address.toLowerCase()
2888
+ )
2889
+ );
2890
+ return [
2891
+ ...(context.historic?.movements ?? []).map(
2892
+ (movement) => movementEntry(
2893
+ movement,
2894
+ context.wallet,
2895
+ context.agent,
2896
+ context.asset,
2897
+ options.ownerAddress,
2898
+ vaultAddresses
2899
+ )
2900
+ ),
2901
+ ...(context.actions ?? []).map(actionEntry)
2902
+ ].filter((entry) => entry !== void 0);
2903
+ });
2904
+ const filtered = entries.filter(
2905
+ (entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
2906
+ ).filter((entry, index, all) => {
2907
+ const transactionHash = entry.transactions[0]?.txHashes[0];
2908
+ if (!transactionHash) return true;
2909
+ return all.findIndex(
2910
+ (candidate) => candidate.action === entry.action && candidate.transactions[0]?.txHashes[0] === transactionHash
2911
+ ) === index;
2912
+ }).sort((left, right) => right.date.localeCompare(left.date));
2913
+ return {
2914
+ data: filtered.slice(0, options.limit),
2915
+ // v1 returns the whole action/movement collection and defines no cursor.
2916
+ // Report a terminal page so callers never loop over the same prefix.
2917
+ hasMore: false
2918
+ };
2919
+ }
2920
+ function mapYieldseekerProfile(address, contexts) {
2921
+ const protocols = /* @__PURE__ */ new Set();
2922
+ for (const context of contexts) {
2923
+ for (const current of context.positions) {
2924
+ if (current.yieldOption?.provider) {
2925
+ protocols.add(String(current.yieldOption.provider));
2926
+ }
2927
+ }
2679
2928
  }
2680
2929
  return {
2681
- data: history.data.map(historyItem),
2682
- hasMore: history.hasMore,
2683
- ...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
2930
+ address,
2931
+ smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2932
+ chains: contexts.length > 0 ? [8453] : [],
2933
+ hasActiveSessionKey: contexts.some(
2934
+ (context) => context.wallet.initializedDate != null
2935
+ ),
2936
+ protocols: [...protocols]
2684
2937
  };
2685
2938
  }
2686
- function mapYieldseekerAgentApy(value) {
2687
- const agentApy = requireEnvelope(
2688
- value,
2689
- "agentApy",
2690
- "agent APY"
2691
- );
2692
- if (typeof agentApy.averageApy !== "number") {
2693
- return invalid("agent APY", "missing averageApy");
2939
+ function mapYieldseekerAgentApy(options, days) {
2940
+ const perAsset = {};
2941
+ const all = [];
2942
+ for (const entry of options) {
2943
+ const apys = entry.yieldOptions.map(
2944
+ (option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
2945
+ ).filter(Number.isFinite);
2946
+ if (apys.length === 0) continue;
2947
+ const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
2948
+ perAsset[entry.asset] = average;
2949
+ all.push(average);
2694
2950
  }
2695
2951
  return {
2696
- averageApy: agentApy.averageApy,
2697
- ...agentApy.detailedApys ? {
2698
- detailedApys: {
2699
- apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
2700
- }
2701
- } : {}
2952
+ averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
2953
+ detailedApys: { apyPerAsset: { 8453: perAsset } }
2702
2954
  };
2703
2955
  }
2704
2956
 
2705
2957
  // src/agents/yieldseeker/yieldseeker.agent.ts
2958
+ var OWNEY_AGENT_NAME = "owney";
2959
+ var YIELDSEEKER_AGENT_WALLET_ABI = [
2960
+ {
2961
+ type: "function",
2962
+ name: "withdrawAssetToUser",
2963
+ stateMutability: "nonpayable",
2964
+ inputs: [
2965
+ { name: "recipient", type: "address" },
2966
+ { name: "asset", type: "address" },
2967
+ { name: "amount", type: "uint256" }
2968
+ ],
2969
+ outputs: []
2970
+ },
2971
+ {
2972
+ type: "function",
2973
+ name: "withdrawAllAssetToUser",
2974
+ stateMutability: "nonpayable",
2975
+ inputs: [
2976
+ { name: "recipient", type: "address" },
2977
+ { name: "asset", type: "address" }
2978
+ ],
2979
+ outputs: []
2980
+ }
2981
+ ];
2706
2982
  function query(params) {
2707
2983
  const search = new URLSearchParams();
2708
2984
  for (const [key2, value] of Object.entries(params)) {
@@ -2720,7 +2996,7 @@ var YieldseekerAgent = class {
2720
2996
  chainId: 8453,
2721
2997
  chain: "BASE",
2722
2998
  assets: [
2723
- { symbol: "USDC", minDepositAmount: "1" },
2999
+ { symbol: "USDC", minDepositAmount: "10000000" },
2724
3000
  { symbol: "WETH", minDepositAmount: "1" }
2725
3001
  ]
2726
3002
  }
@@ -2729,7 +3005,9 @@ var YieldseekerAgent = class {
2729
3005
  auth;
2730
3006
  transactionExecutor;
2731
3007
  unwindReceiptWaiter;
2732
- activated = /* @__PURE__ */ new Set();
3008
+ agentContexts = /* @__PURE__ */ new Map();
3009
+ users = /* @__PURE__ */ new Map();
3010
+ pendingAgents = /* @__PURE__ */ new Map();
2733
3011
  constructor(owneyApiKey, options = {}) {
2734
3012
  this.api = new YieldseekerApiClient(
2735
3013
  owneyApiKey,
@@ -2742,14 +3020,19 @@ var YieldseekerAgent = class {
2742
3020
  }
2743
3021
  async disconnect() {
2744
3022
  this.auth.clear();
2745
- this.activated.clear();
3023
+ for (const key2 of this.users.keys()) {
3024
+ const [walletAddress, chainId] = key2.split(":");
3025
+ clearYieldseekerIdentity(walletAddress, Number(chainId));
3026
+ }
3027
+ this.users.clear();
3028
+ this.agentContexts.clear();
3029
+ this.pendingAgents.clear();
2746
3030
  }
2747
3031
  async activateAgent(state, chainId, asset) {
2748
3032
  this.assertChain(chainId);
2749
- await this.auth.getToken(state, chainId);
2750
- if (asset === "USDC" || asset === "WETH") {
2751
- await this.ensureActivated(state, chainId, asset);
2752
- }
3033
+ const targetAsset = asset ?? "USDC";
3034
+ this.assertAsset(targetAsset);
3035
+ await this.ensureAgent(state, chainId, targetAsset);
2753
3036
  }
2754
3037
  async deposit(state, chainId, amount, asset, depositCallback) {
2755
3038
  this.assertChain(chainId);
@@ -2762,42 +3045,48 @@ var YieldseekerAgent = class {
2762
3045
  this.id
2763
3046
  );
2764
3047
  }
2765
- await this.ensureActivated(state, chainId, asset);
2766
- const response = await this.walletRequest(
2767
- state,
2768
- chainId,
2769
- "/deposits",
2770
- { method: "POST", body: { chainId, asset, amount } }
2771
- );
2772
- const deposit = response?.deposit;
2773
- if (!deposit?.transaction || typeof deposit.amount !== "string" || deposit.amount !== amount || typeof deposit.smartWallet !== "string" || !(0, import_viem6.isAddress)(deposit.smartWallet)) {
2774
- throw this.invalidResponse("deposit");
2775
- }
2776
- if (depositCallback) {
2777
- provideDepositVerificationContext(depositCallback, {
2778
- agentId: "yieldseeker",
2779
- // Re-read after /deposits so an authentication retry cannot forward
2780
- // the stale token that Yieldseeker just rejected.
2781
- signature: await this.auth.getToken(state, chainId)
2782
- });
2783
- }
2784
- const txHash = depositCallback ? await depositCallback(deposit.smartWallet, chainId, amount) : await this.submitTransaction(state, chainId, deposit.transaction);
2785
- const confirmation = await this.walletRequest(
2786
- state,
2787
- chainId,
2788
- "/deposits/confirm",
2789
- { method: "POST", body: { chainId, asset } }
2790
- );
2791
- if (typeof confirmation?.confirmation?.autoseekQueued !== "boolean") {
2792
- throw this.invalidResponse("deposit confirmation", {
2793
- transactionHash: txHash,
2794
- transactionConfirmed: true
2795
- });
3048
+ const context = await this.ensureAgent(state, chainId, asset);
3049
+ let txHash;
3050
+ try {
3051
+ if (depositCallback) {
3052
+ provideDepositVerificationContext(depositCallback, {
3053
+ agentId: "yieldseeker",
3054
+ signature: await this.auth.getToken(state, chainId),
3055
+ userId: context.user.userId,
3056
+ yieldseekerAgentId: context.agent.agentId
3057
+ });
3058
+ txHash = await depositCallback(
3059
+ context.wallet.walletAddress,
3060
+ chainId,
3061
+ amount
3062
+ );
3063
+ await this.waitForReceipt(state, chainId, txHash);
3064
+ } else {
3065
+ txHash = await this.submitTransaction(state, chainId, {
3066
+ from: (0, import_viem6.getAddress)(state.walletAddress),
3067
+ to: YIELDSEEKER_ASSET_METADATA[asset].address,
3068
+ data: (0, import_viem6.encodeFunctionData)({
3069
+ abi: import_viem6.erc20Abi,
3070
+ functionName: "transfer",
3071
+ args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
3072
+ }),
3073
+ value: "0",
3074
+ chainId
3075
+ });
3076
+ }
3077
+ await this.deployAfterFunding(state, chainId, context);
3078
+ } finally {
3079
+ await this.refreshSnapshotAfterMovement(
3080
+ state,
3081
+ chainId,
3082
+ context,
3083
+ "deposit"
3084
+ );
2796
3085
  }
2797
3086
  return {
2798
3087
  txHash,
2799
- smartWallet: deposit.smartWallet,
2800
- amount: deposit.amount
3088
+ smartWallet: context.wallet.walletAddress,
3089
+ amount
2801
3090
  };
2802
3091
  }
2803
3092
  async withdraw(state, chainId, asset, amount) {
@@ -2811,198 +3100,455 @@ var YieldseekerAgent = class {
2811
3100
  this.id
2812
3101
  );
2813
3102
  }
2814
- const balances = await this.getBalances(state, chainId);
2815
- const decimals = asset === "USDC" ? 6 : 18;
2816
- const targetAsset = asset.toUpperCase();
2817
- const idle = balances.tokens.filter(
2818
- (token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
2819
- ).reduce((total, token2) => total + (0, import_viem6.parseUnits)(token2.amount, decimals), 0n);
2820
- const positions = (balances.positions ?? []).filter(
2821
- (position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
2822
- ).map((position2) => ({
2823
- position: position2,
2824
- amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : (0, import_viem6.parseUnits)(position2.amount, decimals)
2825
- }));
2826
- const deployed = positions.reduce(
2827
- (total, position2) => total + position2.amount,
2828
- 0n
2829
- );
2830
- const totalAvailable = idle + deployed;
2831
- const requested = amount === void 0 ? totalAvailable : BigInt(amount);
2832
- if (requested > totalAvailable) {
3103
+ const context = await this.findAgent(state, chainId, asset);
3104
+ if (!context) {
2833
3105
  throw new OwneyError(
2834
3106
  "WITHDRAW_INSUFFICIENT_BALANCE",
2835
- `Requested withdrawal "${requested.toString()}" exceeds available Yieldseeker balance "${totalAvailable.toString()}" for asset "${asset}".`,
2836
- {
2837
- asset,
2838
- requested: requested.toString(),
2839
- available: totalAvailable.toString()
2840
- },
3107
+ `No Yieldseeker ${asset} agent exists for this wallet.`,
3108
+ { asset, available: "0" },
2841
3109
  this.id
2842
3110
  );
2843
3111
  }
2844
- const requiredFromPositions = requested > idle ? requested - idle : 0n;
2845
- const unwindPlans = [];
2846
- let remainingToUnwind = requiredFromPositions;
2847
- for (const { position: position2, amount: positionAmount } of positions) {
2848
- if (remainingToUnwind === 0n) break;
2849
- if (positionAmount <= 0n) continue;
2850
- if (!position2.protocolId || !(0, import_viem6.isAddress)(position2.protocolId)) {
2851
- throw this.invalidResponse("position", {
2852
- reason: "A deployed position is missing its vault address.",
2853
- protocol: position2.protocol,
2854
- pool: position2.pool
3112
+ try {
3113
+ const portfolio = await this.loadPortfolioContext(
3114
+ state,
3115
+ chainId,
3116
+ context
3117
+ );
3118
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3119
+ const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
3120
+ ([address]) => address.toLowerCase() === metadata.address.toLowerCase()
3121
+ );
3122
+ const idle = BigInt(idleEntry?.[1] ?? "0");
3123
+ const deployed = portfolio.positions.reduce(
3124
+ (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
3125
+ 0n
3126
+ );
3127
+ const totalAvailable = idle + deployed;
3128
+ const requested = amount === void 0 ? totalAvailable : BigInt(amount);
3129
+ if (requested > totalAvailable) {
3130
+ throw new OwneyError(
3131
+ "WITHDRAW_INSUFFICIENT_BALANCE",
3132
+ `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
3133
+ {
3134
+ asset,
3135
+ requested: requested.toString(),
3136
+ available: totalAvailable.toString()
3137
+ },
3138
+ this.id
3139
+ );
3140
+ }
3141
+ let remaining = requested > idle ? requested - idle : 0n;
3142
+ for (const position2 of portfolio.positions) {
3143
+ if (remaining === 0n) break;
3144
+ const available = BigInt(position2.withdrawableAssetsRaw);
3145
+ if (available <= 0n) continue;
3146
+ const assetsRaw = available < remaining ? available : remaining;
3147
+ const response = await this.walletRequest(
3148
+ state,
3149
+ chainId,
3150
+ this.agentPath(context, "withdraw-from-position"),
3151
+ {
3152
+ method: "POST",
3153
+ body: {
3154
+ chainId,
3155
+ vaultAddress: position2.yieldOption.address,
3156
+ assetsRaw: assetsRaw.toString()
3157
+ }
3158
+ }
3159
+ );
3160
+ if (!this.isTransactionHash(response?.transactionHash)) {
3161
+ throw this.invalidResponse("position withdrawal");
3162
+ }
3163
+ await this.waitForReceipt(state, chainId, response.transactionHash);
3164
+ remaining -= assetsRaw;
3165
+ }
3166
+ if (remaining > 0n) {
3167
+ throw this.invalidResponse("yield positions", {
3168
+ reason: "Withdrawable positions could not cover the request.",
3169
+ remaining: remaining.toString()
2855
3170
  });
2856
3171
  }
2857
- const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
2858
- unwindPlans.push({
2859
- vaultAddress: (0, import_viem6.getAddress)(position2.protocolId),
2860
- amount: unwindAmount
3172
+ const account = (0, import_viem6.getAddress)(state.walletAddress);
3173
+ const txHash = await this.submitTransaction(state, chainId, {
3174
+ from: account,
3175
+ to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
3176
+ data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
3177
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3178
+ functionName: "withdrawAllAssetToUser",
3179
+ args: [account, metadata.address]
3180
+ }) : (0, import_viem6.encodeFunctionData)({
3181
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3182
+ functionName: "withdrawAssetToUser",
3183
+ args: [account, metadata.address, requested]
3184
+ }),
3185
+ value: "0",
3186
+ chainId
2861
3187
  });
2862
- remainingToUnwind -= unwindAmount;
3188
+ return {
3189
+ txHash,
3190
+ type: amount === void 0 ? "full" : "partial",
3191
+ amount: requested.toString()
3192
+ };
3193
+ } finally {
3194
+ await this.refreshSnapshotAfterMovement(
3195
+ state,
3196
+ chainId,
3197
+ context,
3198
+ "withdrawal"
3199
+ );
2863
3200
  }
2864
- if (remainingToUnwind > 0n) {
2865
- throw this.invalidResponse("balances", {
2866
- reason: "Deployed position balances could not cover the unwind.",
2867
- required: requiredFromPositions.toString(),
2868
- planned: (requiredFromPositions - remainingToUnwind).toString()
2869
- });
3201
+ }
3202
+ async getBalances(state, chainId) {
3203
+ this.assertChain(chainId);
3204
+ return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
3205
+ }
3206
+ async getEarnings(state, chainId) {
3207
+ this.assertChain(chainId);
3208
+ return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
3209
+ }
3210
+ async getAccountApy(state, chainId, days, tokenSymbol) {
3211
+ this.assertChain(chainId);
3212
+ const asset = tokenSymbol?.toUpperCase();
3213
+ if (asset !== void 0) this.assertAsset(asset);
3214
+ const contexts = await this.loadPortfolio(state, chainId, {
3215
+ ...asset ? { asset } : {},
3216
+ historic: true
3217
+ });
3218
+ return mapYieldseekerApy(state.walletAddress, contexts, days);
3219
+ }
3220
+ async getHistory(state, chainId, options) {
3221
+ this.assertChain(chainId);
3222
+ const asset = options?.tokenSymbol?.toUpperCase();
3223
+ if (asset !== void 0) this.assertAsset(asset);
3224
+ const contexts = await this.loadPortfolio(state, chainId, {
3225
+ ...asset ? { asset } : {},
3226
+ historic: true,
3227
+ actions: true
3228
+ });
3229
+ return mapYieldseekerHistory(contexts, {
3230
+ limit: options?.limit ?? 10,
3231
+ ownerAddress: state.walletAddress,
3232
+ ...options?.fromDate ? { fromDate: options.fromDate } : {},
3233
+ ...options?.toDate ? { toDate: options.toDate } : {}
3234
+ });
3235
+ }
3236
+ async getUserProfile(state, chainId) {
3237
+ this.assertChain(chainId);
3238
+ return mapYieldseekerProfile(
3239
+ state.walletAddress,
3240
+ await this.loadPortfolio(state, chainId, {})
3241
+ );
3242
+ }
3243
+ async getAgentApy(days, options) {
3244
+ this.assertOptionalChain(options?.chainId);
3245
+ const requested = options?.tokenSymbol?.toUpperCase();
3246
+ if (requested !== void 0) this.assertAsset(requested);
3247
+ const assets = requested ? [requested] : ["USDC", "WETH"];
3248
+ const values = await Promise.all(
3249
+ assets.map(async (asset) => {
3250
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3251
+ const response = await this.api.request(
3252
+ `/chains/8453/assets/${metadata.address}/yield-options`
3253
+ );
3254
+ if (!Array.isArray(response?.yieldOptions)) {
3255
+ throw this.invalidResponse("yield options");
3256
+ }
3257
+ return { asset, yieldOptions: response.yieldOptions };
3258
+ })
3259
+ );
3260
+ return mapYieldseekerAgentApy(values, days);
3261
+ }
3262
+ userKey(state, chainId) {
3263
+ return `${state.walletAddress.toLowerCase()}:${chainId}`;
3264
+ }
3265
+ contextKey(state, chainId, asset) {
3266
+ return `${this.userKey(state, chainId)}:${asset}`;
3267
+ }
3268
+ async resolveUser(state, chainId) {
3269
+ const key2 = this.userKey(state, chainId);
3270
+ const inMemory = this.users.get(key2);
3271
+ if (inMemory) return inMemory;
3272
+ const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
3273
+ if (persisted) {
3274
+ this.users.set(key2, persisted);
3275
+ return persisted;
2870
3276
  }
2871
- for (const plan of unwindPlans) {
2872
- const response2 = await this.walletRequest(
3277
+ const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
3278
+ let user = null;
3279
+ try {
3280
+ const login = await this.providerRequest(
2873
3281
  state,
2874
3282
  chainId,
2875
- "/positions/unwind",
3283
+ "/users/login-with-wallet",
3284
+ { method: "POST", body: { walletAddress } }
3285
+ );
3286
+ user = login?.user ?? null;
3287
+ if (!user) {
3288
+ throw this.invalidResponse("wallet login", {
3289
+ reason: "A successful login returned no user."
3290
+ });
3291
+ }
3292
+ } catch (error) {
3293
+ if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
3294
+ if (error instanceof OwneyError) throw error;
3295
+ throw this.mapApiError(error);
3296
+ }
3297
+ let created;
3298
+ try {
3299
+ created = await this.providerRequest(
3300
+ state,
3301
+ chainId,
3302
+ "/users",
3303
+ {
3304
+ method: "POST",
3305
+ body: {
3306
+ walletAddress,
3307
+ username: `owney-${walletAddress.slice(2).toLowerCase()}`
3308
+ }
3309
+ }
3310
+ );
3311
+ } catch (createError) {
3312
+ throw this.mapApiError(createError);
3313
+ }
3314
+ user = created?.user ?? null;
3315
+ }
3316
+ if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
3317
+ throw this.invalidResponse("wallet identity");
3318
+ }
3319
+ const resolved = { userId: user.userId };
3320
+ this.users.set(key2, resolved);
3321
+ writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
3322
+ return resolved;
3323
+ }
3324
+ forgetUser(state, chainId) {
3325
+ this.users.delete(this.userKey(state, chainId));
3326
+ clearYieldseekerIdentity(state.walletAddress, chainId);
3327
+ }
3328
+ async ensureAgent(state, chainId, asset) {
3329
+ const key2 = this.contextKey(state, chainId, asset);
3330
+ const cached = this.agentContexts.get(key2);
3331
+ if (cached) return cached;
3332
+ const pending = this.pendingAgents.get(key2);
3333
+ if (pending) return pending;
3334
+ const request = this.resolveAgent(state, chainId, asset, true).then(
3335
+ (context) => {
3336
+ if (!context) throw this.invalidResponse("agent creation");
3337
+ this.agentContexts.set(key2, context);
3338
+ return context;
3339
+ }
3340
+ );
3341
+ this.pendingAgents.set(key2, request);
3342
+ try {
3343
+ return await request;
3344
+ } finally {
3345
+ this.pendingAgents.delete(key2);
3346
+ }
3347
+ }
3348
+ async findAgent(state, chainId, asset) {
3349
+ const key2 = this.contextKey(state, chainId, asset);
3350
+ const cached = this.agentContexts.get(key2);
3351
+ if (cached) return cached;
3352
+ const context = await this.resolveAgent(state, chainId, asset, false);
3353
+ if (context) this.agentContexts.set(key2, context);
3354
+ return context;
3355
+ }
3356
+ async resolveAgent(state, chainId, asset, createIfMissing) {
3357
+ const user = await this.resolveUser(state, chainId);
3358
+ const response = await this.walletRequest(
3359
+ state,
3360
+ chainId,
3361
+ `/users/${user.userId}/agents`
3362
+ );
3363
+ if (!Array.isArray(response?.agents)) {
3364
+ throw this.invalidResponse("agent list");
3365
+ }
3366
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3367
+ let agent = response.agents.find(
3368
+ (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3369
+ );
3370
+ if (!agent && createIfMissing) {
3371
+ const created = await this.walletRequest(
3372
+ state,
3373
+ chainId,
3374
+ `/users/${user.userId}/agents`,
2876
3375
  {
2877
3376
  method: "POST",
2878
3377
  body: {
3378
+ name: OWNEY_AGENT_NAME,
3379
+ emoji: "\u{1F989}",
2879
3380
  chainId,
2880
- asset,
2881
- vaultAddress: plan.vaultAddress,
2882
- amount: plan.amount.toString()
3381
+ assetAddress: metadata.address,
3382
+ type: "vault",
3383
+ rulePreset: null
2883
3384
  }
2884
3385
  }
2885
3386
  );
2886
- const transactionHash = response2?.unwind?.transactionHash;
2887
- if (!this.isTransactionHash(transactionHash)) {
2888
- throw this.invalidResponse("position unwind");
2889
- }
2890
- await this.waitForUnwindReceipt(state, chainId, transactionHash);
3387
+ agent = created?.agent;
2891
3388
  }
2892
- const response = await this.walletRequest(
3389
+ if (!agent) return null;
3390
+ this.assertAgent(agent);
3391
+ const walletResponse = await this.walletRequest(
2893
3392
  state,
2894
3393
  chainId,
2895
- "/withdrawals",
2896
- {
2897
- method: "POST",
2898
- body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
2899
- }
3394
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
2900
3395
  );
2901
- const withdrawal = response?.withdrawal;
2902
- if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
2903
- throw this.invalidResponse("withdrawal");
3396
+ if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3397
+ throw this.invalidResponse("agent wallet");
2904
3398
  }
2905
- const txHash = await this.submitTransaction(
3399
+ return { user, agent, wallet: walletResponse.agentWallet, asset };
3400
+ }
3401
+ async loadPortfolio(state, chainId, options) {
3402
+ const user = await this.resolveUser(state, chainId);
3403
+ const response = await this.walletRequest(
2906
3404
  state,
2907
3405
  chainId,
2908
- withdrawal.transaction
3406
+ `/users/${user.userId}/agents`
2909
3407
  );
2910
- return {
2911
- txHash,
2912
- type: amount === void 0 ? "full" : "partial",
2913
- amount: withdrawal.amount
2914
- };
2915
- }
2916
- async getBalances(state, chainId) {
2917
- return mapYieldseekerBalances(
2918
- await this.walletRequest(
3408
+ if (!Array.isArray(response?.agents)) {
3409
+ throw this.invalidResponse("agent list");
3410
+ }
3411
+ const contexts = [];
3412
+ for (const agent of response.agents) {
3413
+ const asset = this.assetForAgent(agent);
3414
+ if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3415
+ continue;
3416
+ }
3417
+ this.assertAgent(agent);
3418
+ const walletResponse = await this.walletRequest(
2919
3419
  state,
2920
3420
  chainId,
2921
- `/balances${query({ chainId })}`
3421
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
3422
+ );
3423
+ if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3424
+ throw this.invalidResponse("agent wallet");
3425
+ }
3426
+ const context = {
3427
+ user,
3428
+ agent,
3429
+ wallet: walletResponse.agentWallet,
3430
+ asset
3431
+ };
3432
+ this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3433
+ contexts.push(context);
3434
+ }
3435
+ return Promise.all(
3436
+ contexts.map(
3437
+ (context) => this.loadPortfolioContext(state, chainId, context, options)
2922
3438
  )
2923
3439
  );
2924
3440
  }
2925
- async getEarnings(state, chainId) {
2926
- return mapYieldseekerEarnings(
2927
- await this.walletRequest(
3441
+ async loadPortfolioContext(state, chainId, context, options = {}) {
3442
+ const [snapshot, positions, historic, actions] = await Promise.all([
3443
+ this.walletRequest(
2928
3444
  state,
2929
3445
  chainId,
2930
- `/earnings${query({ chainId })}`
2931
- )
2932
- );
3446
+ `${this.agentPath(context, "snapshot")}${query({
3447
+ shouldOnlyUseRecentValue: true,
3448
+ shouldAllowStaleOnError: true
3449
+ })}`
3450
+ ),
3451
+ this.walletRequest(
3452
+ state,
3453
+ chainId,
3454
+ this.agentPath(context, "yield-positions")
3455
+ ),
3456
+ options.historic ? this.walletRequest(
3457
+ state,
3458
+ chainId,
3459
+ this.agentPath(context, "wallet/historic-position")
3460
+ ) : Promise.resolve(void 0),
3461
+ options.actions ? this.walletRequest(
3462
+ state,
3463
+ chainId,
3464
+ this.agentPath(context, "actions")
3465
+ ) : Promise.resolve(void 0)
3466
+ ]);
3467
+ if (!snapshot?.agentSnapshot) {
3468
+ throw this.invalidResponse("agent snapshot");
3469
+ }
3470
+ if (!Array.isArray(positions?.yieldPositions)) {
3471
+ throw this.invalidResponse("yield positions");
3472
+ }
3473
+ return {
3474
+ ...context,
3475
+ snapshot: snapshot.agentSnapshot,
3476
+ positions: positions.yieldPositions,
3477
+ ...historic?.position ? { historic: historic.position } : {},
3478
+ ...actions?.actions ? { actions: actions.actions } : {}
3479
+ };
2933
3480
  }
2934
- async getAccountApy(state, chainId, days, tokenSymbol) {
2935
- return mapYieldseekerApy(
2936
- await this.walletRequest(
3481
+ async deployAfterFunding(state, chainId, context) {
3482
+ if (context.wallet.initializedDate != null) return;
3483
+ try {
3484
+ const deployed = await this.walletRequest(
2937
3485
  state,
2938
3486
  chainId,
2939
- `/apy${query({ chainId, days, tokenSymbol })}`
2940
- )
2941
- );
3487
+ this.agentPath(context, "deploy"),
3488
+ { method: "POST", body: {} }
3489
+ );
3490
+ if (deployed?.agentWallet) {
3491
+ context.wallet = deployed.agentWallet;
3492
+ }
3493
+ } catch (error) {
3494
+ console.warn(
3495
+ "[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
3496
+ error
3497
+ );
3498
+ }
2942
3499
  }
2943
- async getHistory(state, chainId, options) {
2944
- return mapYieldseekerHistory(
2945
- await this.walletRequest(
3500
+ async refreshSnapshotAfterMovement(state, chainId, context, movement) {
3501
+ try {
3502
+ const response = await this.walletRequest(
2946
3503
  state,
2947
3504
  chainId,
2948
- `/history${query({
2949
- chainId,
2950
- limit: options?.limit ?? 10,
2951
- cursor: options?.cursor,
2952
- fromDate: options?.fromDate,
2953
- toDate: options?.toDate
3505
+ `${this.agentPath(context, "snapshot")}${query({
3506
+ shouldForceRefresh: true
2954
3507
  })}`
2955
- )
2956
- );
2957
- }
2958
- async getUserProfile(state, chainId) {
2959
- return mapYieldseekerProfile(
2960
- await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
2961
- );
3508
+ );
3509
+ if (!response?.agentSnapshot) {
3510
+ throw this.invalidResponse("agent snapshot refresh");
3511
+ }
3512
+ } catch (error) {
3513
+ console.warn(
3514
+ `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3515
+ error
3516
+ );
3517
+ }
2962
3518
  }
2963
- async getAgentApy(days, options) {
2964
- this.assertOptionalChain(options?.chainId);
2965
- return mapYieldseekerAgentApy(
2966
- await this.api.request(
2967
- `/agent/apy${query({
2968
- days,
2969
- tokenSymbol: options?.tokenSymbol,
2970
- chainId: options?.chainId
2971
- })}`
2972
- )
2973
- );
3519
+ agentPath(context, suffix) {
3520
+ return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
2974
3521
  }
2975
- async ensureActivated(state, chainId, asset) {
2976
- const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
2977
- if (this.activated.has(key2)) return;
2978
- const response = await this.walletRequest(
2979
- state,
2980
- chainId,
2981
- "/activation",
2982
- { method: "POST", body: { chainId, asset } }
2983
- );
2984
- if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
2985
- throw this.invalidResponse("activation");
3522
+ async walletRequest(state, chainId, path, options = {}) {
3523
+ try {
3524
+ return await this.providerRequest(state, chainId, path, options);
3525
+ } catch (error) {
3526
+ throw this.mapApiError(error);
2986
3527
  }
2987
- this.activated.add(key2);
2988
3528
  }
2989
- async walletRequest(state, chainId, path, options = {}) {
3529
+ async providerRequest(state, chainId, path, options = {}) {
2990
3530
  this.assertChain(chainId);
3531
+ const request = (signature2) => this.api.request(path, {
3532
+ ...options,
3533
+ signature: signature2
3534
+ });
2991
3535
  let signature = await this.auth.getToken(state, chainId);
2992
3536
  try {
2993
- return await this.api.request(path, { ...options, signature });
3537
+ return await request(signature);
2994
3538
  } catch (error) {
2995
3539
  if (!(error instanceof YieldseekerApiError)) throw error;
2996
- if (error.isAuthenticationError) {
2997
- this.auth.clear(state, chainId);
2998
- signature = await this.auth.getToken(state, chainId);
2999
- try {
3000
- return await this.api.request(path, { ...options, signature });
3001
- } catch (retryError) {
3002
- throw this.mapApiError(retryError);
3540
+ if (error.providerCode === "NO_USER") throw error;
3541
+ if (!error.isAuthenticationError) throw error;
3542
+ this.auth.clear(state, chainId);
3543
+ signature = await this.auth.getToken(state, chainId);
3544
+ try {
3545
+ return await request(signature);
3546
+ } catch (retryError) {
3547
+ if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
3548
+ this.forgetUser(state, chainId);
3003
3549
  }
3550
+ throw retryError;
3004
3551
  }
3005
- throw this.mapApiError(error);
3006
3552
  }
3007
3553
  }
3008
3554
  mapApiError(error) {
@@ -3030,15 +3576,8 @@ var YieldseekerAgent = class {
3030
3576
  if (this.transactionExecutor) {
3031
3577
  return this.transactionExecutor(state, chainId, transaction);
3032
3578
  }
3033
- if (typeof transaction.from !== "string" || typeof transaction.to !== "string" || typeof transaction.data !== "string" || typeof transaction.value !== "string" || transaction.chainId !== chainId) {
3034
- throw this.invalidResponse("transaction");
3035
- }
3579
+ this.assertTransaction(transaction, state, chainId);
3036
3580
  const account = (0, import_viem6.getAddress)(state.walletAddress);
3037
- if ((0, import_viem6.getAddress)(transaction.from) !== account) {
3038
- throw this.invalidResponse("transaction", {
3039
- reason: "transaction.from does not match the connected wallet"
3040
- });
3041
- }
3042
3581
  const walletClient = (0, import_viem6.createWalletClient)({
3043
3582
  account,
3044
3583
  chain: import_chains3.base,
@@ -3074,7 +3613,7 @@ var YieldseekerAgent = class {
3074
3613
  }
3075
3614
  return hash;
3076
3615
  }
3077
- async waitForUnwindReceipt(state, chainId, transactionHash) {
3616
+ async waitForReceipt(state, chainId, transactionHash) {
3078
3617
  if (this.unwindReceiptWaiter) {
3079
3618
  await this.unwindReceiptWaiter(state, chainId, transactionHash);
3080
3619
  return;
@@ -3090,15 +3629,32 @@ var YieldseekerAgent = class {
3090
3629
  if (receipt.status !== "success") {
3091
3630
  throw new OwneyError(
3092
3631
  "AGENT_TRANSACTION_REVERTED",
3093
- `Yieldseeker position unwind reverted (${transactionHash}).`,
3632
+ `Yieldseeker transaction reverted (${transactionHash}).`,
3094
3633
  { transactionHash },
3095
3634
  this.id
3096
3635
  );
3097
3636
  }
3098
3637
  }
3099
- positionMatchesChain(chain, chainId) {
3100
- const normalized = chain.trim().toUpperCase();
3101
- return normalized === String(chainId) || normalized === "BASE";
3638
+ assertTransaction(transaction, state, chainId) {
3639
+ if (!transaction || typeof transaction.from !== "string" || !(0, import_viem6.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem6.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem6.getAddress)(transaction.from) !== (0, import_viem6.getAddress)(state.walletAddress)) {
3640
+ throw this.invalidResponse("transaction");
3641
+ }
3642
+ }
3643
+ assertAgent(agent) {
3644
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
3645
+ throw this.invalidResponse("agent");
3646
+ }
3647
+ }
3648
+ isOwneyAgent(agent) {
3649
+ return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
3650
+ }
3651
+ assetForAgent(agent) {
3652
+ for (const asset of ["USDC", "WETH"]) {
3653
+ if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
3654
+ return asset;
3655
+ }
3656
+ }
3657
+ return null;
3102
3658
  }
3103
3659
  isTransactionHash(value) {
3104
3660
  return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
@@ -3120,8 +3676,12 @@ var YieldseekerAgent = class {
3120
3676
  if (asset !== "USDC" && asset !== "WETH") {
3121
3677
  throw new OwneyError(
3122
3678
  "ASSET_UNSUPPORTED",
3123
- `Yieldseeker does not support asset ${asset}.`,
3124
- { asset, supportedAssets: ["USDC", "WETH"] },
3679
+ `Yieldseeker does not support asset ${asset} in the Owney rollout.`,
3680
+ {
3681
+ asset,
3682
+ supportedAssets: ["USDC", "WETH"],
3683
+ providerAlsoAdvertises: ["cbBTC"]
3684
+ },
3125
3685
  this.id
3126
3686
  );
3127
3687
  }
@@ -3343,11 +3903,11 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
3343
3903
  return Number.isFinite(total) && total > 0 ? total : 0;
3344
3904
  }
3345
3905
  const normalizedToken = tokenSymbol.toUpperCase();
3346
- return balance.tokens.reduce((total, token2) => {
3347
- if (Number(token2.chainId) !== chainId || String(token2.asset).toUpperCase() !== normalizedToken) {
3906
+ return balance.tokens.reduce((total, token) => {
3907
+ if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
3348
3908
  return total;
3349
3909
  }
3350
- const amount = Number(token2.amount);
3910
+ const amount = Number(token.amount);
3351
3911
  return Number.isFinite(amount) && amount > 0 ? total + amount : total;
3352
3912
  }, 0);
3353
3913
  }
@@ -3417,8 +3977,8 @@ function makeSponsoredWethCallback(deps) {
3417
3977
  return makeVerificationAwareDepositCallback(
3418
3978
  async (smartWallet, chainId, amount, verification) => {
3419
3979
  const cid = chainId;
3420
- const token2 = deps.tokenAddressByChain[cid];
3421
- if (!token2) {
3980
+ const token = deps.tokenAddressByChain[cid];
3981
+ if (!token) {
3422
3982
  throw new OwneyError(
3423
3983
  "CHAIN_UNSUPPORTED",
3424
3984
  `No sponsored WETH configured for chain ${chainId}`
@@ -3429,12 +3989,12 @@ function makeSponsoredWethCallback(deps) {
3429
3989
  const wallet = deps.getWalletClient(cid);
3430
3990
  await ensureWalletOnChain(pub, wallet, cid);
3431
3991
  try {
3432
- const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
3992
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3433
3993
  if (balance < amountWei) {
3434
3994
  throw new OwneyError(
3435
3995
  "DEPOSIT_INSUFFICIENT_BALANCE",
3436
3996
  "Insufficient WETH balance for this deposit.",
3437
- { token: token2, chainId: cid, balance: balance.toString(), amount }
3997
+ { token, chainId: cid, balance: balance.toString(), amount }
3438
3998
  );
3439
3999
  }
3440
4000
  } catch (err) {
@@ -3446,14 +4006,14 @@ function makeSponsoredWethCallback(deps) {
3446
4006
  }
3447
4007
  const allowance = await readPermit2Allowance(
3448
4008
  pub,
3449
- token2,
4009
+ token,
3450
4010
  deps.ownerAddress
3451
4011
  );
3452
4012
  if (allowance < amountWei) {
3453
4013
  throw new OwneyError(
3454
4014
  "PERMIT2_APPROVAL_REQUIRED",
3455
4015
  "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3456
- { token: token2, chainId: cid, allowance: allowance.toString(), amount }
4016
+ { token, chainId: cid, allowance: allowance.toString(), amount }
3457
4017
  );
3458
4018
  }
3459
4019
  const relayer = await get({
@@ -3468,7 +4028,7 @@ function makeSponsoredWethCallback(deps) {
3468
4028
  const typedData = buildPermitTransferFromTypedData({
3469
4029
  chainId: cid,
3470
4030
  message: {
3471
- permitted: { token: token2, amount: amountWei },
4031
+ permitted: { token, amount: amountWei },
3472
4032
  spender: relayer,
3473
4033
  nonce,
3474
4034
  deadline
@@ -3485,13 +4045,17 @@ function makeSponsoredWethCallback(deps) {
3485
4045
  ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
3486
4046
  body: {
3487
4047
  chainId: cid,
3488
- token: token2,
4048
+ token,
3489
4049
  from: deps.ownerAddress,
3490
4050
  to: smartWallet,
3491
4051
  amount,
3492
4052
  nonce: nonce.toString(),
3493
4053
  deadline: deadline.toString(),
3494
- signature
4054
+ signature,
4055
+ ...verification?.agentId === "yieldseeker" ? {
4056
+ yieldseekerUserId: verification.userId,
4057
+ yieldseekerAgentId: verification.yieldseekerAgentId
4058
+ } : {}
3495
4059
  }
3496
4060
  });
3497
4061
  return result.txHash;
@@ -3526,10 +4090,10 @@ function makeSponsoredCallsCallback(deps) {
3526
4090
  }
3527
4091
  return new URL(configured, origin).toString();
3528
4092
  };
3529
- return async (smartWallet, chainId, amount) => {
4093
+ return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
3530
4094
  const cid = chainId;
3531
- const token2 = deps.tokenAddressByChain[cid];
3532
- if (!token2) {
4095
+ const token = deps.tokenAddressByChain[cid];
4096
+ if (!token) {
3533
4097
  throw new OwneyError(
3534
4098
  "CHAIN_UNSUPPORTED",
3535
4099
  `No sponsored token configured for chain ${chainId}`
@@ -3547,6 +4111,32 @@ function makeSponsoredCallsCallback(deps) {
3547
4111
  functionName: "transfer",
3548
4112
  args: [smartWallet, BigInt(amount)]
3549
4113
  });
4114
+ let paymasterUrl = absolutePaymasterUrl();
4115
+ if (verification?.agentId === "yieldseeker") {
4116
+ if (chainId !== 8453) {
4117
+ throw new OwneyError(
4118
+ "CHAIN_UNSUPPORTED",
4119
+ `Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
4120
+ );
4121
+ }
4122
+ const { intent } = await postPaymasterIntent({
4123
+ baseUrl: deps.routingApiBaseUrl,
4124
+ apiKey: deps.apiKey,
4125
+ yieldseekerSignature: verification.signature,
4126
+ body: {
4127
+ chainId,
4128
+ token,
4129
+ from: deps.ownerAddress,
4130
+ to: smartWallet,
4131
+ amount,
4132
+ yieldseekerUserId: verification.userId,
4133
+ yieldseekerAgentId: verification.yieldseekerAgentId
4134
+ }
4135
+ });
4136
+ const url = new URL(paymasterUrl);
4137
+ url.searchParams.set("owneyIntent", intent);
4138
+ paymasterUrl = url.toString();
4139
+ }
3550
4140
  const sendResult = await deps.provider.request({
3551
4141
  method: "wallet_sendCalls",
3552
4142
  params: [
@@ -3555,9 +4145,9 @@ function makeSponsoredCallsCallback(deps) {
3555
4145
  from: deps.ownerAddress,
3556
4146
  chainId: (0, import_viem8.toHex)(chainId),
3557
4147
  atomicRequired: false,
3558
- calls: [{ to: token2, value: "0x0", data }],
4148
+ calls: [{ to: token, value: "0x0", data }],
3559
4149
  capabilities: {
3560
- paymasterService: { url: absolutePaymasterUrl() }
4150
+ paymasterService: { url: paymasterUrl }
3561
4151
  }
3562
4152
  }
3563
4153
  ]
@@ -3587,17 +4177,17 @@ function makeSponsoredCallsCallback(deps) {
3587
4177
  `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
3588
4178
  { chainId, callsId }
3589
4179
  );
3590
- };
4180
+ });
3591
4181
  }
3592
4182
 
3593
4183
  // src/client.ts
3594
4184
  function encodeMultiAgentCursor(map) {
3595
4185
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
3596
4186
  }
3597
- function decodeMultiAgentCursor(token2) {
4187
+ function decodeMultiAgentCursor(token) {
3598
4188
  let parsed;
3599
4189
  try {
3600
- const json = Buffer.from(token2, "base64").toString("utf8");
4190
+ const json = Buffer.from(token, "base64").toString("utf8");
3601
4191
  parsed = JSON.parse(json);
3602
4192
  } catch {
3603
4193
  throw new InvalidHistoryCursorError(
@@ -3786,6 +4376,8 @@ var OwneySDK = class {
3786
4376
  if (!onApproved && cached) return cached;
3787
4377
  const provider = this.requireConnectedProvider();
3788
4378
  const callback = makeSponsoredCallsCallback({
4379
+ apiKey: this.apiKey,
4380
+ routingApiBaseUrl: this.routingApiBaseUrl,
3789
4381
  provider,
3790
4382
  ownerAddress: this.state.walletAddress,
3791
4383
  paymasterServiceUrl: this.paymasterServiceUrl,
@@ -4228,15 +4820,15 @@ var OwneySDK = class {
4228
4820
  agent,
4229
4821
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
4230
4822
  }));
4231
- const valid = splits.filter(
4823
+ const valid2 = splits.filter(
4232
4824
  (s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
4233
4825
  );
4234
- if (valid.length === agents.length) {
4826
+ if (valid2.length === agents.length) {
4235
4827
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
4236
4828
  }
4237
4829
  return this.splitDepositAmount(
4238
4830
  totalAmount,
4239
- valid.map((s) => s.agent),
4831
+ valid2.map((s) => s.agent),
4240
4832
  chainId,
4241
4833
  asset,
4242
4834
  exempt
@@ -4269,10 +4861,10 @@ var OwneySDK = class {
4269
4861
  try {
4270
4862
  const balance = await agent.getBalances(state, chainId);
4271
4863
  const target = asset.toLowerCase();
4272
- const token2 = balance.tokens.find(
4864
+ const token = balance.tokens.find(
4273
4865
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
4274
4866
  );
4275
- return !!token2 && Number(token2.amount) > 0;
4867
+ return !!token && Number(token.amount) > 0;
4276
4868
  } catch {
4277
4869
  return false;
4278
4870
  }
@@ -4323,9 +4915,9 @@ var OwneySDK = class {
4323
4915
  const { asset, amount, agentId } = options;
4324
4916
  const state = this.requireState();
4325
4917
  const chainId = this.requireChainId();
4326
- const token2 = asset;
4918
+ const token = asset;
4327
4919
  const assetInfo = SupportedAssets.find(
4328
- (a) => a.chainId === chainId && a.symbol === token2
4920
+ (a) => a.chainId === chainId && a.symbol === token
4329
4921
  );
4330
4922
  if (!assetInfo) {
4331
4923
  throw new OwneyError(
@@ -4339,7 +4931,7 @@ var OwneySDK = class {
4339
4931
  return withFailureReporting(
4340
4932
  this.apiKey,
4341
4933
  agent.id,
4342
- () => agent.withdraw(state, chainId, token2, amount),
4934
+ () => agent.withdraw(state, chainId, token, amount),
4343
4935
  this.routingApiBaseUrl
4344
4936
  );
4345
4937
  }
@@ -4349,7 +4941,7 @@ var OwneySDK = class {
4349
4941
  const agentErrors2 = {};
4350
4942
  for (const agent of eligibleAgents) {
4351
4943
  try {
4352
- results2[agent.id] = await agent.withdraw(state, chainId, token2);
4944
+ results2[agent.id] = await agent.withdraw(state, chainId, token);
4353
4945
  } catch (err) {
4354
4946
  console.error(`withdraw failed for agent "${agent.id}":`, err);
4355
4947
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
@@ -4439,7 +5031,7 @@ var OwneySDK = class {
4439
5031
  results[p.agent.id] = await p.agent.withdraw(
4440
5032
  state,
4441
5033
  chainId,
4442
- token2,
5034
+ token,
4443
5035
  p.planned.toString()
4444
5036
  );
4445
5037
  } catch (err) {
@@ -4790,8 +5382,8 @@ var OwneySDK = class {
4790
5382
  const state = this.requireState();
4791
5383
  const chainId = this.requireChainId();
4792
5384
  this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
4793
- const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
4794
- if (!token2) {
5385
+ const token = SPONSORED_WETH_BY_CHAIN[chainId];
5386
+ if (!token) {
4795
5387
  throw new OwneyError(
4796
5388
  "CHAIN_UNSUPPORTED",
4797
5389
  `No sponsored WETH on chain ${chainId}`
@@ -4804,7 +5396,7 @@ var OwneySDK = class {
4804
5396
  transport: (0, import_viem9.custom)(provider)
4805
5397
  });
4806
5398
  const hash = await wallet.writeContract({
4807
- address: token2,
5399
+ address: token,
4808
5400
  abi: ERC20_ALLOWANCE_ABI,
4809
5401
  functionName: "approve",
4810
5402
  args: [PERMIT2_ADDRESS, MAX_UINT256],
@@ -4929,8 +5521,8 @@ var import_siwe2 = require("siwe");
4929
5521
  var import_sdk2 = require("@zyfai/sdk");
4930
5522
 
4931
5523
  // src/agents/zyfai/zyfai.siwx-cache.ts
4932
- var KEY_PREFIX3 = "owney.siwx.session";
4933
- var storage3 = () => {
5524
+ var KEY_PREFIX4 = "owney.siwx.session";
5525
+ var storage4 = () => {
4934
5526
  if (typeof window === "undefined") return null;
4935
5527
  try {
4936
5528
  return window.localStorage;
@@ -4938,8 +5530,8 @@ var storage3 = () => {
4938
5530
  return null;
4939
5531
  }
4940
5532
  };
4941
- var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
4942
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
5533
+ var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
5534
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
4943
5535
  var memorySiwxSessions = /* @__PURE__ */ new Map();
4944
5536
  var readLegacySiwxSession = (store, address) => {
4945
5537
  if (!store) return null;
@@ -4971,16 +5563,16 @@ var readLegacySiwxSession = (store, address) => {
4971
5563
  var readSiwxSession = (address, chainId) => {
4972
5564
  if (typeof window === "undefined") return null;
4973
5565
  const key2 = buildKey3(address);
4974
- const store = storage3();
4975
- let raw = null;
5566
+ const store = storage4();
5567
+ let raw2 = null;
4976
5568
  try {
4977
- raw = store?.getItem(key2) ?? null;
5569
+ raw2 = store?.getItem(key2) ?? null;
4978
5570
  } catch {
4979
- raw = null;
5571
+ raw2 = null;
4980
5572
  }
4981
- if (raw) {
5573
+ if (raw2) {
4982
5574
  try {
4983
- return JSON.parse(raw);
5575
+ return JSON.parse(raw2);
4984
5576
  } catch {
4985
5577
  memorySiwxSessions.delete(key2);
4986
5578
  try {
@@ -5001,7 +5593,7 @@ var writeSiwxSession = (address, _chainId, session) => {
5001
5593
  if (typeof window === "undefined") return;
5002
5594
  const key2 = buildKey3(address);
5003
5595
  memorySiwxSessions.set(key2, session);
5004
- const store = storage3();
5596
+ const store = storage4();
5005
5597
  try {
5006
5598
  store?.setItem(key2, JSON.stringify(session));
5007
5599
  } catch {
@@ -5010,7 +5602,7 @@ var writeSiwxSession = (address, _chainId, session) => {
5010
5602
  var clearSiwxSession = (address, _chainId) => {
5011
5603
  const key2 = buildKey3(address);
5012
5604
  memorySiwxSessions.delete(key2);
5013
- const store = storage3();
5605
+ const store = storage4();
5014
5606
  try {
5015
5607
  store?.removeItem(key2);
5016
5608
  } catch {