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

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,313 @@ 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;
2564
2664
  }
2565
- function mapYieldseekerProfile(value) {
2566
- const profile = requireEnvelope(
2567
- value,
2568
- "profile",
2569
- "profile"
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;
2669
+ }
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) {
2644
2828
  return {
2645
2829
  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
- }))
2830
+ action: actionType(action.actionType),
2831
+ date: action.createdDate,
2832
+ oldApy: null,
2833
+ newApy: null,
2834
+ transactions: [
2835
+ {
2836
+ txHashes: transactionHashes(action.details),
2837
+ chainId: 8453
2838
+ }
2839
+ ],
2840
+ rebalanceLog: []
2669
2841
  };
2670
2842
  }
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");
2843
+ function movementEntry(movement, wallet, agent, asset) {
2844
+ const isDeposit = movement.toAddress.toLowerCase() === wallet.walletAddress.toLowerCase();
2845
+ return {
2846
+ agent: "yieldseeker",
2847
+ action: isDeposit ? "Deposit" : "Withdraw",
2848
+ date: movement.blockDate,
2849
+ oldApy: null,
2850
+ newApy: null,
2851
+ transactions: [
2852
+ {
2853
+ txHashes: [movement.transactionHash],
2854
+ chainId: agent.chainId,
2855
+ tokenSymbol: asset,
2856
+ amount: decimal(
2857
+ movement.assetAmount,
2858
+ YIELDSEEKER_ASSET_METADATA[asset].decimals,
2859
+ "historic position"
2860
+ )
2861
+ }
2862
+ ],
2863
+ rebalanceLog: []
2864
+ };
2865
+ }
2866
+ function mapYieldseekerHistory(contexts, options) {
2867
+ const entries = contexts.flatMap((context) => [
2868
+ ...(context.actions ?? []).map(actionEntry),
2869
+ ...(context.historic?.movements ?? []).map(
2870
+ (movement) => movementEntry(movement, context.wallet, context.agent, context.asset)
2871
+ )
2872
+ ]);
2873
+ const filtered = entries.filter(
2874
+ (entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
2875
+ ).filter(
2876
+ (entry, index, all) => all.findIndex(
2877
+ (candidate) => candidate.action === entry.action && candidate.date === entry.date && candidate.transactions[0]?.txHashes[0] === entry.transactions[0]?.txHashes[0]
2878
+ ) === index
2879
+ ).sort((left, right) => right.date.localeCompare(left.date));
2880
+ return {
2881
+ data: filtered.slice(0, options.limit),
2882
+ // v1 returns the whole action/movement collection and defines no cursor.
2883
+ // Report a terminal page so callers never loop over the same prefix.
2884
+ hasMore: false
2885
+ };
2886
+ }
2887
+ function mapYieldseekerProfile(address, contexts) {
2888
+ const protocols = /* @__PURE__ */ new Set();
2889
+ for (const context of contexts) {
2890
+ for (const current of context.positions) {
2891
+ if (current.yieldOption?.provider) {
2892
+ protocols.add(String(current.yieldOption.provider));
2893
+ }
2894
+ }
2679
2895
  }
2680
2896
  return {
2681
- data: history.data.map(historyItem),
2682
- hasMore: history.hasMore,
2683
- ...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
2897
+ address,
2898
+ smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2899
+ chains: contexts.length > 0 ? [8453] : [],
2900
+ hasActiveSessionKey: contexts.some(
2901
+ (context) => context.wallet.initializedDate != null
2902
+ ),
2903
+ protocols: [...protocols]
2684
2904
  };
2685
2905
  }
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");
2906
+ function mapYieldseekerAgentApy(options, days) {
2907
+ const perAsset = {};
2908
+ const all = [];
2909
+ for (const entry of options) {
2910
+ const apys = entry.yieldOptions.map(
2911
+ (option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
2912
+ ).filter(Number.isFinite);
2913
+ if (apys.length === 0) continue;
2914
+ const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
2915
+ perAsset[entry.asset] = average;
2916
+ all.push(average);
2694
2917
  }
2695
2918
  return {
2696
- averageApy: agentApy.averageApy,
2697
- ...agentApy.detailedApys ? {
2698
- detailedApys: {
2699
- apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
2700
- }
2701
- } : {}
2919
+ averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
2920
+ detailedApys: { apyPerAsset: { 8453: perAsset } }
2702
2921
  };
2703
2922
  }
2704
2923
 
2705
2924
  // src/agents/yieldseeker/yieldseeker.agent.ts
2925
+ var OWNEY_AGENT_NAME = "owney";
2926
+ var YIELDSEEKER_AGENT_WALLET_ABI = [
2927
+ {
2928
+ type: "function",
2929
+ name: "withdrawAssetToUser",
2930
+ stateMutability: "nonpayable",
2931
+ inputs: [
2932
+ { name: "recipient", type: "address" },
2933
+ { name: "asset", type: "address" },
2934
+ { name: "amount", type: "uint256" }
2935
+ ],
2936
+ outputs: []
2937
+ },
2938
+ {
2939
+ type: "function",
2940
+ name: "withdrawAllAssetToUser",
2941
+ stateMutability: "nonpayable",
2942
+ inputs: [
2943
+ { name: "recipient", type: "address" },
2944
+ { name: "asset", type: "address" }
2945
+ ],
2946
+ outputs: []
2947
+ }
2948
+ ];
2706
2949
  function query(params) {
2707
2950
  const search = new URLSearchParams();
2708
2951
  for (const [key2, value] of Object.entries(params)) {
@@ -2720,7 +2963,7 @@ var YieldseekerAgent = class {
2720
2963
  chainId: 8453,
2721
2964
  chain: "BASE",
2722
2965
  assets: [
2723
- { symbol: "USDC", minDepositAmount: "1" },
2966
+ { symbol: "USDC", minDepositAmount: "10000000" },
2724
2967
  { symbol: "WETH", minDepositAmount: "1" }
2725
2968
  ]
2726
2969
  }
@@ -2729,7 +2972,9 @@ var YieldseekerAgent = class {
2729
2972
  auth;
2730
2973
  transactionExecutor;
2731
2974
  unwindReceiptWaiter;
2732
- activated = /* @__PURE__ */ new Set();
2975
+ agentContexts = /* @__PURE__ */ new Map();
2976
+ users = /* @__PURE__ */ new Map();
2977
+ pendingAgents = /* @__PURE__ */ new Map();
2733
2978
  constructor(owneyApiKey, options = {}) {
2734
2979
  this.api = new YieldseekerApiClient(
2735
2980
  owneyApiKey,
@@ -2742,14 +2987,19 @@ var YieldseekerAgent = class {
2742
2987
  }
2743
2988
  async disconnect() {
2744
2989
  this.auth.clear();
2745
- this.activated.clear();
2990
+ for (const key2 of this.users.keys()) {
2991
+ const [walletAddress, chainId] = key2.split(":");
2992
+ clearYieldseekerIdentity(walletAddress, Number(chainId));
2993
+ }
2994
+ this.users.clear();
2995
+ this.agentContexts.clear();
2996
+ this.pendingAgents.clear();
2746
2997
  }
2747
2998
  async activateAgent(state, chainId, asset) {
2748
2999
  this.assertChain(chainId);
2749
- await this.auth.getToken(state, chainId);
2750
- if (asset === "USDC" || asset === "WETH") {
2751
- await this.ensureActivated(state, chainId, asset);
2752
- }
3000
+ const targetAsset = asset ?? "USDC";
3001
+ this.assertAsset(targetAsset);
3002
+ await this.ensureAgent(state, chainId, targetAsset);
2753
3003
  }
2754
3004
  async deposit(state, chainId, amount, asset, depositCallback) {
2755
3005
  this.assertChain(chainId);
@@ -2762,42 +3012,48 @@ var YieldseekerAgent = class {
2762
3012
  this.id
2763
3013
  );
2764
3014
  }
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
- });
3015
+ const context = await this.ensureAgent(state, chainId, asset);
3016
+ let txHash;
3017
+ try {
3018
+ if (depositCallback) {
3019
+ provideDepositVerificationContext(depositCallback, {
3020
+ agentId: "yieldseeker",
3021
+ signature: await this.auth.getToken(state, chainId),
3022
+ userId: context.user.userId,
3023
+ yieldseekerAgentId: context.agent.agentId
3024
+ });
3025
+ txHash = await depositCallback(
3026
+ context.wallet.walletAddress,
3027
+ chainId,
3028
+ amount
3029
+ );
3030
+ await this.waitForReceipt(state, chainId, txHash);
3031
+ } else {
3032
+ txHash = await this.submitTransaction(state, chainId, {
3033
+ from: (0, import_viem6.getAddress)(state.walletAddress),
3034
+ to: YIELDSEEKER_ASSET_METADATA[asset].address,
3035
+ data: (0, import_viem6.encodeFunctionData)({
3036
+ abi: import_viem6.erc20Abi,
3037
+ functionName: "transfer",
3038
+ args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
3039
+ }),
3040
+ value: "0",
3041
+ chainId
3042
+ });
3043
+ }
3044
+ await this.deployAfterFunding(state, chainId, context);
3045
+ } finally {
3046
+ await this.refreshSnapshotAfterMovement(
3047
+ state,
3048
+ chainId,
3049
+ context,
3050
+ "deposit"
3051
+ );
2796
3052
  }
2797
3053
  return {
2798
3054
  txHash,
2799
- smartWallet: deposit.smartWallet,
2800
- amount: deposit.amount
3055
+ smartWallet: context.wallet.walletAddress,
3056
+ amount
2801
3057
  };
2802
3058
  }
2803
3059
  async withdraw(state, chainId, asset, amount) {
@@ -2811,198 +3067,454 @@ var YieldseekerAgent = class {
2811
3067
  this.id
2812
3068
  );
2813
3069
  }
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) {
3070
+ const context = await this.findAgent(state, chainId, asset);
3071
+ if (!context) {
2833
3072
  throw new OwneyError(
2834
3073
  "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
- },
3074
+ `No Yieldseeker ${asset} agent exists for this wallet.`,
3075
+ { asset, available: "0" },
2841
3076
  this.id
2842
3077
  );
2843
3078
  }
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
3079
+ try {
3080
+ const portfolio = await this.loadPortfolioContext(
3081
+ state,
3082
+ chainId,
3083
+ context
3084
+ );
3085
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3086
+ const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
3087
+ ([address]) => address.toLowerCase() === metadata.address.toLowerCase()
3088
+ );
3089
+ const idle = BigInt(idleEntry?.[1] ?? "0");
3090
+ const deployed = portfolio.positions.reduce(
3091
+ (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
3092
+ 0n
3093
+ );
3094
+ const totalAvailable = idle + deployed;
3095
+ const requested = amount === void 0 ? totalAvailable : BigInt(amount);
3096
+ if (requested > totalAvailable) {
3097
+ throw new OwneyError(
3098
+ "WITHDRAW_INSUFFICIENT_BALANCE",
3099
+ `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
3100
+ {
3101
+ asset,
3102
+ requested: requested.toString(),
3103
+ available: totalAvailable.toString()
3104
+ },
3105
+ this.id
3106
+ );
3107
+ }
3108
+ let remaining = requested > idle ? requested - idle : 0n;
3109
+ for (const position2 of portfolio.positions) {
3110
+ if (remaining === 0n) break;
3111
+ const available = BigInt(position2.withdrawableAssetsRaw);
3112
+ if (available <= 0n) continue;
3113
+ const assetsRaw = available < remaining ? available : remaining;
3114
+ const response = await this.walletRequest(
3115
+ state,
3116
+ chainId,
3117
+ this.agentPath(context, "withdraw-from-position"),
3118
+ {
3119
+ method: "POST",
3120
+ body: {
3121
+ chainId,
3122
+ vaultAddress: position2.yieldOption.address,
3123
+ assetsRaw: assetsRaw.toString()
3124
+ }
3125
+ }
3126
+ );
3127
+ if (!this.isTransactionHash(response?.transactionHash)) {
3128
+ throw this.invalidResponse("position withdrawal");
3129
+ }
3130
+ await this.waitForReceipt(state, chainId, response.transactionHash);
3131
+ remaining -= assetsRaw;
3132
+ }
3133
+ if (remaining > 0n) {
3134
+ throw this.invalidResponse("yield positions", {
3135
+ reason: "Withdrawable positions could not cover the request.",
3136
+ remaining: remaining.toString()
2855
3137
  });
2856
3138
  }
2857
- const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
2858
- unwindPlans.push({
2859
- vaultAddress: (0, import_viem6.getAddress)(position2.protocolId),
2860
- amount: unwindAmount
3139
+ const account = (0, import_viem6.getAddress)(state.walletAddress);
3140
+ const txHash = await this.submitTransaction(state, chainId, {
3141
+ from: account,
3142
+ to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
3143
+ data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
3144
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3145
+ functionName: "withdrawAllAssetToUser",
3146
+ args: [account, metadata.address]
3147
+ }) : (0, import_viem6.encodeFunctionData)({
3148
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3149
+ functionName: "withdrawAssetToUser",
3150
+ args: [account, metadata.address, requested]
3151
+ }),
3152
+ value: "0",
3153
+ chainId
2861
3154
  });
2862
- remainingToUnwind -= unwindAmount;
3155
+ return {
3156
+ txHash,
3157
+ type: amount === void 0 ? "full" : "partial",
3158
+ amount: requested.toString()
3159
+ };
3160
+ } finally {
3161
+ await this.refreshSnapshotAfterMovement(
3162
+ state,
3163
+ chainId,
3164
+ context,
3165
+ "withdrawal"
3166
+ );
2863
3167
  }
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
- });
3168
+ }
3169
+ async getBalances(state, chainId) {
3170
+ this.assertChain(chainId);
3171
+ return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
3172
+ }
3173
+ async getEarnings(state, chainId) {
3174
+ this.assertChain(chainId);
3175
+ return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
3176
+ }
3177
+ async getAccountApy(state, chainId, days, tokenSymbol) {
3178
+ this.assertChain(chainId);
3179
+ const asset = tokenSymbol?.toUpperCase();
3180
+ if (asset !== void 0) this.assertAsset(asset);
3181
+ const contexts = await this.loadPortfolio(state, chainId, {
3182
+ ...asset ? { asset } : {},
3183
+ historic: true
3184
+ });
3185
+ return mapYieldseekerApy(state.walletAddress, contexts, days);
3186
+ }
3187
+ async getHistory(state, chainId, options) {
3188
+ this.assertChain(chainId);
3189
+ const asset = options?.tokenSymbol?.toUpperCase();
3190
+ if (asset !== void 0) this.assertAsset(asset);
3191
+ const contexts = await this.loadPortfolio(state, chainId, {
3192
+ ...asset ? { asset } : {},
3193
+ historic: true,
3194
+ actions: true
3195
+ });
3196
+ return mapYieldseekerHistory(contexts, {
3197
+ limit: options?.limit ?? 10,
3198
+ ...options?.fromDate ? { fromDate: options.fromDate } : {},
3199
+ ...options?.toDate ? { toDate: options.toDate } : {}
3200
+ });
3201
+ }
3202
+ async getUserProfile(state, chainId) {
3203
+ this.assertChain(chainId);
3204
+ return mapYieldseekerProfile(
3205
+ state.walletAddress,
3206
+ await this.loadPortfolio(state, chainId, {})
3207
+ );
3208
+ }
3209
+ async getAgentApy(days, options) {
3210
+ this.assertOptionalChain(options?.chainId);
3211
+ const requested = options?.tokenSymbol?.toUpperCase();
3212
+ if (requested !== void 0) this.assertAsset(requested);
3213
+ const assets = requested ? [requested] : ["USDC", "WETH"];
3214
+ const values = await Promise.all(
3215
+ assets.map(async (asset) => {
3216
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3217
+ const response = await this.api.request(
3218
+ `/chains/8453/assets/${metadata.address}/yield-options`
3219
+ );
3220
+ if (!Array.isArray(response?.yieldOptions)) {
3221
+ throw this.invalidResponse("yield options");
3222
+ }
3223
+ return { asset, yieldOptions: response.yieldOptions };
3224
+ })
3225
+ );
3226
+ return mapYieldseekerAgentApy(values, days);
3227
+ }
3228
+ userKey(state, chainId) {
3229
+ return `${state.walletAddress.toLowerCase()}:${chainId}`;
3230
+ }
3231
+ contextKey(state, chainId, asset) {
3232
+ return `${this.userKey(state, chainId)}:${asset}`;
3233
+ }
3234
+ async resolveUser(state, chainId) {
3235
+ const key2 = this.userKey(state, chainId);
3236
+ const inMemory = this.users.get(key2);
3237
+ if (inMemory) return inMemory;
3238
+ const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
3239
+ if (persisted) {
3240
+ this.users.set(key2, persisted);
3241
+ return persisted;
3242
+ }
3243
+ const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
3244
+ let user = null;
3245
+ try {
3246
+ const login = await this.providerRequest(
3247
+ state,
3248
+ chainId,
3249
+ "/users/login-with-wallet",
3250
+ { method: "POST", body: { walletAddress } }
3251
+ );
3252
+ user = login?.user ?? null;
3253
+ if (!user) {
3254
+ throw this.invalidResponse("wallet login", {
3255
+ reason: "A successful login returned no user."
3256
+ });
3257
+ }
3258
+ } catch (error) {
3259
+ if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
3260
+ if (error instanceof OwneyError) throw error;
3261
+ throw this.mapApiError(error);
3262
+ }
3263
+ let created;
3264
+ try {
3265
+ created = await this.providerRequest(
3266
+ state,
3267
+ chainId,
3268
+ "/users",
3269
+ {
3270
+ method: "POST",
3271
+ body: {
3272
+ walletAddress,
3273
+ username: `owney-${walletAddress.slice(2).toLowerCase()}`
3274
+ }
3275
+ }
3276
+ );
3277
+ } catch (createError) {
3278
+ throw this.mapApiError(createError);
3279
+ }
3280
+ user = created?.user ?? null;
3281
+ }
3282
+ if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
3283
+ throw this.invalidResponse("wallet identity");
3284
+ }
3285
+ const resolved = { userId: user.userId };
3286
+ this.users.set(key2, resolved);
3287
+ writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
3288
+ return resolved;
3289
+ }
3290
+ forgetUser(state, chainId) {
3291
+ this.users.delete(this.userKey(state, chainId));
3292
+ clearYieldseekerIdentity(state.walletAddress, chainId);
3293
+ }
3294
+ async ensureAgent(state, chainId, asset) {
3295
+ const key2 = this.contextKey(state, chainId, asset);
3296
+ const cached = this.agentContexts.get(key2);
3297
+ if (cached) return cached;
3298
+ const pending = this.pendingAgents.get(key2);
3299
+ if (pending) return pending;
3300
+ const request = this.resolveAgent(state, chainId, asset, true).then(
3301
+ (context) => {
3302
+ if (!context) throw this.invalidResponse("agent creation");
3303
+ this.agentContexts.set(key2, context);
3304
+ return context;
3305
+ }
3306
+ );
3307
+ this.pendingAgents.set(key2, request);
3308
+ try {
3309
+ return await request;
3310
+ } finally {
3311
+ this.pendingAgents.delete(key2);
2870
3312
  }
2871
- for (const plan of unwindPlans) {
2872
- const response2 = await this.walletRequest(
3313
+ }
3314
+ async findAgent(state, chainId, asset) {
3315
+ const key2 = this.contextKey(state, chainId, asset);
3316
+ const cached = this.agentContexts.get(key2);
3317
+ if (cached) return cached;
3318
+ const context = await this.resolveAgent(state, chainId, asset, false);
3319
+ if (context) this.agentContexts.set(key2, context);
3320
+ return context;
3321
+ }
3322
+ async resolveAgent(state, chainId, asset, createIfMissing) {
3323
+ const user = await this.resolveUser(state, chainId);
3324
+ const response = await this.walletRequest(
3325
+ state,
3326
+ chainId,
3327
+ `/users/${user.userId}/agents`
3328
+ );
3329
+ if (!Array.isArray(response?.agents)) {
3330
+ throw this.invalidResponse("agent list");
3331
+ }
3332
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3333
+ let agent = response.agents.find(
3334
+ (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3335
+ );
3336
+ if (!agent && createIfMissing) {
3337
+ const created = await this.walletRequest(
2873
3338
  state,
2874
3339
  chainId,
2875
- "/positions/unwind",
3340
+ `/users/${user.userId}/agents`,
2876
3341
  {
2877
3342
  method: "POST",
2878
3343
  body: {
3344
+ name: OWNEY_AGENT_NAME,
3345
+ emoji: "\u{1F989}",
2879
3346
  chainId,
2880
- asset,
2881
- vaultAddress: plan.vaultAddress,
2882
- amount: plan.amount.toString()
3347
+ assetAddress: metadata.address,
3348
+ type: "vault",
3349
+ rulePreset: null
2883
3350
  }
2884
3351
  }
2885
3352
  );
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);
3353
+ agent = created?.agent;
2891
3354
  }
2892
- const response = await this.walletRequest(
3355
+ if (!agent) return null;
3356
+ this.assertAgent(agent);
3357
+ const walletResponse = await this.walletRequest(
2893
3358
  state,
2894
3359
  chainId,
2895
- "/withdrawals",
2896
- {
2897
- method: "POST",
2898
- body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
2899
- }
3360
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
2900
3361
  );
2901
- const withdrawal = response?.withdrawal;
2902
- if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
2903
- throw this.invalidResponse("withdrawal");
3362
+ if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3363
+ throw this.invalidResponse("agent wallet");
2904
3364
  }
2905
- const txHash = await this.submitTransaction(
3365
+ return { user, agent, wallet: walletResponse.agentWallet, asset };
3366
+ }
3367
+ async loadPortfolio(state, chainId, options) {
3368
+ const user = await this.resolveUser(state, chainId);
3369
+ const response = await this.walletRequest(
2906
3370
  state,
2907
3371
  chainId,
2908
- withdrawal.transaction
3372
+ `/users/${user.userId}/agents`
2909
3373
  );
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(
3374
+ if (!Array.isArray(response?.agents)) {
3375
+ throw this.invalidResponse("agent list");
3376
+ }
3377
+ const contexts = [];
3378
+ for (const agent of response.agents) {
3379
+ const asset = this.assetForAgent(agent);
3380
+ if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3381
+ continue;
3382
+ }
3383
+ this.assertAgent(agent);
3384
+ const walletResponse = await this.walletRequest(
2919
3385
  state,
2920
3386
  chainId,
2921
- `/balances${query({ chainId })}`
3387
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
3388
+ );
3389
+ if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3390
+ throw this.invalidResponse("agent wallet");
3391
+ }
3392
+ const context = {
3393
+ user,
3394
+ agent,
3395
+ wallet: walletResponse.agentWallet,
3396
+ asset
3397
+ };
3398
+ this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3399
+ contexts.push(context);
3400
+ }
3401
+ return Promise.all(
3402
+ contexts.map(
3403
+ (context) => this.loadPortfolioContext(state, chainId, context, options)
2922
3404
  )
2923
3405
  );
2924
3406
  }
2925
- async getEarnings(state, chainId) {
2926
- return mapYieldseekerEarnings(
2927
- await this.walletRequest(
3407
+ async loadPortfolioContext(state, chainId, context, options = {}) {
3408
+ const [snapshot, positions, historic, actions] = await Promise.all([
3409
+ this.walletRequest(
2928
3410
  state,
2929
3411
  chainId,
2930
- `/earnings${query({ chainId })}`
2931
- )
2932
- );
3412
+ `${this.agentPath(context, "snapshot")}${query({
3413
+ shouldOnlyUseRecentValue: true,
3414
+ shouldAllowStaleOnError: true
3415
+ })}`
3416
+ ),
3417
+ this.walletRequest(
3418
+ state,
3419
+ chainId,
3420
+ this.agentPath(context, "yield-positions")
3421
+ ),
3422
+ options.historic ? this.walletRequest(
3423
+ state,
3424
+ chainId,
3425
+ this.agentPath(context, "wallet/historic-position")
3426
+ ) : Promise.resolve(void 0),
3427
+ options.actions ? this.walletRequest(
3428
+ state,
3429
+ chainId,
3430
+ this.agentPath(context, "actions")
3431
+ ) : Promise.resolve(void 0)
3432
+ ]);
3433
+ if (!snapshot?.agentSnapshot) {
3434
+ throw this.invalidResponse("agent snapshot");
3435
+ }
3436
+ if (!Array.isArray(positions?.yieldPositions)) {
3437
+ throw this.invalidResponse("yield positions");
3438
+ }
3439
+ return {
3440
+ ...context,
3441
+ snapshot: snapshot.agentSnapshot,
3442
+ positions: positions.yieldPositions,
3443
+ ...historic?.position ? { historic: historic.position } : {},
3444
+ ...actions?.actions ? { actions: actions.actions } : {}
3445
+ };
2933
3446
  }
2934
- async getAccountApy(state, chainId, days, tokenSymbol) {
2935
- return mapYieldseekerApy(
2936
- await this.walletRequest(
3447
+ async deployAfterFunding(state, chainId, context) {
3448
+ if (context.wallet.initializedDate != null) return;
3449
+ try {
3450
+ const deployed = await this.walletRequest(
2937
3451
  state,
2938
3452
  chainId,
2939
- `/apy${query({ chainId, days, tokenSymbol })}`
2940
- )
2941
- );
3453
+ this.agentPath(context, "deploy"),
3454
+ { method: "POST", body: {} }
3455
+ );
3456
+ if (deployed?.agentWallet) {
3457
+ context.wallet = deployed.agentWallet;
3458
+ }
3459
+ } catch (error) {
3460
+ console.warn(
3461
+ "[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
3462
+ error
3463
+ );
3464
+ }
2942
3465
  }
2943
- async getHistory(state, chainId, options) {
2944
- return mapYieldseekerHistory(
2945
- await this.walletRequest(
3466
+ async refreshSnapshotAfterMovement(state, chainId, context, movement) {
3467
+ try {
3468
+ const response = await this.walletRequest(
2946
3469
  state,
2947
3470
  chainId,
2948
- `/history${query({
2949
- chainId,
2950
- limit: options?.limit ?? 10,
2951
- cursor: options?.cursor,
2952
- fromDate: options?.fromDate,
2953
- toDate: options?.toDate
3471
+ `${this.agentPath(context, "snapshot")}${query({
3472
+ shouldForceRefresh: true
2954
3473
  })}`
2955
- )
2956
- );
3474
+ );
3475
+ if (!response?.agentSnapshot) {
3476
+ throw this.invalidResponse("agent snapshot refresh");
3477
+ }
3478
+ } catch (error) {
3479
+ console.warn(
3480
+ `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3481
+ error
3482
+ );
3483
+ }
2957
3484
  }
2958
- async getUserProfile(state, chainId) {
2959
- return mapYieldseekerProfile(
2960
- await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
2961
- );
3485
+ agentPath(context, suffix) {
3486
+ return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
2962
3487
  }
2963
- async getAgentApy(days, options) {
2964
- this.assertOptionalChain(options?.chainId);
2965
- return mapYieldseekerAgentApy(
2966
- await this.api.request(
2967
- `/agent/apy${query({
2968
- days,
2969
- tokenSymbol: options?.tokenSymbol,
2970
- chainId: options?.chainId
2971
- })}`
2972
- )
2973
- );
2974
- }
2975
- async ensureActivated(state, chainId, asset) {
2976
- const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
2977
- if (this.activated.has(key2)) return;
2978
- const response = await this.walletRequest(
2979
- state,
2980
- chainId,
2981
- "/activation",
2982
- { method: "POST", body: { chainId, asset } }
2983
- );
2984
- if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
2985
- throw this.invalidResponse("activation");
3488
+ async walletRequest(state, chainId, path, options = {}) {
3489
+ try {
3490
+ return await this.providerRequest(state, chainId, path, options);
3491
+ } catch (error) {
3492
+ throw this.mapApiError(error);
2986
3493
  }
2987
- this.activated.add(key2);
2988
3494
  }
2989
- async walletRequest(state, chainId, path, options = {}) {
3495
+ async providerRequest(state, chainId, path, options = {}) {
2990
3496
  this.assertChain(chainId);
3497
+ const request = (signature2) => this.api.request(path, {
3498
+ ...options,
3499
+ signature: signature2
3500
+ });
2991
3501
  let signature = await this.auth.getToken(state, chainId);
2992
3502
  try {
2993
- return await this.api.request(path, { ...options, signature });
3503
+ return await request(signature);
2994
3504
  } catch (error) {
2995
3505
  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);
3506
+ if (error.providerCode === "NO_USER") throw error;
3507
+ if (!error.isAuthenticationError) throw error;
3508
+ this.auth.clear(state, chainId);
3509
+ signature = await this.auth.getToken(state, chainId);
3510
+ try {
3511
+ return await request(signature);
3512
+ } catch (retryError) {
3513
+ if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
3514
+ this.forgetUser(state, chainId);
3003
3515
  }
3516
+ throw retryError;
3004
3517
  }
3005
- throw this.mapApiError(error);
3006
3518
  }
3007
3519
  }
3008
3520
  mapApiError(error) {
@@ -3030,15 +3542,8 @@ var YieldseekerAgent = class {
3030
3542
  if (this.transactionExecutor) {
3031
3543
  return this.transactionExecutor(state, chainId, transaction);
3032
3544
  }
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
- }
3545
+ this.assertTransaction(transaction, state, chainId);
3036
3546
  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
3547
  const walletClient = (0, import_viem6.createWalletClient)({
3043
3548
  account,
3044
3549
  chain: import_chains3.base,
@@ -3074,7 +3579,7 @@ var YieldseekerAgent = class {
3074
3579
  }
3075
3580
  return hash;
3076
3581
  }
3077
- async waitForUnwindReceipt(state, chainId, transactionHash) {
3582
+ async waitForReceipt(state, chainId, transactionHash) {
3078
3583
  if (this.unwindReceiptWaiter) {
3079
3584
  await this.unwindReceiptWaiter(state, chainId, transactionHash);
3080
3585
  return;
@@ -3090,15 +3595,32 @@ var YieldseekerAgent = class {
3090
3595
  if (receipt.status !== "success") {
3091
3596
  throw new OwneyError(
3092
3597
  "AGENT_TRANSACTION_REVERTED",
3093
- `Yieldseeker position unwind reverted (${transactionHash}).`,
3598
+ `Yieldseeker transaction reverted (${transactionHash}).`,
3094
3599
  { transactionHash },
3095
3600
  this.id
3096
3601
  );
3097
3602
  }
3098
3603
  }
3099
- positionMatchesChain(chain, chainId) {
3100
- const normalized = chain.trim().toUpperCase();
3101
- return normalized === String(chainId) || normalized === "BASE";
3604
+ assertTransaction(transaction, state, chainId) {
3605
+ 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)) {
3606
+ throw this.invalidResponse("transaction");
3607
+ }
3608
+ }
3609
+ assertAgent(agent) {
3610
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
3611
+ throw this.invalidResponse("agent");
3612
+ }
3613
+ }
3614
+ isOwneyAgent(agent) {
3615
+ return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
3616
+ }
3617
+ assetForAgent(agent) {
3618
+ for (const asset of ["USDC", "WETH"]) {
3619
+ if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
3620
+ return asset;
3621
+ }
3622
+ }
3623
+ return null;
3102
3624
  }
3103
3625
  isTransactionHash(value) {
3104
3626
  return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
@@ -3120,8 +3642,12 @@ var YieldseekerAgent = class {
3120
3642
  if (asset !== "USDC" && asset !== "WETH") {
3121
3643
  throw new OwneyError(
3122
3644
  "ASSET_UNSUPPORTED",
3123
- `Yieldseeker does not support asset ${asset}.`,
3124
- { asset, supportedAssets: ["USDC", "WETH"] },
3645
+ `Yieldseeker does not support asset ${asset} in the Owney rollout.`,
3646
+ {
3647
+ asset,
3648
+ supportedAssets: ["USDC", "WETH"],
3649
+ providerAlsoAdvertises: ["cbBTC"]
3650
+ },
3125
3651
  this.id
3126
3652
  );
3127
3653
  }
@@ -3343,11 +3869,11 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
3343
3869
  return Number.isFinite(total) && total > 0 ? total : 0;
3344
3870
  }
3345
3871
  const normalizedToken = tokenSymbol.toUpperCase();
3346
- return balance.tokens.reduce((total, token2) => {
3347
- if (Number(token2.chainId) !== chainId || String(token2.asset).toUpperCase() !== normalizedToken) {
3872
+ return balance.tokens.reduce((total, token) => {
3873
+ if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
3348
3874
  return total;
3349
3875
  }
3350
- const amount = Number(token2.amount);
3876
+ const amount = Number(token.amount);
3351
3877
  return Number.isFinite(amount) && amount > 0 ? total + amount : total;
3352
3878
  }, 0);
3353
3879
  }
@@ -3417,8 +3943,8 @@ function makeSponsoredWethCallback(deps) {
3417
3943
  return makeVerificationAwareDepositCallback(
3418
3944
  async (smartWallet, chainId, amount, verification) => {
3419
3945
  const cid = chainId;
3420
- const token2 = deps.tokenAddressByChain[cid];
3421
- if (!token2) {
3946
+ const token = deps.tokenAddressByChain[cid];
3947
+ if (!token) {
3422
3948
  throw new OwneyError(
3423
3949
  "CHAIN_UNSUPPORTED",
3424
3950
  `No sponsored WETH configured for chain ${chainId}`
@@ -3429,12 +3955,12 @@ function makeSponsoredWethCallback(deps) {
3429
3955
  const wallet = deps.getWalletClient(cid);
3430
3956
  await ensureWalletOnChain(pub, wallet, cid);
3431
3957
  try {
3432
- const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
3958
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3433
3959
  if (balance < amountWei) {
3434
3960
  throw new OwneyError(
3435
3961
  "DEPOSIT_INSUFFICIENT_BALANCE",
3436
3962
  "Insufficient WETH balance for this deposit.",
3437
- { token: token2, chainId: cid, balance: balance.toString(), amount }
3963
+ { token, chainId: cid, balance: balance.toString(), amount }
3438
3964
  );
3439
3965
  }
3440
3966
  } catch (err) {
@@ -3446,14 +3972,14 @@ function makeSponsoredWethCallback(deps) {
3446
3972
  }
3447
3973
  const allowance = await readPermit2Allowance(
3448
3974
  pub,
3449
- token2,
3975
+ token,
3450
3976
  deps.ownerAddress
3451
3977
  );
3452
3978
  if (allowance < amountWei) {
3453
3979
  throw new OwneyError(
3454
3980
  "PERMIT2_APPROVAL_REQUIRED",
3455
3981
  "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3456
- { token: token2, chainId: cid, allowance: allowance.toString(), amount }
3982
+ { token, chainId: cid, allowance: allowance.toString(), amount }
3457
3983
  );
3458
3984
  }
3459
3985
  const relayer = await get({
@@ -3468,7 +3994,7 @@ function makeSponsoredWethCallback(deps) {
3468
3994
  const typedData = buildPermitTransferFromTypedData({
3469
3995
  chainId: cid,
3470
3996
  message: {
3471
- permitted: { token: token2, amount: amountWei },
3997
+ permitted: { token, amount: amountWei },
3472
3998
  spender: relayer,
3473
3999
  nonce,
3474
4000
  deadline
@@ -3485,13 +4011,17 @@ function makeSponsoredWethCallback(deps) {
3485
4011
  ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
3486
4012
  body: {
3487
4013
  chainId: cid,
3488
- token: token2,
4014
+ token,
3489
4015
  from: deps.ownerAddress,
3490
4016
  to: smartWallet,
3491
4017
  amount,
3492
4018
  nonce: nonce.toString(),
3493
4019
  deadline: deadline.toString(),
3494
- signature
4020
+ signature,
4021
+ ...verification?.agentId === "yieldseeker" ? {
4022
+ yieldseekerUserId: verification.userId,
4023
+ yieldseekerAgentId: verification.yieldseekerAgentId
4024
+ } : {}
3495
4025
  }
3496
4026
  });
3497
4027
  return result.txHash;
@@ -3526,10 +4056,10 @@ function makeSponsoredCallsCallback(deps) {
3526
4056
  }
3527
4057
  return new URL(configured, origin).toString();
3528
4058
  };
3529
- return async (smartWallet, chainId, amount) => {
4059
+ return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
3530
4060
  const cid = chainId;
3531
- const token2 = deps.tokenAddressByChain[cid];
3532
- if (!token2) {
4061
+ const token = deps.tokenAddressByChain[cid];
4062
+ if (!token) {
3533
4063
  throw new OwneyError(
3534
4064
  "CHAIN_UNSUPPORTED",
3535
4065
  `No sponsored token configured for chain ${chainId}`
@@ -3547,6 +4077,32 @@ function makeSponsoredCallsCallback(deps) {
3547
4077
  functionName: "transfer",
3548
4078
  args: [smartWallet, BigInt(amount)]
3549
4079
  });
4080
+ let paymasterUrl = absolutePaymasterUrl();
4081
+ if (verification?.agentId === "yieldseeker") {
4082
+ if (chainId !== 8453) {
4083
+ throw new OwneyError(
4084
+ "CHAIN_UNSUPPORTED",
4085
+ `Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
4086
+ );
4087
+ }
4088
+ const { intent } = await postPaymasterIntent({
4089
+ baseUrl: deps.routingApiBaseUrl,
4090
+ apiKey: deps.apiKey,
4091
+ yieldseekerSignature: verification.signature,
4092
+ body: {
4093
+ chainId,
4094
+ token,
4095
+ from: deps.ownerAddress,
4096
+ to: smartWallet,
4097
+ amount,
4098
+ yieldseekerUserId: verification.userId,
4099
+ yieldseekerAgentId: verification.yieldseekerAgentId
4100
+ }
4101
+ });
4102
+ const url = new URL(paymasterUrl);
4103
+ url.searchParams.set("owneyIntent", intent);
4104
+ paymasterUrl = url.toString();
4105
+ }
3550
4106
  const sendResult = await deps.provider.request({
3551
4107
  method: "wallet_sendCalls",
3552
4108
  params: [
@@ -3555,9 +4111,9 @@ function makeSponsoredCallsCallback(deps) {
3555
4111
  from: deps.ownerAddress,
3556
4112
  chainId: (0, import_viem8.toHex)(chainId),
3557
4113
  atomicRequired: false,
3558
- calls: [{ to: token2, value: "0x0", data }],
4114
+ calls: [{ to: token, value: "0x0", data }],
3559
4115
  capabilities: {
3560
- paymasterService: { url: absolutePaymasterUrl() }
4116
+ paymasterService: { url: paymasterUrl }
3561
4117
  }
3562
4118
  }
3563
4119
  ]
@@ -3587,17 +4143,17 @@ function makeSponsoredCallsCallback(deps) {
3587
4143
  `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
3588
4144
  { chainId, callsId }
3589
4145
  );
3590
- };
4146
+ });
3591
4147
  }
3592
4148
 
3593
4149
  // src/client.ts
3594
4150
  function encodeMultiAgentCursor(map) {
3595
4151
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
3596
4152
  }
3597
- function decodeMultiAgentCursor(token2) {
4153
+ function decodeMultiAgentCursor(token) {
3598
4154
  let parsed;
3599
4155
  try {
3600
- const json = Buffer.from(token2, "base64").toString("utf8");
4156
+ const json = Buffer.from(token, "base64").toString("utf8");
3601
4157
  parsed = JSON.parse(json);
3602
4158
  } catch {
3603
4159
  throw new InvalidHistoryCursorError(
@@ -3786,6 +4342,8 @@ var OwneySDK = class {
3786
4342
  if (!onApproved && cached) return cached;
3787
4343
  const provider = this.requireConnectedProvider();
3788
4344
  const callback = makeSponsoredCallsCallback({
4345
+ apiKey: this.apiKey,
4346
+ routingApiBaseUrl: this.routingApiBaseUrl,
3789
4347
  provider,
3790
4348
  ownerAddress: this.state.walletAddress,
3791
4349
  paymasterServiceUrl: this.paymasterServiceUrl,
@@ -4228,15 +4786,15 @@ var OwneySDK = class {
4228
4786
  agent,
4229
4787
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
4230
4788
  }));
4231
- const valid = splits.filter(
4789
+ const valid2 = splits.filter(
4232
4790
  (s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
4233
4791
  );
4234
- if (valid.length === agents.length) {
4792
+ if (valid2.length === agents.length) {
4235
4793
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
4236
4794
  }
4237
4795
  return this.splitDepositAmount(
4238
4796
  totalAmount,
4239
- valid.map((s) => s.agent),
4797
+ valid2.map((s) => s.agent),
4240
4798
  chainId,
4241
4799
  asset,
4242
4800
  exempt
@@ -4269,10 +4827,10 @@ var OwneySDK = class {
4269
4827
  try {
4270
4828
  const balance = await agent.getBalances(state, chainId);
4271
4829
  const target = asset.toLowerCase();
4272
- const token2 = balance.tokens.find(
4830
+ const token = balance.tokens.find(
4273
4831
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
4274
4832
  );
4275
- return !!token2 && Number(token2.amount) > 0;
4833
+ return !!token && Number(token.amount) > 0;
4276
4834
  } catch {
4277
4835
  return false;
4278
4836
  }
@@ -4323,9 +4881,9 @@ var OwneySDK = class {
4323
4881
  const { asset, amount, agentId } = options;
4324
4882
  const state = this.requireState();
4325
4883
  const chainId = this.requireChainId();
4326
- const token2 = asset;
4884
+ const token = asset;
4327
4885
  const assetInfo = SupportedAssets.find(
4328
- (a) => a.chainId === chainId && a.symbol === token2
4886
+ (a) => a.chainId === chainId && a.symbol === token
4329
4887
  );
4330
4888
  if (!assetInfo) {
4331
4889
  throw new OwneyError(
@@ -4339,7 +4897,7 @@ var OwneySDK = class {
4339
4897
  return withFailureReporting(
4340
4898
  this.apiKey,
4341
4899
  agent.id,
4342
- () => agent.withdraw(state, chainId, token2, amount),
4900
+ () => agent.withdraw(state, chainId, token, amount),
4343
4901
  this.routingApiBaseUrl
4344
4902
  );
4345
4903
  }
@@ -4349,7 +4907,7 @@ var OwneySDK = class {
4349
4907
  const agentErrors2 = {};
4350
4908
  for (const agent of eligibleAgents) {
4351
4909
  try {
4352
- results2[agent.id] = await agent.withdraw(state, chainId, token2);
4910
+ results2[agent.id] = await agent.withdraw(state, chainId, token);
4353
4911
  } catch (err) {
4354
4912
  console.error(`withdraw failed for agent "${agent.id}":`, err);
4355
4913
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
@@ -4439,7 +4997,7 @@ var OwneySDK = class {
4439
4997
  results[p.agent.id] = await p.agent.withdraw(
4440
4998
  state,
4441
4999
  chainId,
4442
- token2,
5000
+ token,
4443
5001
  p.planned.toString()
4444
5002
  );
4445
5003
  } catch (err) {
@@ -4790,8 +5348,8 @@ var OwneySDK = class {
4790
5348
  const state = this.requireState();
4791
5349
  const chainId = this.requireChainId();
4792
5350
  this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
4793
- const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
4794
- if (!token2) {
5351
+ const token = SPONSORED_WETH_BY_CHAIN[chainId];
5352
+ if (!token) {
4795
5353
  throw new OwneyError(
4796
5354
  "CHAIN_UNSUPPORTED",
4797
5355
  `No sponsored WETH on chain ${chainId}`
@@ -4804,7 +5362,7 @@ var OwneySDK = class {
4804
5362
  transport: (0, import_viem9.custom)(provider)
4805
5363
  });
4806
5364
  const hash = await wallet.writeContract({
4807
- address: token2,
5365
+ address: token,
4808
5366
  abi: ERC20_ALLOWANCE_ABI,
4809
5367
  functionName: "approve",
4810
5368
  args: [PERMIT2_ADDRESS, MAX_UINT256],
@@ -4929,8 +5487,8 @@ var import_siwe2 = require("siwe");
4929
5487
  var import_sdk2 = require("@zyfai/sdk");
4930
5488
 
4931
5489
  // src/agents/zyfai/zyfai.siwx-cache.ts
4932
- var KEY_PREFIX3 = "owney.siwx.session";
4933
- var storage3 = () => {
5490
+ var KEY_PREFIX4 = "owney.siwx.session";
5491
+ var storage4 = () => {
4934
5492
  if (typeof window === "undefined") return null;
4935
5493
  try {
4936
5494
  return window.localStorage;
@@ -4938,8 +5496,8 @@ var storage3 = () => {
4938
5496
  return null;
4939
5497
  }
4940
5498
  };
4941
- var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
4942
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
5499
+ var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
5500
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
4943
5501
  var memorySiwxSessions = /* @__PURE__ */ new Map();
4944
5502
  var readLegacySiwxSession = (store, address) => {
4945
5503
  if (!store) return null;
@@ -4971,16 +5529,16 @@ var readLegacySiwxSession = (store, address) => {
4971
5529
  var readSiwxSession = (address, chainId) => {
4972
5530
  if (typeof window === "undefined") return null;
4973
5531
  const key2 = buildKey3(address);
4974
- const store = storage3();
4975
- let raw = null;
5532
+ const store = storage4();
5533
+ let raw2 = null;
4976
5534
  try {
4977
- raw = store?.getItem(key2) ?? null;
5535
+ raw2 = store?.getItem(key2) ?? null;
4978
5536
  } catch {
4979
- raw = null;
5537
+ raw2 = null;
4980
5538
  }
4981
- if (raw) {
5539
+ if (raw2) {
4982
5540
  try {
4983
- return JSON.parse(raw);
5541
+ return JSON.parse(raw2);
4984
5542
  } catch {
4985
5543
  memorySiwxSessions.delete(key2);
4986
5544
  try {
@@ -5001,7 +5559,7 @@ var writeSiwxSession = (address, _chainId, session) => {
5001
5559
  if (typeof window === "undefined") return;
5002
5560
  const key2 = buildKey3(address);
5003
5561
  memorySiwxSessions.set(key2, session);
5004
- const store = storage3();
5562
+ const store = storage4();
5005
5563
  try {
5006
5564
  store?.setItem(key2, JSON.stringify(session));
5007
5565
  } catch {
@@ -5010,7 +5568,7 @@ var writeSiwxSession = (address, _chainId, session) => {
5010
5568
  var clearSiwxSession = (address, _chainId) => {
5011
5569
  const key2 = buildKey3(address);
5012
5570
  memorySiwxSessions.delete(key2);
5013
- const store = storage3();
5571
+ const store = storage4();
5014
5572
  try {
5015
5573
  store?.removeItem(key2);
5016
5574
  } catch {