@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.js CHANGED
@@ -147,18 +147,18 @@ function tokenDecimals(symbol, explicit) {
147
147
  return explicit;
148
148
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
149
149
  }
150
- function mapDeposit(raw) {
150
+ function mapDeposit(raw2) {
151
151
  return {
152
- txHash: raw.txHash,
153
- smartWallet: raw.smartWallet,
154
- amount: raw.amount
152
+ txHash: raw2.txHash,
153
+ smartWallet: raw2.smartWallet,
154
+ amount: raw2.amount
155
155
  };
156
156
  }
157
- function mapWithdraw(raw) {
157
+ function mapWithdraw(raw2) {
158
158
  return {
159
- txHash: raw.txHash,
160
- type: raw.type,
161
- amount: raw.amount
159
+ txHash: raw2.txHash,
160
+ type: raw2.type,
161
+ amount: raw2.amount
162
162
  };
163
163
  }
164
164
  var CHAIN_ID_TO_NAME = {
@@ -177,10 +177,10 @@ function resolveChainId(chain) {
177
177
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
178
178
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
179
179
  }
180
- function mapPendingAllocations(raw) {
181
- if (!Array.isArray(raw)) return void 0;
180
+ function mapPendingAllocations(raw2) {
181
+ if (!Array.isArray(raw2)) return void 0;
182
182
  const pending = [];
183
- for (const entry of raw) {
183
+ for (const entry of raw2) {
184
184
  if (typeof entry !== "object" || entry === null) continue;
185
185
  const e = entry;
186
186
  if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
@@ -203,8 +203,8 @@ function mapPendingAllocations(raw) {
203
203
  }
204
204
  return pending.length > 0 ? pending : void 0;
205
205
  }
206
- function mapBalances(raw, _chainId, smartWallet) {
207
- const portfolio = raw.portfolio;
206
+ function mapBalances(raw2, _chainId, smartWallet) {
207
+ const portfolio = raw2.portfolio;
208
208
  const portfolioByChain = portfolio.portfolioByChain ?? {};
209
209
  let totalBalance = 0;
210
210
  const tokens = [];
@@ -273,8 +273,8 @@ function sumTokenValues(tokens) {
273
273
  function sumTokenEarnings(tokens) {
274
274
  return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
275
275
  }
276
- function mapEarnings(raw, smartWallet) {
277
- const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
276
+ function mapEarnings(raw2, smartWallet) {
277
+ const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
278
278
  const tokens = [];
279
279
  for (const [chainIdKey, tokensBySymbol] of Object.entries(
280
280
  totalEarningsByChain
@@ -293,22 +293,22 @@ function mapEarnings(raw, smartWallet) {
293
293
  return {
294
294
  smartWallet,
295
295
  lifetimeEarnings: sumTokenEarnings(
296
- raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
296
+ raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
297
297
  ),
298
298
  tokens
299
299
  };
300
300
  }
301
- function mapWeightedApyByChain(raw) {
302
- if (!raw) return void 0;
301
+ function mapWeightedApyByChain(raw2) {
302
+ if (!raw2) return void 0;
303
303
  const out = {};
304
- for (const [chainKey, tokenApy] of Object.entries(raw)) {
304
+ for (const [chainKey, tokenApy] of Object.entries(raw2)) {
305
305
  const chainId = Number(chainKey);
306
306
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
307
307
  const perAsset = {};
308
308
  for (const [symbol, apy] of Object.entries(tokenApy)) {
309
- const token2 = symbol;
310
- if (!SUPPORTED_TOKENS.includes(token2)) continue;
311
- perAsset[token2] = apy;
309
+ const token = symbol;
310
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
311
+ perAsset[token] = apy;
312
312
  }
313
313
  if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
314
314
  }
@@ -327,15 +327,15 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
327
327
  }
328
328
  return totalBalance > 0 ? weightedSum / totalBalance : null;
329
329
  }
330
- function mapApyHistory(raw, chainId, tokenSymbol) {
331
- const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
330
+ function mapApyHistory(raw2, chainId, tokenSymbol) {
331
+ const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
332
332
  date,
333
333
  apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
334
334
  })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
335
335
  return {
336
- walletAddress: raw.walletAddress,
337
- weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
338
- apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
336
+ walletAddress: raw2.walletAddress,
337
+ weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
338
+ apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
339
339
  history
340
340
  };
341
341
  }
@@ -427,23 +427,23 @@ function mapEntries(rawEntries, chainId) {
427
427
  };
428
428
  });
429
429
  }
430
- function mapUserProfile(raw, userAddress) {
430
+ function mapUserProfile(raw2, userAddress) {
431
431
  return {
432
432
  address: userAddress,
433
- smartWallet: raw.smartWallet || "",
434
- chains: raw.chains || [],
435
- strategy: raw.strategy,
436
- hasActiveSessionKey: raw.hasActiveSessionKey || false,
437
- protocols: raw.protocols || [],
438
- splitting: raw.splitting,
439
- minSplits: raw.minSplits
433
+ smartWallet: raw2.smartWallet || "",
434
+ chains: raw2.chains || [],
435
+ strategy: raw2.strategy,
436
+ hasActiveSessionKey: raw2.hasActiveSessionKey || false,
437
+ protocols: raw2.protocols || [],
438
+ splitting: raw2.splitting,
439
+ minSplits: raw2.minSplits
440
440
  };
441
441
  }
442
- function mapApyByStrategy(raw) {
442
+ function mapApyByStrategy(raw2) {
443
443
  const apyPerAsset = {};
444
444
  let apySum = 0;
445
445
  let apyCount = 0;
446
- for (const entry of raw.data) {
446
+ for (const entry of raw2.data) {
447
447
  const supported = SupportedAssets.find(
448
448
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
449
449
  );
@@ -477,23 +477,23 @@ function computeAllocationApy(positions) {
477
477
  weightedSum += apy * value;
478
478
  totalValue += value;
479
479
  const chainId = resolveChainId(p.chain);
480
- const token2 = p.asset;
480
+ const token = p.asset;
481
481
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
482
- if (!SUPPORTED_TOKENS.includes(token2)) continue;
482
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
483
483
  const perAsset = buckets[chainId] ?? {};
484
- const cell = perAsset[token2] ?? { weightedSum: 0, value: 0 };
484
+ const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
485
485
  cell.weightedSum += apy * value;
486
486
  cell.value += value;
487
- perAsset[token2] = cell;
487
+ perAsset[token] = cell;
488
488
  buckets[chainId] = perAsset;
489
489
  }
490
490
  const apyByChainAndAsset = {};
491
491
  for (const [chainKey, perAsset] of Object.entries(buckets)) {
492
492
  const chainId = Number(chainKey);
493
493
  const out = {};
494
- for (const [token2, cell] of Object.entries(perAsset ?? {})) {
494
+ for (const [token, cell] of Object.entries(perAsset ?? {})) {
495
495
  if (cell && cell.value > 0) {
496
- out[token2] = cell.weightedSum / cell.value;
496
+ out[token] = cell.weightedSum / cell.value;
497
497
  }
498
498
  }
499
499
  if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
@@ -507,9 +507,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
507
507
  function topicToAddress(topic) {
508
508
  return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
509
509
  }
510
- function extractWithdrawnAmount(logs, recipient, token2, decimals) {
510
+ function extractWithdrawnAmount(logs, recipient, token, decimals) {
511
511
  const wantRecipient = recipient.toLowerCase();
512
- const wantToken = token2.toLowerCase();
512
+ const wantToken = token.toLowerCase();
513
513
  let total = 0n;
514
514
  let matched = false;
515
515
  for (const log of logs) {
@@ -535,11 +535,11 @@ var InvalidHistoryCursorError = class extends Error {
535
535
  function encodeHistoryCursor(payload) {
536
536
  return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
537
537
  }
538
- function decodeHistoryCursor(token2) {
539
- if (!token2) throw new InvalidHistoryCursorError("Cursor is empty.");
538
+ function decodeHistoryCursor(token) {
539
+ if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
540
540
  let parsed;
541
541
  try {
542
- const json = Buffer.from(token2, "base64").toString("utf8");
542
+ const json = Buffer.from(token, "base64").toString("utf8");
543
543
  parsed = JSON.parse(json);
544
544
  } catch {
545
545
  throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
@@ -562,8 +562,8 @@ var storage = () => {
562
562
  };
563
563
  var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
564
564
  var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
565
- var isJwtExpired = (token2) => {
566
- const parts = token2.split(".");
565
+ var isJwtExpired = (token) => {
566
+ const parts = token.split(".");
567
567
  if (parts.length !== 3) return false;
568
568
  try {
569
569
  const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
@@ -610,15 +610,15 @@ var readSession = (address, _chainId) => {
610
610
  if (typeof window === "undefined") return null;
611
611
  const key2 = buildKey(address);
612
612
  const store = storage();
613
- let raw = null;
613
+ let raw2 = null;
614
614
  try {
615
- raw = store?.getItem(key2) ?? null;
615
+ raw2 = store?.getItem(key2) ?? null;
616
616
  } catch {
617
- raw = null;
617
+ raw2 = null;
618
618
  }
619
- if (raw) {
619
+ if (raw2) {
620
620
  try {
621
- const parsed = JSON.parse(raw);
621
+ const parsed = JSON.parse(raw2);
622
622
  if (isFreshSession(parsed)) return parsed;
623
623
  } catch {
624
624
  }
@@ -786,8 +786,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
786
786
  }
787
787
  return result;
788
788
  }
789
- function flattenAvailablePools(raw) {
790
- const byChain = raw ?? {};
789
+ function flattenAvailablePools(raw2) {
790
+ const byChain = raw2 ?? {};
791
791
  const names = [];
792
792
  for (const byToken of Object.values(byChain ?? {})) {
793
793
  for (const entry of Object.values(byToken ?? {})) {
@@ -1298,8 +1298,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1298
1298
  const poolResults = await Promise.all(
1299
1299
  universe.map(async (protocol) => {
1300
1300
  try {
1301
- const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1302
- return [protocol.id, flattenAvailablePools(raw)];
1301
+ const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
1302
+ return [protocol.id, flattenAvailablePools(raw2)];
1303
1303
  } catch (error) {
1304
1304
  console.warn(
1305
1305
  `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
@@ -1365,14 +1365,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1365
1365
  async readWalletState(ownerAddress) {
1366
1366
  try {
1367
1367
  const { portfolio } = await this.sdk.getPositions(ownerAddress);
1368
- const raw = portfolio;
1368
+ const raw2 = portfolio;
1369
1369
  debugLog("zyfai:onboard", "wallet state from getPositions", {
1370
- predeployed: raw?.predeployed,
1371
- hasActiveSessionKey: raw?.hasActiveSessionKey
1370
+ predeployed: raw2?.predeployed,
1371
+ hasActiveSessionKey: raw2?.hasActiveSessionKey
1372
1372
  });
1373
1373
  return {
1374
- predeployed: raw?.predeployed,
1375
- hasActiveSessionKey: raw?.hasActiveSessionKey
1374
+ predeployed: raw2?.predeployed,
1375
+ hasActiveSessionKey: raw2?.hasActiveSessionKey
1376
1376
  };
1377
1377
  } catch (error) {
1378
1378
  console.warn(
@@ -1652,48 +1652,48 @@ var ZyfaiAgent = class _ZyfaiAgent {
1652
1652
  return { txHash, smartWallet, amount };
1653
1653
  }
1654
1654
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
1655
- const raw = await this.sdk.depositFunds(
1655
+ const raw2 = await this.sdk.depositFunds(
1656
1656
  this.getAddress(),
1657
1657
  validChainId,
1658
1658
  amount,
1659
1659
  asset,
1660
1660
  "aggressive"
1661
1661
  );
1662
- return mapDeposit(raw);
1662
+ return mapDeposit(raw2);
1663
1663
  } catch (error) {
1664
1664
  throw error;
1665
1665
  }
1666
1666
  }
1667
1667
  // --- IAgent: Fund operations ---
1668
- async withdraw(state, chainId, token2, amount) {
1668
+ async withdraw(state, chainId, token, amount) {
1669
1669
  const validChainId = isValidChainId(chainId);
1670
1670
  await this.ensureConnected(state, validChainId);
1671
- const raw = await this.sdk.withdrawFunds(
1671
+ const raw2 = await this.sdk.withdrawFunds(
1672
1672
  this.getAddress(),
1673
1673
  validChainId,
1674
1674
  amount,
1675
- token2
1675
+ token
1676
1676
  );
1677
- if (!raw.success) {
1677
+ if (!raw2.success) {
1678
1678
  throw new OwneyError(
1679
1679
  "WITHDRAW_FAILED",
1680
- raw.message || "Zyfai withdraw failed.",
1681
- { chainId: validChainId, token: token2, amount, response: raw },
1680
+ raw2.message || "Zyfai withdraw failed.",
1681
+ { chainId: validChainId, token, amount, response: raw2 },
1682
1682
  this.id
1683
1683
  );
1684
1684
  }
1685
- return mapWithdraw(raw);
1685
+ return mapWithdraw(raw2);
1686
1686
  }
1687
1687
  // --- IAgent: Portfolio reads ---
1688
1688
  async getBalances(state, chainId) {
1689
1689
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
1690
- const raw = await this.sdk.getPortfolio(this.getAddress());
1691
- return mapBalances(raw, validChainId, smartWallet);
1690
+ const raw2 = await this.sdk.getPortfolio(this.getAddress());
1691
+ return mapBalances(raw2, validChainId, smartWallet);
1692
1692
  }
1693
1693
  async getEarnings(state, chainId) {
1694
1694
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1695
- const raw = await this.sdk.getOnchainEarnings(smartWallet);
1696
- return mapEarnings(raw, smartWallet);
1695
+ const raw2 = await this.sdk.getOnchainEarnings(smartWallet);
1696
+ return mapEarnings(raw2, smartWallet);
1697
1697
  }
1698
1698
  async refreshEarnings(state, chainId) {
1699
1699
  if (this.earningsRefreshInFlight) return this.earningsRefreshInFlight;
@@ -1713,8 +1713,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1713
1713
  }
1714
1714
  async getAccountApy(state, chainId, days, tokenSymbol) {
1715
1715
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1716
- const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
1717
- return mapApyHistory(raw, chainId, tokenSymbol);
1716
+ const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
1717
+ return mapApyHistory(raw2, chainId, tokenSymbol);
1718
1718
  }
1719
1719
  /**
1720
1720
  * Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
@@ -1749,7 +1749,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1749
1749
  const matched = [];
1750
1750
  let backendExhausted = false;
1751
1751
  for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
1752
- const raw = await this.sdk.getHistory(smartWallet, validChainId, {
1752
+ const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
1753
1753
  limit: backendPageSize,
1754
1754
  offset,
1755
1755
  fromDate: options?.fromDate,
@@ -1760,13 +1760,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
1760
1760
  // asset's rows and handing back a page that filters to nothing.
1761
1761
  assetType
1762
1762
  });
1763
- raw.data.forEach((entry, idx) => {
1763
+ raw2.data.forEach((entry, idx) => {
1764
1764
  if (entry.chainId === validChainId) {
1765
1765
  matched.push({ entry, rawIdx: offset + idx });
1766
1766
  }
1767
1767
  });
1768
- offset += raw.data.length;
1769
- if (raw.data.length < backendPageSize) {
1768
+ offset += raw2.data.length;
1769
+ if (raw2.data.length < backendPageSize) {
1770
1770
  backendExhausted = true;
1771
1771
  break;
1772
1772
  }
@@ -1788,18 +1788,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
1788
1788
  }
1789
1789
  async getUserProfile(state, chainId) {
1790
1790
  await this.connectAuth(state, chainId);
1791
- const raw = await this.sdk.getUserDetails();
1791
+ const raw2 = await this.sdk.getUserDetails();
1792
1792
  debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
1793
1793
  asset: "USDC (default \u2014 no asset passed)",
1794
- splitting: raw.splitting,
1795
- minSplits: raw.minSplits,
1796
- strategy: raw.strategy,
1797
- chains: raw.chains,
1798
- protocolCount: raw.protocols?.length,
1799
- hasActiveSessionKey: raw.hasActiveSessionKey,
1800
- smartWallet: raw.smartWallet
1794
+ splitting: raw2.splitting,
1795
+ minSplits: raw2.minSplits,
1796
+ strategy: raw2.strategy,
1797
+ chains: raw2.chains,
1798
+ protocolCount: raw2.protocols?.length,
1799
+ hasActiveSessionKey: raw2.hasActiveSessionKey,
1800
+ smartWallet: raw2.smartWallet
1801
1801
  });
1802
- return mapUserProfile(raw, this.connectedAddress);
1802
+ return mapUserProfile(raw2, this.connectedAddress);
1803
1803
  }
1804
1804
  async ensureAutoSelectProtocols(state, chainId, asset) {
1805
1805
  await this.connectAuth(state, chainId);
@@ -1818,14 +1818,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1818
1818
  }
1819
1819
  // --- IAgent: Discovery (no wallet required) ---
1820
1820
  async getAgentApy(days, options) {
1821
- const raw = await this.sdk.getAPYPerStrategy(
1821
+ const raw2 = await this.sdk.getAPYPerStrategy(
1822
1822
  false,
1823
1823
  DayFilterMapping[days],
1824
1824
  "aggressive",
1825
1825
  options?.chainId,
1826
1826
  options?.tokenSymbol
1827
1827
  );
1828
- return mapApyByStrategy(raw);
1828
+ return mapApyByStrategy(raw2);
1829
1829
  }
1830
1830
  };
1831
1831
 
@@ -1834,9 +1834,10 @@ import {
1834
1834
  createPublicClient as createPublicClient3,
1835
1835
  createWalletClient as createWalletClient2,
1836
1836
  custom as custom2,
1837
+ encodeFunctionData,
1838
+ erc20Abi,
1837
1839
  getAddress as getAddress2,
1838
- isAddress,
1839
- parseUnits
1840
+ isAddress as isAddress2
1840
1841
  } from "viem";
1841
1842
  import { base as base3 } from "viem/chains";
1842
1843
 
@@ -1898,10 +1899,10 @@ function buildTransferWithAuthorizationTypedData(input) {
1898
1899
  message: input.message
1899
1900
  };
1900
1901
  }
1901
- async function readTokenMeta(publicClient, token2) {
1902
+ async function readTokenMeta(publicClient, token) {
1902
1903
  const [tokenName, tokenVersion] = await Promise.all([
1903
- publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "name" }),
1904
- publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1904
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
1905
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1905
1906
  ]);
1906
1907
  return { tokenName, tokenVersion };
1907
1908
  }
@@ -1913,6 +1914,45 @@ function randomAuthNonce() {
1913
1914
 
1914
1915
  // src/lib/sponsor-client.ts
1915
1916
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1917
+ async function postPaymasterIntent(input) {
1918
+ const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1919
+ let res;
1920
+ try {
1921
+ res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
1922
+ method: "POST",
1923
+ headers: {
1924
+ "content-type": "application/json",
1925
+ "x-owney-api-key": input.apiKey,
1926
+ Authorization: `Signature ${input.yieldseekerSignature}`
1927
+ },
1928
+ body: JSON.stringify(input.body)
1929
+ });
1930
+ } catch (networkError) {
1931
+ throw new OwneyError(
1932
+ "SPONSOR_REQUEST_FAILED",
1933
+ `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1934
+ { cause: String(networkError), safeToFallback: true }
1935
+ );
1936
+ }
1937
+ const text = await res.text();
1938
+ let parsed = null;
1939
+ try {
1940
+ parsed = JSON.parse(text);
1941
+ } catch {
1942
+ }
1943
+ if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
1944
+ throw new OwneyError(
1945
+ "SPONSOR_REQUEST_FAILED",
1946
+ `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1947
+ {
1948
+ statusCode: res.status,
1949
+ responseBody: text.slice(0, 500),
1950
+ safeToFallback: true
1951
+ }
1952
+ );
1953
+ }
1954
+ return parsed.data;
1955
+ }
1916
1956
  async function postSponsorTransferAuth(input) {
1917
1957
  const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
1918
1958
  let res;
@@ -1922,7 +1962,7 @@ async function postSponsorTransferAuth(input) {
1922
1962
  headers: {
1923
1963
  "content-type": "application/json",
1924
1964
  "x-owney-api-key": input.apiKey,
1925
- ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
1965
+ ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
1926
1966
  },
1927
1967
  body: JSON.stringify(input.body)
1928
1968
  });
@@ -1963,7 +2003,7 @@ async function postSponsorPermit2Transfer(input) {
1963
2003
  headers: {
1964
2004
  "content-type": "application/json",
1965
2005
  "x-owney-api-key": input.apiKey,
1966
- ...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
2006
+ ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
1967
2007
  },
1968
2008
  body: JSON.stringify(input.body)
1969
2009
  });
@@ -2091,17 +2131,17 @@ function randomPermit2Nonce() {
2091
2131
  globalThis.crypto.getRandomValues(bytes);
2092
2132
  return BigInt(bytesToHex2(bytes));
2093
2133
  }
2094
- async function readPermit2Allowance(publicClient, token2, owner) {
2134
+ async function readPermit2Allowance(publicClient, token, owner) {
2095
2135
  return publicClient.readContract({
2096
- address: token2,
2136
+ address: token,
2097
2137
  abi: ERC20_ALLOWANCE_ABI,
2098
2138
  functionName: "allowance",
2099
2139
  args: [owner, PERMIT2_ADDRESS]
2100
2140
  });
2101
2141
  }
2102
- async function readErc20Balance(publicClient, token2, owner) {
2142
+ async function readErc20Balance(publicClient, token, owner) {
2103
2143
  return publicClient.readContract({
2104
- address: token2,
2144
+ address: token,
2105
2145
  abi: ERC20_ALLOWANCE_ABI,
2106
2146
  functionName: "balanceOf",
2107
2147
  args: [owner]
@@ -2133,8 +2173,8 @@ function makeSponsoredDepositCallback(deps) {
2133
2173
  return makeVerificationAwareDepositCallback(
2134
2174
  async (smartWallet, chainId, amount, verification) => {
2135
2175
  const cid = chainId;
2136
- const token2 = deps.tokenAddressByChain[cid];
2137
- if (!token2) {
2176
+ const token = deps.tokenAddressByChain[cid];
2177
+ if (!token) {
2138
2178
  throw new OwneyError(
2139
2179
  "CHAIN_UNSUPPORTED",
2140
2180
  `No sponsored token configured for chain ${chainId}`
@@ -2144,12 +2184,12 @@ function makeSponsoredDepositCallback(deps) {
2144
2184
  const wallet = deps.getWalletClient(cid);
2145
2185
  await ensureWalletOnChain(pub, wallet, cid);
2146
2186
  try {
2147
- const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
2187
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2148
2188
  if (balance < BigInt(amount)) {
2149
2189
  throw new OwneyError(
2150
2190
  "DEPOSIT_INSUFFICIENT_BALANCE",
2151
2191
  "Insufficient balance for this deposit.",
2152
- { token: token2, chainId: cid, balance: balance.toString(), amount }
2192
+ { token, chainId: cid, balance: balance.toString(), amount }
2153
2193
  );
2154
2194
  }
2155
2195
  } catch (err) {
@@ -2159,14 +2199,14 @@ function makeSponsoredDepositCallback(deps) {
2159
2199
  err instanceof Error ? err.message : String(err)
2160
2200
  );
2161
2201
  }
2162
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token2);
2202
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2163
2203
  const validAfter = 0n;
2164
2204
  const validBefore = BigInt(
2165
2205
  Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2166
2206
  );
2167
2207
  const nonce = randomAuthNonce();
2168
2208
  const typedData = buildTransferWithAuthorizationTypedData({
2169
- token: token2,
2209
+ token,
2170
2210
  chainId: cid,
2171
2211
  tokenName,
2172
2212
  tokenVersion,
@@ -2190,7 +2230,7 @@ function makeSponsoredDepositCallback(deps) {
2190
2230
  ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
2191
2231
  body: {
2192
2232
  chainId: cid,
2193
- token: token2,
2233
+ token,
2194
2234
  from: deps.ownerAddress,
2195
2235
  to: smartWallet,
2196
2236
  value: amount,
@@ -2199,7 +2239,11 @@ function makeSponsoredDepositCallback(deps) {
2199
2239
  nonce,
2200
2240
  authSignature,
2201
2241
  tokenName,
2202
- tokenVersion
2242
+ tokenVersion,
2243
+ ...verification?.agentId === "yieldseeker" ? {
2244
+ yieldseekerUserId: verification.userId,
2245
+ yieldseekerAgentId: verification.yieldseekerAgentId
2246
+ } : {}
2203
2247
  }
2204
2248
  });
2205
2249
  return result.txHash;
@@ -2218,7 +2262,11 @@ import {
2218
2262
  import { base as base2 } from "viem/chains";
2219
2263
 
2220
2264
  // src/agents/yieldseeker/yieldseeker.auth-cache.ts
2221
- var KEY_PREFIX2 = "owney.yieldseeker.session";
2265
+ var KEY_PREFIX2 = "owney.yieldseeker.session.v4";
2266
+ var INVALIDATED_KEY_PREFIXES = [
2267
+ "owney.yieldseeker.session",
2268
+ "owney.yieldseeker.session.v3"
2269
+ ];
2222
2270
  var storage2 = () => {
2223
2271
  if (typeof window === "undefined") return null;
2224
2272
  try {
@@ -2228,6 +2276,18 @@ var storage2 = () => {
2228
2276
  }
2229
2277
  };
2230
2278
  var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2279
+ var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
2280
+ (prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
2281
+ );
2282
+ var clearInvalidatedSessions = (store, address, chainId) => {
2283
+ for (const key2 of invalidatedKeys(address, chainId)) {
2284
+ memorySessions2.delete(key2);
2285
+ try {
2286
+ store?.removeItem(key2);
2287
+ } catch {
2288
+ }
2289
+ }
2290
+ };
2231
2291
  var memorySessions2 = /* @__PURE__ */ new Map();
2232
2292
  var isValidSession = (session) => {
2233
2293
  if (!session?.token) return false;
@@ -2242,15 +2302,16 @@ var readYieldseekerSession = (address, chainId) => {
2242
2302
  if (typeof window === "undefined") return null;
2243
2303
  const key2 = buildKey2(address, chainId);
2244
2304
  const store = storage2();
2245
- let raw = null;
2305
+ clearInvalidatedSessions(store, address, chainId);
2306
+ let raw2 = null;
2246
2307
  try {
2247
- raw = store?.getItem(key2) ?? null;
2308
+ raw2 = store?.getItem(key2) ?? null;
2248
2309
  } catch {
2249
- raw = null;
2310
+ raw2 = null;
2250
2311
  }
2251
- if (raw) {
2312
+ if (raw2) {
2252
2313
  try {
2253
- const parsed = JSON.parse(raw);
2314
+ const parsed = JSON.parse(raw2);
2254
2315
  if (isValidSession(parsed)) return parsed.token;
2255
2316
  } catch {
2256
2317
  }
@@ -2266,9 +2327,9 @@ var readYieldseekerSession = (address, chainId) => {
2266
2327
  if (cached) memorySessions2.delete(key2);
2267
2328
  return null;
2268
2329
  };
2269
- var writeYieldseekerSession = (address, chainId, token2) => {
2330
+ var writeYieldseekerSession = (address, chainId, token) => {
2270
2331
  if (typeof window === "undefined") return;
2271
- const session = { token: token2 };
2332
+ const session = { token };
2272
2333
  if (!isValidSession(session)) return;
2273
2334
  const key2 = buildKey2(address, chainId);
2274
2335
  memorySessions2.set(key2, session);
@@ -2282,6 +2343,7 @@ var clearYieldseekerSession = (address, chainId) => {
2282
2343
  const key2 = buildKey2(address, chainId);
2283
2344
  memorySessions2.delete(key2);
2284
2345
  const store = storage2();
2346
+ clearInvalidatedSessions(store, address, chainId);
2285
2347
  try {
2286
2348
  store?.removeItem(key2);
2287
2349
  } catch {
@@ -2300,8 +2362,8 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2300
2362
  issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
2301
2363
  }).prepareMessage();
2302
2364
  }
2303
- function encodeYieldseekerAuthToken(token2) {
2304
- const bytes = new TextEncoder().encode(JSON.stringify(token2));
2365
+ function encodeYieldseekerAuthToken(token) {
2366
+ const bytes = new TextEncoder().encode(JSON.stringify(token));
2305
2367
  let binary = "";
2306
2368
  for (const byte of bytes) binary += String.fromCharCode(byte);
2307
2369
  return btoa(binary);
@@ -2330,10 +2392,10 @@ var YieldseekerAuth = class {
2330
2392
  }
2331
2393
  const inFlight = this.pending.get(key2);
2332
2394
  if (inFlight) return inFlight;
2333
- const request = this.sign(state, chainId).then((token2) => {
2334
- this.tokens.set(key2, token2);
2335
- writeYieldseekerSession(scope.address, scope.chainId, token2);
2336
- return token2;
2395
+ const request = this.sign(state, chainId).then((token) => {
2396
+ this.tokens.set(key2, token);
2397
+ writeYieldseekerSession(scope.address, scope.chainId, token);
2398
+ return token;
2337
2399
  });
2338
2400
  this.pending.set(key2, request);
2339
2401
  try {
@@ -2384,6 +2446,72 @@ var YieldseekerAuth = class {
2384
2446
  }
2385
2447
  };
2386
2448
 
2449
+ // src/agents/yieldseeker/yieldseeker.identity-cache.ts
2450
+ var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
2451
+ var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
2452
+ var memoryIdentities = /* @__PURE__ */ new Map();
2453
+ var storage3 = () => {
2454
+ if (typeof window === "undefined") return null;
2455
+ try {
2456
+ return window.localStorage;
2457
+ } catch {
2458
+ return null;
2459
+ }
2460
+ };
2461
+ var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
2462
+ function valid(value, walletAddress, chainId, now) {
2463
+ return Boolean(
2464
+ 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
2465
+ );
2466
+ }
2467
+ function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
2468
+ if (typeof window === "undefined") return null;
2469
+ const key2 = keyFor(walletAddress, chainId);
2470
+ const store = storage3();
2471
+ let parsed = null;
2472
+ try {
2473
+ const raw2 = store?.getItem(key2);
2474
+ parsed = raw2 ? JSON.parse(raw2) : null;
2475
+ } catch {
2476
+ parsed = null;
2477
+ }
2478
+ const candidate = parsed ?? memoryIdentities.get(key2);
2479
+ if (valid(candidate, walletAddress, chainId, now)) {
2480
+ memoryIdentities.set(key2, candidate);
2481
+ return { userId: candidate.userId };
2482
+ }
2483
+ memoryIdentities.delete(key2);
2484
+ try {
2485
+ store?.removeItem(key2);
2486
+ } catch {
2487
+ }
2488
+ return null;
2489
+ }
2490
+ function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
2491
+ if (typeof window === "undefined") return;
2492
+ const identity = {
2493
+ userId,
2494
+ walletAddress,
2495
+ chainId,
2496
+ expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
2497
+ };
2498
+ if (!valid(identity, walletAddress, chainId, now)) return;
2499
+ const key2 = keyFor(walletAddress, chainId);
2500
+ memoryIdentities.set(key2, identity);
2501
+ try {
2502
+ storage3()?.setItem(key2, JSON.stringify(identity));
2503
+ } catch {
2504
+ }
2505
+ }
2506
+ function clearYieldseekerIdentity(walletAddress, chainId) {
2507
+ const key2 = keyFor(walletAddress, chainId);
2508
+ memoryIdentities.delete(key2);
2509
+ try {
2510
+ storage3()?.removeItem(key2);
2511
+ } catch {
2512
+ }
2513
+ }
2514
+
2387
2515
  // src/agents/yieldseeker/yieldseeker.client.ts
2388
2516
  var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2389
2517
  function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
@@ -2434,7 +2562,7 @@ var YieldseekerApiClient = class {
2434
2562
  headers: {
2435
2563
  "Content-Type": "application/json",
2436
2564
  "x-owney-api-key": this.owneyApiKey,
2437
- ...options.signature ? { "X-Signature": options.signature } : {}
2565
+ ...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
2438
2566
  },
2439
2567
  body: options.body ? JSON.stringify(options.body) : void 0,
2440
2568
  signal: controller.signal
@@ -2467,25 +2595,21 @@ var YieldseekerApiClient = class {
2467
2595
  };
2468
2596
 
2469
2597
  // src/agents/yieldseeker/yieldseeker.mapper.ts
2470
- import { formatUnits } from "viem";
2471
- var REBALANCE_AMOUNT_DECIMALS = {
2472
- USDC: 6,
2473
- WETH: 18
2474
- };
2475
- function rebalanceAmount(value, tokenSymbol) {
2476
- const decimals = REBALANCE_AMOUNT_DECIMALS[tokenSymbol.toUpperCase()];
2477
- if (decimals === void 0) {
2478
- return invalid(
2479
- "history",
2480
- `unsupported rebalance token ${tokenSymbol || "<empty>"}`
2481
- );
2482
- }
2483
- try {
2484
- return formatUnits(BigInt(value), decimals);
2485
- } catch {
2486
- return invalid("history", `invalid rebalance amount ${value}`);
2598
+ import { formatUnits, isAddress } from "viem";
2599
+
2600
+ // src/agents/yieldseeker/yieldseeker.types.ts
2601
+ var YIELDSEEKER_ASSET_METADATA = {
2602
+ USDC: {
2603
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2604
+ decimals: 6
2605
+ },
2606
+ WETH: {
2607
+ address: "0x4200000000000000000000000000000000000006",
2608
+ decimals: 18
2487
2609
  }
2488
- }
2610
+ };
2611
+
2612
+ // src/agents/yieldseeker/yieldseeker.mapper.ts
2489
2613
  function invalid(endpoint, detail) {
2490
2614
  throw new OwneyError(
2491
2615
  "AGENT_INVALID_RESPONSE",
@@ -2494,193 +2618,313 @@ function invalid(endpoint, detail) {
2494
2618
  "yieldseeker"
2495
2619
  );
2496
2620
  }
2497
- function requireObject(value, endpoint) {
2498
- if (!value || typeof value !== "object" || Array.isArray(value)) {
2499
- return invalid(endpoint, "expected an object");
2621
+ function raw(value, endpoint) {
2622
+ if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
2623
+ return invalid(endpoint, "expected a base-10 integer string");
2500
2624
  }
2501
- return value;
2625
+ return BigInt(value);
2502
2626
  }
2503
- function requireEnvelope(value, key2, endpoint) {
2504
- const root = requireObject(value, endpoint);
2505
- return requireObject(root[key2], endpoint);
2627
+ function decimal(value, decimals, endpoint) {
2628
+ return formatUnits(raw(value, endpoint), decimals);
2506
2629
  }
2507
- function token(value) {
2508
- return {
2509
- chain: value.chain,
2510
- chainId: value.chainId,
2511
- asset: value.asset,
2512
- amount: String(value.amount)
2513
- };
2630
+ function usd(rawAmount, decimals, price) {
2631
+ return Number(formatUnits(rawAmount, decimals)) * price;
2514
2632
  }
2515
- function position(value) {
2516
- return {
2517
- chain: String(value.chain),
2518
- protocol: String(value.protocol),
2519
- ...value.protocolId ? { protocolId: value.protocolId } : {},
2520
- ...value.pool ? { pool: value.pool } : {},
2521
- asset: String(value.asset),
2522
- amount: String(value.amount),
2523
- ...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
2524
- ...typeof value.apy === "number" ? { apy: value.apy } : {},
2525
- ...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
2526
- ...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
2527
- };
2633
+ function percent(value) {
2634
+ const result = Number(value);
2635
+ return Number.isFinite(result) ? result * 100 : 0;
2528
2636
  }
2529
- function mapApyMatrix(matrix) {
2530
- const mapped = {};
2531
- for (const [chain, assets] of Object.entries(matrix ?? {})) {
2532
- const chainId = Number(chain);
2533
- if (chainId !== 8453 || !assets || typeof assets !== "object") continue;
2534
- mapped[8453] = {};
2535
- for (const [asset, apy] of Object.entries(assets)) {
2536
- if ((asset === "USDC" || asset === "WETH") && typeof apy === "number") {
2537
- mapped[8453][asset] = apy;
2538
- }
2539
- }
2540
- }
2541
- return mapped;
2637
+ var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
2638
+ function publicApyAfterYieldseekerFee(value) {
2639
+ const grossPercent = percent(value);
2640
+ if (grossPercent <= 0) return grossPercent;
2641
+ const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
2642
+ return Math.round(netPercent * 1e12) / 1e12;
2542
2643
  }
2543
- function mapYieldseekerProfile(value) {
2544
- const profile = requireEnvelope(
2545
- value,
2546
- "profile",
2547
- "profile"
2644
+ function riskAdjustedApyForDays(option, days) {
2645
+ if (days === "7D") return option.riskAdjustedApy7dAverage;
2646
+ if (days === "30D") return option.riskAdjustedApy30dAverage;
2647
+ return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
2648
+ }
2649
+ function assetAddressValue(record, address) {
2650
+ const entry = Object.entries(record).find(
2651
+ ([key2]) => key2.toLowerCase() === address.toLowerCase()
2548
2652
  );
2549
- if (typeof profile.address !== "string" || typeof profile.smartWallet !== "string" || !Array.isArray(profile.chains) || typeof profile.hasActiveSessionKey !== "boolean" || !Array.isArray(profile.protocols)) {
2550
- return invalid("profile", "missing required fields");
2653
+ return entry?.[1] ?? "0";
2654
+ }
2655
+ function position(value, asset) {
2656
+ const option = value?.yieldOption;
2657
+ if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress(option.address)) {
2658
+ return invalid("yield positions", "missing vault metadata");
2551
2659
  }
2660
+ const decimals = YIELDSEEKER_ASSET_METADATA[asset].decimals;
2552
2661
  return {
2553
- address: profile.address,
2554
- smartWallet: profile.smartWallet,
2555
- chains: profile.chains.map(Number),
2556
- hasActiveSessionKey: profile.hasActiveSessionKey,
2557
- protocols: profile.protocols.map(String)
2662
+ chain: "BASE",
2663
+ protocol: option.provider,
2664
+ protocolId: option.address,
2665
+ pool: option.name,
2666
+ asset,
2667
+ amount: decimal(value.assetsRaw, decimals, "yield positions"),
2668
+ amountRaw: String(value.assetsRaw),
2669
+ apy: percent(option.riskAdjustedApy),
2670
+ tvl: Number(option.totalDepositsUsd),
2671
+ liquidity: Number(option.withdrawableDepositsUsd)
2558
2672
  };
2559
2673
  }
2560
- function mapYieldseekerBalances(value) {
2561
- const balances = requireEnvelope(
2562
- value,
2563
- "balances",
2564
- "balances"
2565
- );
2566
- if (typeof balances.smartWallet !== "string" || typeof balances.totalBalance !== "string" || typeof balances.totalBalanceAsset !== "string" || !Array.isArray(balances.tokens) || !Array.isArray(balances.positions)) {
2567
- return invalid("balances", "missing required fields");
2674
+ function mapYieldseekerBalances(contexts) {
2675
+ const tokens = [];
2676
+ const positions = [];
2677
+ let totalUsd = 0;
2678
+ for (const context of contexts) {
2679
+ const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
2680
+ const idle = assetAddressValue(
2681
+ context.snapshot.tokenBalances,
2682
+ metadata.address
2683
+ );
2684
+ tokens.push({
2685
+ chain: "BASE",
2686
+ chainId: 8453,
2687
+ asset: context.asset,
2688
+ amount: decimal(idle, metadata.decimals, "snapshot")
2689
+ });
2690
+ positions.push(
2691
+ ...context.positions.map((entry) => position(entry, context.asset))
2692
+ );
2693
+ totalUsd += usd(
2694
+ raw(context.snapshot.totalValueBase, "snapshot"),
2695
+ context.snapshot.baseAssetDecimals,
2696
+ context.snapshot.baseAssetPriceUsd
2697
+ );
2568
2698
  }
2569
2699
  return {
2570
- smartWallet: balances.smartWallet,
2571
- totalBalance: balances.totalBalance,
2572
- totalBalanceAsset: balances.totalBalanceAsset.toLowerCase(),
2573
- tokens: balances.tokens.map(token),
2574
- positions: balances.positions.map(position)
2700
+ ...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
2701
+ totalBalance: String(totalUsd),
2702
+ totalBalanceAsset: "usdc",
2703
+ tokens,
2704
+ positions
2575
2705
  };
2576
2706
  }
2577
- function mapYieldseekerEarnings(value) {
2578
- const earnings = requireEnvelope(
2579
- value,
2580
- "earnings",
2581
- "earnings"
2582
- );
2583
- if (typeof earnings.smartWallet !== "string" || typeof earnings.lifetimeEarnings !== "number" || !Array.isArray(earnings.tokens)) {
2584
- return invalid("earnings", "missing required fields");
2707
+ function mapYieldseekerEarnings(contexts) {
2708
+ const tokens = [];
2709
+ let lifetimeEarnings = 0;
2710
+ for (const context of contexts) {
2711
+ const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
2712
+ tokens.push({
2713
+ chain: "BASE",
2714
+ chainId: 8453,
2715
+ asset: context.asset,
2716
+ amount: formatUnits(amount, context.snapshot.baseAssetDecimals)
2717
+ });
2718
+ lifetimeEarnings += usd(
2719
+ amount,
2720
+ context.snapshot.baseAssetDecimals,
2721
+ context.snapshot.baseAssetPriceUsd
2722
+ );
2585
2723
  }
2586
2724
  return {
2587
- smartWallet: earnings.smartWallet,
2588
- lifetimeEarnings: earnings.lifetimeEarnings,
2589
- tokens: earnings.tokens.map(token)
2725
+ smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2726
+ lifetimeEarnings,
2727
+ tokens
2590
2728
  };
2591
2729
  }
2592
- function mapYieldseekerApy(value) {
2593
- const apy = requireEnvelope(
2594
- value,
2595
- "apy",
2596
- "apy"
2597
- );
2598
- if (typeof apy.walletAddress !== "string" || typeof apy.weightedApyAfterFee !== "number" || !apy.apyByChainAndAsset || typeof apy.apyByChainAndAsset !== "object" || !Array.isArray(apy.history)) {
2599
- return invalid("apy", "missing required fields");
2730
+ function apyForDays(snapshot, days) {
2731
+ if (days === "7D") return percent(snapshot.apy7d);
2732
+ if (days === "30D") return percent(snapshot.apy30d);
2733
+ return percent((snapshot.apy7d + snapshot.apy30d) / 2);
2734
+ }
2735
+ function dailyApy(point) {
2736
+ const total = raw(point.totalValueBase, "historic position");
2737
+ const earned = raw(point.dailyYieldBase, "historic position");
2738
+ const principal = total - earned;
2739
+ if (principal <= 0n || earned === 0n) return 0;
2740
+ return Number(earned) / Number(principal) * 365 * 100;
2741
+ }
2742
+ function aggregateHistory(contexts, dayCount) {
2743
+ const byDate = /* @__PURE__ */ new Map();
2744
+ for (const context of contexts) {
2745
+ const points = context.historic?.dailyYieldSnapshots ?? [];
2746
+ for (const point of points.slice(-dayCount)) {
2747
+ const valueUsd = usd(
2748
+ raw(point.totalValueBase, "historic position"),
2749
+ point.baseAssetDecimals,
2750
+ point.baseAssetPriceUsd
2751
+ );
2752
+ const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
2753
+ current.weighted += dailyApy(point) * valueUsd;
2754
+ current.valueUsd += valueUsd;
2755
+ byDate.set(point.date, current);
2756
+ }
2600
2757
  }
2758
+ return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
2759
+ date,
2760
+ apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
2761
+ }));
2762
+ }
2763
+ function mapYieldseekerApy(walletAddress, contexts, days) {
2764
+ let weighted = 0;
2765
+ let totalUsd = 0;
2766
+ const byAsset = {};
2767
+ for (const context of contexts) {
2768
+ const valueUsd = usd(
2769
+ raw(context.snapshot.totalValueBase, "snapshot"),
2770
+ context.snapshot.baseAssetDecimals,
2771
+ context.snapshot.baseAssetPriceUsd
2772
+ );
2773
+ const apy = apyForDays(context.snapshot, days);
2774
+ weighted += apy * valueUsd;
2775
+ totalUsd += valueUsd;
2776
+ byAsset[context.asset] = apy;
2777
+ }
2778
+ const dayCount = Number(days.slice(0, -1));
2601
2779
  return {
2602
- walletAddress: apy.walletAddress,
2603
- weightedApyAfterFee: apy.weightedApyAfterFee,
2604
- apyByChainAndAsset: mapApyMatrix(apy.apyByChainAndAsset),
2605
- history: apy.history.map((point) => ({
2606
- date: String(point.date),
2607
- apy: Number(point.apy)
2608
- }))
2780
+ walletAddress,
2781
+ weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
2782
+ apyByChainAndAsset: { 8453: byAsset },
2783
+ history: aggregateHistory(contexts, dayCount)
2609
2784
  };
2610
2785
  }
2611
- function historyAction(value) {
2612
- const normalized = value.trim().toLowerCase();
2613
- if (normalized === "rebalance") return "Rebalance";
2614
- if (normalized === "deposit") return "Deposit";
2615
- if (normalized === "top up" || normalized === "topup") return "Top up";
2616
- if (normalized === "withdraw" || normalized === "withdrawal")
2617
- return "Withdraw";
2618
- if (normalized === "earned" || normalized === "earnings") return "Earned";
2619
- return invalid("history", `unsupported action ${value}`);
2786
+ function actionType(value) {
2787
+ const normalized = value.toLowerCase();
2788
+ if (normalized.includes("deposit")) return "Deposit";
2789
+ if (normalized.includes("withdraw")) return "Withdraw";
2790
+ if (normalized.includes("yield") || normalized.includes("earn"))
2791
+ return "Earned";
2792
+ return "Rebalance";
2793
+ }
2794
+ function transactionHashes(details) {
2795
+ if (!details) return [];
2796
+ const values = [
2797
+ details.transactionHash,
2798
+ details.txHash,
2799
+ ...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
2800
+ ...Array.isArray(details.txHashes) ? details.txHashes : []
2801
+ ];
2802
+ return values.filter(
2803
+ (value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
2804
+ ).filter((value, index, all) => all.indexOf(value) === index);
2620
2805
  }
2621
- function historyItem(value) {
2806
+ function actionEntry(action) {
2622
2807
  return {
2623
2808
  agent: "yieldseeker",
2624
- action: historyAction(value.action),
2625
- date: String(value.date),
2626
- oldApy: value.oldApy === null ? null : String(value.oldApy),
2627
- newApy: value.newApy === null ? null : String(value.newApy),
2628
- transactions: (value.transactions ?? []).map((transaction) => ({
2629
- txHashes: transaction.txHashes.map(String),
2630
- ...typeof transaction.chainId === "number" ? { chainId: transaction.chainId } : {},
2631
- ...typeof transaction.tokenSymbol === "string" ? { tokenSymbol: transaction.tokenSymbol } : {},
2632
- ...typeof transaction.amount === "string" ? { amount: transaction.amount } : {}
2633
- })),
2634
- rebalanceLog: (value.rebalanceLog ?? []).map((entry) => ({
2635
- fromProtocol: String(entry.fromProtocol),
2636
- toProtocol: String(entry.toProtocol),
2637
- ...entry.fromPool ? { fromPool: entry.fromPool } : {},
2638
- ...entry.toPool ? { toPool: entry.toPool } : {},
2639
- tokenSymbol: String(entry.tokenSymbol),
2640
- // Unlike Yieldseeker's other read-response amounts, live history
2641
- // rebalance amounts are returned in the token's smallest unit. Normalize
2642
- // them at the adapter boundary so every Owney consumer receives the
2643
- // shared human-readable decimal shape.
2644
- amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
2645
- status: entry.status
2646
- }))
2809
+ action: actionType(action.actionType),
2810
+ date: action.createdDate,
2811
+ oldApy: null,
2812
+ newApy: null,
2813
+ transactions: [
2814
+ {
2815
+ txHashes: transactionHashes(action.details),
2816
+ chainId: 8453
2817
+ }
2818
+ ],
2819
+ rebalanceLog: []
2647
2820
  };
2648
2821
  }
2649
- function mapYieldseekerHistory(value) {
2650
- const history = requireEnvelope(
2651
- value,
2652
- "history",
2653
- "history"
2654
- );
2655
- if (!Array.isArray(history.data) || typeof history.hasMore !== "boolean") {
2656
- return invalid("history", "missing required fields");
2822
+ function movementEntry(movement, wallet, agent, asset) {
2823
+ const isDeposit = movement.toAddress.toLowerCase() === wallet.walletAddress.toLowerCase();
2824
+ return {
2825
+ agent: "yieldseeker",
2826
+ action: isDeposit ? "Deposit" : "Withdraw",
2827
+ date: movement.blockDate,
2828
+ oldApy: null,
2829
+ newApy: null,
2830
+ transactions: [
2831
+ {
2832
+ txHashes: [movement.transactionHash],
2833
+ chainId: agent.chainId,
2834
+ tokenSymbol: asset,
2835
+ amount: decimal(
2836
+ movement.assetAmount,
2837
+ YIELDSEEKER_ASSET_METADATA[asset].decimals,
2838
+ "historic position"
2839
+ )
2840
+ }
2841
+ ],
2842
+ rebalanceLog: []
2843
+ };
2844
+ }
2845
+ function mapYieldseekerHistory(contexts, options) {
2846
+ const entries = contexts.flatMap((context) => [
2847
+ ...(context.actions ?? []).map(actionEntry),
2848
+ ...(context.historic?.movements ?? []).map(
2849
+ (movement) => movementEntry(movement, context.wallet, context.agent, context.asset)
2850
+ )
2851
+ ]);
2852
+ const filtered = entries.filter(
2853
+ (entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
2854
+ ).filter(
2855
+ (entry, index, all) => all.findIndex(
2856
+ (candidate) => candidate.action === entry.action && candidate.date === entry.date && candidate.transactions[0]?.txHashes[0] === entry.transactions[0]?.txHashes[0]
2857
+ ) === index
2858
+ ).sort((left, right) => right.date.localeCompare(left.date));
2859
+ return {
2860
+ data: filtered.slice(0, options.limit),
2861
+ // v1 returns the whole action/movement collection and defines no cursor.
2862
+ // Report a terminal page so callers never loop over the same prefix.
2863
+ hasMore: false
2864
+ };
2865
+ }
2866
+ function mapYieldseekerProfile(address, contexts) {
2867
+ const protocols = /* @__PURE__ */ new Set();
2868
+ for (const context of contexts) {
2869
+ for (const current of context.positions) {
2870
+ if (current.yieldOption?.provider) {
2871
+ protocols.add(String(current.yieldOption.provider));
2872
+ }
2873
+ }
2657
2874
  }
2658
2875
  return {
2659
- data: history.data.map(historyItem),
2660
- hasMore: history.hasMore,
2661
- ...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
2876
+ address,
2877
+ smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2878
+ chains: contexts.length > 0 ? [8453] : [],
2879
+ hasActiveSessionKey: contexts.some(
2880
+ (context) => context.wallet.initializedDate != null
2881
+ ),
2882
+ protocols: [...protocols]
2662
2883
  };
2663
2884
  }
2664
- function mapYieldseekerAgentApy(value) {
2665
- const agentApy = requireEnvelope(
2666
- value,
2667
- "agentApy",
2668
- "agent APY"
2669
- );
2670
- if (typeof agentApy.averageApy !== "number") {
2671
- return invalid("agent APY", "missing averageApy");
2885
+ function mapYieldseekerAgentApy(options, days) {
2886
+ const perAsset = {};
2887
+ const all = [];
2888
+ for (const entry of options) {
2889
+ const apys = entry.yieldOptions.map(
2890
+ (option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
2891
+ ).filter(Number.isFinite);
2892
+ if (apys.length === 0) continue;
2893
+ const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
2894
+ perAsset[entry.asset] = average;
2895
+ all.push(average);
2672
2896
  }
2673
2897
  return {
2674
- averageApy: agentApy.averageApy,
2675
- ...agentApy.detailedApys ? {
2676
- detailedApys: {
2677
- apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
2678
- }
2679
- } : {}
2898
+ averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
2899
+ detailedApys: { apyPerAsset: { 8453: perAsset } }
2680
2900
  };
2681
2901
  }
2682
2902
 
2683
2903
  // src/agents/yieldseeker/yieldseeker.agent.ts
2904
+ var OWNEY_AGENT_NAME = "owney";
2905
+ var YIELDSEEKER_AGENT_WALLET_ABI = [
2906
+ {
2907
+ type: "function",
2908
+ name: "withdrawAssetToUser",
2909
+ stateMutability: "nonpayable",
2910
+ inputs: [
2911
+ { name: "recipient", type: "address" },
2912
+ { name: "asset", type: "address" },
2913
+ { name: "amount", type: "uint256" }
2914
+ ],
2915
+ outputs: []
2916
+ },
2917
+ {
2918
+ type: "function",
2919
+ name: "withdrawAllAssetToUser",
2920
+ stateMutability: "nonpayable",
2921
+ inputs: [
2922
+ { name: "recipient", type: "address" },
2923
+ { name: "asset", type: "address" }
2924
+ ],
2925
+ outputs: []
2926
+ }
2927
+ ];
2684
2928
  function query(params) {
2685
2929
  const search = new URLSearchParams();
2686
2930
  for (const [key2, value] of Object.entries(params)) {
@@ -2698,7 +2942,7 @@ var YieldseekerAgent = class {
2698
2942
  chainId: 8453,
2699
2943
  chain: "BASE",
2700
2944
  assets: [
2701
- { symbol: "USDC", minDepositAmount: "1" },
2945
+ { symbol: "USDC", minDepositAmount: "10000000" },
2702
2946
  { symbol: "WETH", minDepositAmount: "1" }
2703
2947
  ]
2704
2948
  }
@@ -2707,7 +2951,9 @@ var YieldseekerAgent = class {
2707
2951
  auth;
2708
2952
  transactionExecutor;
2709
2953
  unwindReceiptWaiter;
2710
- activated = /* @__PURE__ */ new Set();
2954
+ agentContexts = /* @__PURE__ */ new Map();
2955
+ users = /* @__PURE__ */ new Map();
2956
+ pendingAgents = /* @__PURE__ */ new Map();
2711
2957
  constructor(owneyApiKey, options = {}) {
2712
2958
  this.api = new YieldseekerApiClient(
2713
2959
  owneyApiKey,
@@ -2720,14 +2966,19 @@ var YieldseekerAgent = class {
2720
2966
  }
2721
2967
  async disconnect() {
2722
2968
  this.auth.clear();
2723
- this.activated.clear();
2969
+ for (const key2 of this.users.keys()) {
2970
+ const [walletAddress, chainId] = key2.split(":");
2971
+ clearYieldseekerIdentity(walletAddress, Number(chainId));
2972
+ }
2973
+ this.users.clear();
2974
+ this.agentContexts.clear();
2975
+ this.pendingAgents.clear();
2724
2976
  }
2725
2977
  async activateAgent(state, chainId, asset) {
2726
2978
  this.assertChain(chainId);
2727
- await this.auth.getToken(state, chainId);
2728
- if (asset === "USDC" || asset === "WETH") {
2729
- await this.ensureActivated(state, chainId, asset);
2730
- }
2979
+ const targetAsset = asset ?? "USDC";
2980
+ this.assertAsset(targetAsset);
2981
+ await this.ensureAgent(state, chainId, targetAsset);
2731
2982
  }
2732
2983
  async deposit(state, chainId, amount, asset, depositCallback) {
2733
2984
  this.assertChain(chainId);
@@ -2740,42 +2991,48 @@ var YieldseekerAgent = class {
2740
2991
  this.id
2741
2992
  );
2742
2993
  }
2743
- await this.ensureActivated(state, chainId, asset);
2744
- const response = await this.walletRequest(
2745
- state,
2746
- chainId,
2747
- "/deposits",
2748
- { method: "POST", body: { chainId, asset, amount } }
2749
- );
2750
- const deposit = response?.deposit;
2751
- if (!deposit?.transaction || typeof deposit.amount !== "string" || deposit.amount !== amount || typeof deposit.smartWallet !== "string" || !isAddress(deposit.smartWallet)) {
2752
- throw this.invalidResponse("deposit");
2753
- }
2754
- if (depositCallback) {
2755
- provideDepositVerificationContext(depositCallback, {
2756
- agentId: "yieldseeker",
2757
- // Re-read after /deposits so an authentication retry cannot forward
2758
- // the stale token that Yieldseeker just rejected.
2759
- signature: await this.auth.getToken(state, chainId)
2760
- });
2761
- }
2762
- const txHash = depositCallback ? await depositCallback(deposit.smartWallet, chainId, amount) : await this.submitTransaction(state, chainId, deposit.transaction);
2763
- const confirmation = await this.walletRequest(
2764
- state,
2765
- chainId,
2766
- "/deposits/confirm",
2767
- { method: "POST", body: { chainId, asset } }
2768
- );
2769
- if (typeof confirmation?.confirmation?.autoseekQueued !== "boolean") {
2770
- throw this.invalidResponse("deposit confirmation", {
2771
- transactionHash: txHash,
2772
- transactionConfirmed: true
2773
- });
2994
+ const context = await this.ensureAgent(state, chainId, asset);
2995
+ let txHash;
2996
+ try {
2997
+ if (depositCallback) {
2998
+ provideDepositVerificationContext(depositCallback, {
2999
+ agentId: "yieldseeker",
3000
+ signature: await this.auth.getToken(state, chainId),
3001
+ userId: context.user.userId,
3002
+ yieldseekerAgentId: context.agent.agentId
3003
+ });
3004
+ txHash = await depositCallback(
3005
+ context.wallet.walletAddress,
3006
+ chainId,
3007
+ amount
3008
+ );
3009
+ await this.waitForReceipt(state, chainId, txHash);
3010
+ } else {
3011
+ txHash = await this.submitTransaction(state, chainId, {
3012
+ from: getAddress2(state.walletAddress),
3013
+ to: YIELDSEEKER_ASSET_METADATA[asset].address,
3014
+ data: encodeFunctionData({
3015
+ abi: erc20Abi,
3016
+ functionName: "transfer",
3017
+ args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
3018
+ }),
3019
+ value: "0",
3020
+ chainId
3021
+ });
3022
+ }
3023
+ await this.deployAfterFunding(state, chainId, context);
3024
+ } finally {
3025
+ await this.refreshSnapshotAfterMovement(
3026
+ state,
3027
+ chainId,
3028
+ context,
3029
+ "deposit"
3030
+ );
2774
3031
  }
2775
3032
  return {
2776
3033
  txHash,
2777
- smartWallet: deposit.smartWallet,
2778
- amount: deposit.amount
3034
+ smartWallet: context.wallet.walletAddress,
3035
+ amount
2779
3036
  };
2780
3037
  }
2781
3038
  async withdraw(state, chainId, asset, amount) {
@@ -2789,198 +3046,454 @@ var YieldseekerAgent = class {
2789
3046
  this.id
2790
3047
  );
2791
3048
  }
2792
- const balances = await this.getBalances(state, chainId);
2793
- const decimals = asset === "USDC" ? 6 : 18;
2794
- const targetAsset = asset.toUpperCase();
2795
- const idle = balances.tokens.filter(
2796
- (token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
2797
- ).reduce((total, token2) => total + parseUnits(token2.amount, decimals), 0n);
2798
- const positions = (balances.positions ?? []).filter(
2799
- (position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
2800
- ).map((position2) => ({
2801
- position: position2,
2802
- amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : parseUnits(position2.amount, decimals)
2803
- }));
2804
- const deployed = positions.reduce(
2805
- (total, position2) => total + position2.amount,
2806
- 0n
2807
- );
2808
- const totalAvailable = idle + deployed;
2809
- const requested = amount === void 0 ? totalAvailable : BigInt(amount);
2810
- if (requested > totalAvailable) {
3049
+ const context = await this.findAgent(state, chainId, asset);
3050
+ if (!context) {
2811
3051
  throw new OwneyError(
2812
3052
  "WITHDRAW_INSUFFICIENT_BALANCE",
2813
- `Requested withdrawal "${requested.toString()}" exceeds available Yieldseeker balance "${totalAvailable.toString()}" for asset "${asset}".`,
2814
- {
2815
- asset,
2816
- requested: requested.toString(),
2817
- available: totalAvailable.toString()
2818
- },
3053
+ `No Yieldseeker ${asset} agent exists for this wallet.`,
3054
+ { asset, available: "0" },
2819
3055
  this.id
2820
3056
  );
2821
3057
  }
2822
- const requiredFromPositions = requested > idle ? requested - idle : 0n;
2823
- const unwindPlans = [];
2824
- let remainingToUnwind = requiredFromPositions;
2825
- for (const { position: position2, amount: positionAmount } of positions) {
2826
- if (remainingToUnwind === 0n) break;
2827
- if (positionAmount <= 0n) continue;
2828
- if (!position2.protocolId || !isAddress(position2.protocolId)) {
2829
- throw this.invalidResponse("position", {
2830
- reason: "A deployed position is missing its vault address.",
2831
- protocol: position2.protocol,
2832
- pool: position2.pool
3058
+ try {
3059
+ const portfolio = await this.loadPortfolioContext(
3060
+ state,
3061
+ chainId,
3062
+ context
3063
+ );
3064
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3065
+ const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
3066
+ ([address]) => address.toLowerCase() === metadata.address.toLowerCase()
3067
+ );
3068
+ const idle = BigInt(idleEntry?.[1] ?? "0");
3069
+ const deployed = portfolio.positions.reduce(
3070
+ (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
3071
+ 0n
3072
+ );
3073
+ const totalAvailable = idle + deployed;
3074
+ const requested = amount === void 0 ? totalAvailable : BigInt(amount);
3075
+ if (requested > totalAvailable) {
3076
+ throw new OwneyError(
3077
+ "WITHDRAW_INSUFFICIENT_BALANCE",
3078
+ `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
3079
+ {
3080
+ asset,
3081
+ requested: requested.toString(),
3082
+ available: totalAvailable.toString()
3083
+ },
3084
+ this.id
3085
+ );
3086
+ }
3087
+ let remaining = requested > idle ? requested - idle : 0n;
3088
+ for (const position2 of portfolio.positions) {
3089
+ if (remaining === 0n) break;
3090
+ const available = BigInt(position2.withdrawableAssetsRaw);
3091
+ if (available <= 0n) continue;
3092
+ const assetsRaw = available < remaining ? available : remaining;
3093
+ const response = await this.walletRequest(
3094
+ state,
3095
+ chainId,
3096
+ this.agentPath(context, "withdraw-from-position"),
3097
+ {
3098
+ method: "POST",
3099
+ body: {
3100
+ chainId,
3101
+ vaultAddress: position2.yieldOption.address,
3102
+ assetsRaw: assetsRaw.toString()
3103
+ }
3104
+ }
3105
+ );
3106
+ if (!this.isTransactionHash(response?.transactionHash)) {
3107
+ throw this.invalidResponse("position withdrawal");
3108
+ }
3109
+ await this.waitForReceipt(state, chainId, response.transactionHash);
3110
+ remaining -= assetsRaw;
3111
+ }
3112
+ if (remaining > 0n) {
3113
+ throw this.invalidResponse("yield positions", {
3114
+ reason: "Withdrawable positions could not cover the request.",
3115
+ remaining: remaining.toString()
2833
3116
  });
2834
3117
  }
2835
- const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
2836
- unwindPlans.push({
2837
- vaultAddress: getAddress2(position2.protocolId),
2838
- amount: unwindAmount
3118
+ const account = getAddress2(state.walletAddress);
3119
+ const txHash = await this.submitTransaction(state, chainId, {
3120
+ from: account,
3121
+ to: getAddress2(context.wallet.walletAddress),
3122
+ data: amount === void 0 ? encodeFunctionData({
3123
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3124
+ functionName: "withdrawAllAssetToUser",
3125
+ args: [account, metadata.address]
3126
+ }) : encodeFunctionData({
3127
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3128
+ functionName: "withdrawAssetToUser",
3129
+ args: [account, metadata.address, requested]
3130
+ }),
3131
+ value: "0",
3132
+ chainId
2839
3133
  });
2840
- remainingToUnwind -= unwindAmount;
3134
+ return {
3135
+ txHash,
3136
+ type: amount === void 0 ? "full" : "partial",
3137
+ amount: requested.toString()
3138
+ };
3139
+ } finally {
3140
+ await this.refreshSnapshotAfterMovement(
3141
+ state,
3142
+ chainId,
3143
+ context,
3144
+ "withdrawal"
3145
+ );
2841
3146
  }
2842
- if (remainingToUnwind > 0n) {
2843
- throw this.invalidResponse("balances", {
2844
- reason: "Deployed position balances could not cover the unwind.",
2845
- required: requiredFromPositions.toString(),
2846
- planned: (requiredFromPositions - remainingToUnwind).toString()
2847
- });
3147
+ }
3148
+ async getBalances(state, chainId) {
3149
+ this.assertChain(chainId);
3150
+ return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
3151
+ }
3152
+ async getEarnings(state, chainId) {
3153
+ this.assertChain(chainId);
3154
+ return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
3155
+ }
3156
+ async getAccountApy(state, chainId, days, tokenSymbol) {
3157
+ this.assertChain(chainId);
3158
+ const asset = tokenSymbol?.toUpperCase();
3159
+ if (asset !== void 0) this.assertAsset(asset);
3160
+ const contexts = await this.loadPortfolio(state, chainId, {
3161
+ ...asset ? { asset } : {},
3162
+ historic: true
3163
+ });
3164
+ return mapYieldseekerApy(state.walletAddress, contexts, days);
3165
+ }
3166
+ async getHistory(state, chainId, options) {
3167
+ this.assertChain(chainId);
3168
+ const asset = options?.tokenSymbol?.toUpperCase();
3169
+ if (asset !== void 0) this.assertAsset(asset);
3170
+ const contexts = await this.loadPortfolio(state, chainId, {
3171
+ ...asset ? { asset } : {},
3172
+ historic: true,
3173
+ actions: true
3174
+ });
3175
+ return mapYieldseekerHistory(contexts, {
3176
+ limit: options?.limit ?? 10,
3177
+ ...options?.fromDate ? { fromDate: options.fromDate } : {},
3178
+ ...options?.toDate ? { toDate: options.toDate } : {}
3179
+ });
3180
+ }
3181
+ async getUserProfile(state, chainId) {
3182
+ this.assertChain(chainId);
3183
+ return mapYieldseekerProfile(
3184
+ state.walletAddress,
3185
+ await this.loadPortfolio(state, chainId, {})
3186
+ );
3187
+ }
3188
+ async getAgentApy(days, options) {
3189
+ this.assertOptionalChain(options?.chainId);
3190
+ const requested = options?.tokenSymbol?.toUpperCase();
3191
+ if (requested !== void 0) this.assertAsset(requested);
3192
+ const assets = requested ? [requested] : ["USDC", "WETH"];
3193
+ const values = await Promise.all(
3194
+ assets.map(async (asset) => {
3195
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3196
+ const response = await this.api.request(
3197
+ `/chains/8453/assets/${metadata.address}/yield-options`
3198
+ );
3199
+ if (!Array.isArray(response?.yieldOptions)) {
3200
+ throw this.invalidResponse("yield options");
3201
+ }
3202
+ return { asset, yieldOptions: response.yieldOptions };
3203
+ })
3204
+ );
3205
+ return mapYieldseekerAgentApy(values, days);
3206
+ }
3207
+ userKey(state, chainId) {
3208
+ return `${state.walletAddress.toLowerCase()}:${chainId}`;
3209
+ }
3210
+ contextKey(state, chainId, asset) {
3211
+ return `${this.userKey(state, chainId)}:${asset}`;
3212
+ }
3213
+ async resolveUser(state, chainId) {
3214
+ const key2 = this.userKey(state, chainId);
3215
+ const inMemory = this.users.get(key2);
3216
+ if (inMemory) return inMemory;
3217
+ const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
3218
+ if (persisted) {
3219
+ this.users.set(key2, persisted);
3220
+ return persisted;
3221
+ }
3222
+ const walletAddress = getAddress2(state.walletAddress);
3223
+ let user = null;
3224
+ try {
3225
+ const login = await this.providerRequest(
3226
+ state,
3227
+ chainId,
3228
+ "/users/login-with-wallet",
3229
+ { method: "POST", body: { walletAddress } }
3230
+ );
3231
+ user = login?.user ?? null;
3232
+ if (!user) {
3233
+ throw this.invalidResponse("wallet login", {
3234
+ reason: "A successful login returned no user."
3235
+ });
3236
+ }
3237
+ } catch (error) {
3238
+ if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
3239
+ if (error instanceof OwneyError) throw error;
3240
+ throw this.mapApiError(error);
3241
+ }
3242
+ let created;
3243
+ try {
3244
+ created = await this.providerRequest(
3245
+ state,
3246
+ chainId,
3247
+ "/users",
3248
+ {
3249
+ method: "POST",
3250
+ body: {
3251
+ walletAddress,
3252
+ username: `owney-${walletAddress.slice(2).toLowerCase()}`
3253
+ }
3254
+ }
3255
+ );
3256
+ } catch (createError) {
3257
+ throw this.mapApiError(createError);
3258
+ }
3259
+ user = created?.user ?? null;
3260
+ }
3261
+ if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
3262
+ throw this.invalidResponse("wallet identity");
3263
+ }
3264
+ const resolved = { userId: user.userId };
3265
+ this.users.set(key2, resolved);
3266
+ writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
3267
+ return resolved;
3268
+ }
3269
+ forgetUser(state, chainId) {
3270
+ this.users.delete(this.userKey(state, chainId));
3271
+ clearYieldseekerIdentity(state.walletAddress, chainId);
3272
+ }
3273
+ async ensureAgent(state, chainId, asset) {
3274
+ const key2 = this.contextKey(state, chainId, asset);
3275
+ const cached = this.agentContexts.get(key2);
3276
+ if (cached) return cached;
3277
+ const pending = this.pendingAgents.get(key2);
3278
+ if (pending) return pending;
3279
+ const request = this.resolveAgent(state, chainId, asset, true).then(
3280
+ (context) => {
3281
+ if (!context) throw this.invalidResponse("agent creation");
3282
+ this.agentContexts.set(key2, context);
3283
+ return context;
3284
+ }
3285
+ );
3286
+ this.pendingAgents.set(key2, request);
3287
+ try {
3288
+ return await request;
3289
+ } finally {
3290
+ this.pendingAgents.delete(key2);
2848
3291
  }
2849
- for (const plan of unwindPlans) {
2850
- const response2 = await this.walletRequest(
3292
+ }
3293
+ async findAgent(state, chainId, asset) {
3294
+ const key2 = this.contextKey(state, chainId, asset);
3295
+ const cached = this.agentContexts.get(key2);
3296
+ if (cached) return cached;
3297
+ const context = await this.resolveAgent(state, chainId, asset, false);
3298
+ if (context) this.agentContexts.set(key2, context);
3299
+ return context;
3300
+ }
3301
+ async resolveAgent(state, chainId, asset, createIfMissing) {
3302
+ const user = await this.resolveUser(state, chainId);
3303
+ const response = await this.walletRequest(
3304
+ state,
3305
+ chainId,
3306
+ `/users/${user.userId}/agents`
3307
+ );
3308
+ if (!Array.isArray(response?.agents)) {
3309
+ throw this.invalidResponse("agent list");
3310
+ }
3311
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3312
+ let agent = response.agents.find(
3313
+ (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3314
+ );
3315
+ if (!agent && createIfMissing) {
3316
+ const created = await this.walletRequest(
2851
3317
  state,
2852
3318
  chainId,
2853
- "/positions/unwind",
3319
+ `/users/${user.userId}/agents`,
2854
3320
  {
2855
3321
  method: "POST",
2856
3322
  body: {
3323
+ name: OWNEY_AGENT_NAME,
3324
+ emoji: "\u{1F989}",
2857
3325
  chainId,
2858
- asset,
2859
- vaultAddress: plan.vaultAddress,
2860
- amount: plan.amount.toString()
3326
+ assetAddress: metadata.address,
3327
+ type: "vault",
3328
+ rulePreset: null
2861
3329
  }
2862
3330
  }
2863
3331
  );
2864
- const transactionHash = response2?.unwind?.transactionHash;
2865
- if (!this.isTransactionHash(transactionHash)) {
2866
- throw this.invalidResponse("position unwind");
2867
- }
2868
- await this.waitForUnwindReceipt(state, chainId, transactionHash);
3332
+ agent = created?.agent;
2869
3333
  }
2870
- const response = await this.walletRequest(
3334
+ if (!agent) return null;
3335
+ this.assertAgent(agent);
3336
+ const walletResponse = await this.walletRequest(
2871
3337
  state,
2872
3338
  chainId,
2873
- "/withdrawals",
2874
- {
2875
- method: "POST",
2876
- body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
2877
- }
3339
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
2878
3340
  );
2879
- const withdrawal = response?.withdrawal;
2880
- if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
2881
- throw this.invalidResponse("withdrawal");
3341
+ if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
3342
+ throw this.invalidResponse("agent wallet");
2882
3343
  }
2883
- const txHash = await this.submitTransaction(
3344
+ return { user, agent, wallet: walletResponse.agentWallet, asset };
3345
+ }
3346
+ async loadPortfolio(state, chainId, options) {
3347
+ const user = await this.resolveUser(state, chainId);
3348
+ const response = await this.walletRequest(
2884
3349
  state,
2885
3350
  chainId,
2886
- withdrawal.transaction
3351
+ `/users/${user.userId}/agents`
2887
3352
  );
2888
- return {
2889
- txHash,
2890
- type: amount === void 0 ? "full" : "partial",
2891
- amount: withdrawal.amount
2892
- };
2893
- }
2894
- async getBalances(state, chainId) {
2895
- return mapYieldseekerBalances(
2896
- await this.walletRequest(
3353
+ if (!Array.isArray(response?.agents)) {
3354
+ throw this.invalidResponse("agent list");
3355
+ }
3356
+ const contexts = [];
3357
+ for (const agent of response.agents) {
3358
+ const asset = this.assetForAgent(agent);
3359
+ if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3360
+ continue;
3361
+ }
3362
+ this.assertAgent(agent);
3363
+ const walletResponse = await this.walletRequest(
2897
3364
  state,
2898
3365
  chainId,
2899
- `/balances${query({ chainId })}`
3366
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
3367
+ );
3368
+ if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
3369
+ throw this.invalidResponse("agent wallet");
3370
+ }
3371
+ const context = {
3372
+ user,
3373
+ agent,
3374
+ wallet: walletResponse.agentWallet,
3375
+ asset
3376
+ };
3377
+ this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3378
+ contexts.push(context);
3379
+ }
3380
+ return Promise.all(
3381
+ contexts.map(
3382
+ (context) => this.loadPortfolioContext(state, chainId, context, options)
2900
3383
  )
2901
3384
  );
2902
3385
  }
2903
- async getEarnings(state, chainId) {
2904
- return mapYieldseekerEarnings(
2905
- await this.walletRequest(
3386
+ async loadPortfolioContext(state, chainId, context, options = {}) {
3387
+ const [snapshot, positions, historic, actions] = await Promise.all([
3388
+ this.walletRequest(
2906
3389
  state,
2907
3390
  chainId,
2908
- `/earnings${query({ chainId })}`
2909
- )
2910
- );
3391
+ `${this.agentPath(context, "snapshot")}${query({
3392
+ shouldOnlyUseRecentValue: true,
3393
+ shouldAllowStaleOnError: true
3394
+ })}`
3395
+ ),
3396
+ this.walletRequest(
3397
+ state,
3398
+ chainId,
3399
+ this.agentPath(context, "yield-positions")
3400
+ ),
3401
+ options.historic ? this.walletRequest(
3402
+ state,
3403
+ chainId,
3404
+ this.agentPath(context, "wallet/historic-position")
3405
+ ) : Promise.resolve(void 0),
3406
+ options.actions ? this.walletRequest(
3407
+ state,
3408
+ chainId,
3409
+ this.agentPath(context, "actions")
3410
+ ) : Promise.resolve(void 0)
3411
+ ]);
3412
+ if (!snapshot?.agentSnapshot) {
3413
+ throw this.invalidResponse("agent snapshot");
3414
+ }
3415
+ if (!Array.isArray(positions?.yieldPositions)) {
3416
+ throw this.invalidResponse("yield positions");
3417
+ }
3418
+ return {
3419
+ ...context,
3420
+ snapshot: snapshot.agentSnapshot,
3421
+ positions: positions.yieldPositions,
3422
+ ...historic?.position ? { historic: historic.position } : {},
3423
+ ...actions?.actions ? { actions: actions.actions } : {}
3424
+ };
2911
3425
  }
2912
- async getAccountApy(state, chainId, days, tokenSymbol) {
2913
- return mapYieldseekerApy(
2914
- await this.walletRequest(
3426
+ async deployAfterFunding(state, chainId, context) {
3427
+ if (context.wallet.initializedDate != null) return;
3428
+ try {
3429
+ const deployed = await this.walletRequest(
2915
3430
  state,
2916
3431
  chainId,
2917
- `/apy${query({ chainId, days, tokenSymbol })}`
2918
- )
2919
- );
3432
+ this.agentPath(context, "deploy"),
3433
+ { method: "POST", body: {} }
3434
+ );
3435
+ if (deployed?.agentWallet) {
3436
+ context.wallet = deployed.agentWallet;
3437
+ }
3438
+ } catch (error) {
3439
+ console.warn(
3440
+ "[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
3441
+ error
3442
+ );
3443
+ }
2920
3444
  }
2921
- async getHistory(state, chainId, options) {
2922
- return mapYieldseekerHistory(
2923
- await this.walletRequest(
3445
+ async refreshSnapshotAfterMovement(state, chainId, context, movement) {
3446
+ try {
3447
+ const response = await this.walletRequest(
2924
3448
  state,
2925
3449
  chainId,
2926
- `/history${query({
2927
- chainId,
2928
- limit: options?.limit ?? 10,
2929
- cursor: options?.cursor,
2930
- fromDate: options?.fromDate,
2931
- toDate: options?.toDate
3450
+ `${this.agentPath(context, "snapshot")}${query({
3451
+ shouldForceRefresh: true
2932
3452
  })}`
2933
- )
2934
- );
3453
+ );
3454
+ if (!response?.agentSnapshot) {
3455
+ throw this.invalidResponse("agent snapshot refresh");
3456
+ }
3457
+ } catch (error) {
3458
+ console.warn(
3459
+ `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3460
+ error
3461
+ );
3462
+ }
2935
3463
  }
2936
- async getUserProfile(state, chainId) {
2937
- return mapYieldseekerProfile(
2938
- await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
2939
- );
3464
+ agentPath(context, suffix) {
3465
+ return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
2940
3466
  }
2941
- async getAgentApy(days, options) {
2942
- this.assertOptionalChain(options?.chainId);
2943
- return mapYieldseekerAgentApy(
2944
- await this.api.request(
2945
- `/agent/apy${query({
2946
- days,
2947
- tokenSymbol: options?.tokenSymbol,
2948
- chainId: options?.chainId
2949
- })}`
2950
- )
2951
- );
2952
- }
2953
- async ensureActivated(state, chainId, asset) {
2954
- const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
2955
- if (this.activated.has(key2)) return;
2956
- const response = await this.walletRequest(
2957
- state,
2958
- chainId,
2959
- "/activation",
2960
- { method: "POST", body: { chainId, asset } }
2961
- );
2962
- if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
2963
- throw this.invalidResponse("activation");
3467
+ async walletRequest(state, chainId, path, options = {}) {
3468
+ try {
3469
+ return await this.providerRequest(state, chainId, path, options);
3470
+ } catch (error) {
3471
+ throw this.mapApiError(error);
2964
3472
  }
2965
- this.activated.add(key2);
2966
3473
  }
2967
- async walletRequest(state, chainId, path, options = {}) {
3474
+ async providerRequest(state, chainId, path, options = {}) {
2968
3475
  this.assertChain(chainId);
3476
+ const request = (signature2) => this.api.request(path, {
3477
+ ...options,
3478
+ signature: signature2
3479
+ });
2969
3480
  let signature = await this.auth.getToken(state, chainId);
2970
3481
  try {
2971
- return await this.api.request(path, { ...options, signature });
3482
+ return await request(signature);
2972
3483
  } catch (error) {
2973
3484
  if (!(error instanceof YieldseekerApiError)) throw error;
2974
- if (error.isAuthenticationError) {
2975
- this.auth.clear(state, chainId);
2976
- signature = await this.auth.getToken(state, chainId);
2977
- try {
2978
- return await this.api.request(path, { ...options, signature });
2979
- } catch (retryError) {
2980
- throw this.mapApiError(retryError);
3485
+ if (error.providerCode === "NO_USER") throw error;
3486
+ if (!error.isAuthenticationError) throw error;
3487
+ this.auth.clear(state, chainId);
3488
+ signature = await this.auth.getToken(state, chainId);
3489
+ try {
3490
+ return await request(signature);
3491
+ } catch (retryError) {
3492
+ if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
3493
+ this.forgetUser(state, chainId);
2981
3494
  }
3495
+ throw retryError;
2982
3496
  }
2983
- throw this.mapApiError(error);
2984
3497
  }
2985
3498
  }
2986
3499
  mapApiError(error) {
@@ -3008,15 +3521,8 @@ var YieldseekerAgent = class {
3008
3521
  if (this.transactionExecutor) {
3009
3522
  return this.transactionExecutor(state, chainId, transaction);
3010
3523
  }
3011
- if (typeof transaction.from !== "string" || typeof transaction.to !== "string" || typeof transaction.data !== "string" || typeof transaction.value !== "string" || transaction.chainId !== chainId) {
3012
- throw this.invalidResponse("transaction");
3013
- }
3524
+ this.assertTransaction(transaction, state, chainId);
3014
3525
  const account = getAddress2(state.walletAddress);
3015
- if (getAddress2(transaction.from) !== account) {
3016
- throw this.invalidResponse("transaction", {
3017
- reason: "transaction.from does not match the connected wallet"
3018
- });
3019
- }
3020
3526
  const walletClient = createWalletClient2({
3021
3527
  account,
3022
3528
  chain: base3,
@@ -3052,7 +3558,7 @@ var YieldseekerAgent = class {
3052
3558
  }
3053
3559
  return hash;
3054
3560
  }
3055
- async waitForUnwindReceipt(state, chainId, transactionHash) {
3561
+ async waitForReceipt(state, chainId, transactionHash) {
3056
3562
  if (this.unwindReceiptWaiter) {
3057
3563
  await this.unwindReceiptWaiter(state, chainId, transactionHash);
3058
3564
  return;
@@ -3068,15 +3574,32 @@ var YieldseekerAgent = class {
3068
3574
  if (receipt.status !== "success") {
3069
3575
  throw new OwneyError(
3070
3576
  "AGENT_TRANSACTION_REVERTED",
3071
- `Yieldseeker position unwind reverted (${transactionHash}).`,
3577
+ `Yieldseeker transaction reverted (${transactionHash}).`,
3072
3578
  { transactionHash },
3073
3579
  this.id
3074
3580
  );
3075
3581
  }
3076
3582
  }
3077
- positionMatchesChain(chain, chainId) {
3078
- const normalized = chain.trim().toUpperCase();
3079
- return normalized === String(chainId) || normalized === "BASE";
3583
+ assertTransaction(transaction, state, chainId) {
3584
+ if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(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 || getAddress2(transaction.from) !== getAddress2(state.walletAddress)) {
3585
+ throw this.invalidResponse("transaction");
3586
+ }
3587
+ }
3588
+ assertAgent(agent) {
3589
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
3590
+ throw this.invalidResponse("agent");
3591
+ }
3592
+ }
3593
+ isOwneyAgent(agent) {
3594
+ return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
3595
+ }
3596
+ assetForAgent(agent) {
3597
+ for (const asset of ["USDC", "WETH"]) {
3598
+ if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
3599
+ return asset;
3600
+ }
3601
+ }
3602
+ return null;
3080
3603
  }
3081
3604
  isTransactionHash(value) {
3082
3605
  return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
@@ -3098,8 +3621,12 @@ var YieldseekerAgent = class {
3098
3621
  if (asset !== "USDC" && asset !== "WETH") {
3099
3622
  throw new OwneyError(
3100
3623
  "ASSET_UNSUPPORTED",
3101
- `Yieldseeker does not support asset ${asset}.`,
3102
- { asset, supportedAssets: ["USDC", "WETH"] },
3624
+ `Yieldseeker does not support asset ${asset} in the Owney rollout.`,
3625
+ {
3626
+ asset,
3627
+ supportedAssets: ["USDC", "WETH"],
3628
+ providerAlsoAdvertises: ["cbBTC"]
3629
+ },
3103
3630
  this.id
3104
3631
  );
3105
3632
  }
@@ -3212,7 +3739,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
3212
3739
  }
3213
3740
 
3214
3741
  // src/lib/helpers/withdraw-helper.ts
3215
- import { parseUnits as parseUnits2 } from "viem";
3742
+ import { parseUnits } from "viem";
3216
3743
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
3217
3744
  const target = asset.toUpperCase();
3218
3745
  return agents.map((agent) => {
@@ -3220,7 +3747,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
3220
3747
  const tokenBalance = agentBalance?.tokens.find(
3221
3748
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
3222
3749
  );
3223
- let balance = tokenBalance ? parseUnits2(tokenBalance.amount, decimals) : 0n;
3750
+ let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
3224
3751
  if (agent.balanceComposition === "tokens-plus-positions") {
3225
3752
  const chainNameById = {
3226
3753
  1: "ETHEREUM",
@@ -3239,7 +3766,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
3239
3766
  } catch {
3240
3767
  }
3241
3768
  }
3242
- balance += parseUnits2(position2.amount, decimals);
3769
+ balance += parseUnits(position2.amount, decimals);
3243
3770
  }
3244
3771
  }
3245
3772
  return { agent, balance };
@@ -3321,11 +3848,11 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
3321
3848
  return Number.isFinite(total) && total > 0 ? total : 0;
3322
3849
  }
3323
3850
  const normalizedToken = tokenSymbol.toUpperCase();
3324
- return balance.tokens.reduce((total, token2) => {
3325
- if (Number(token2.chainId) !== chainId || String(token2.asset).toUpperCase() !== normalizedToken) {
3851
+ return balance.tokens.reduce((total, token) => {
3852
+ if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
3326
3853
  return total;
3327
3854
  }
3328
- const amount = Number(token2.amount);
3855
+ const amount = Number(token.amount);
3329
3856
  return Number.isFinite(amount) && amount > 0 ? total + amount : total;
3330
3857
  }, 0);
3331
3858
  }
@@ -3399,8 +3926,8 @@ function makeSponsoredWethCallback(deps) {
3399
3926
  return makeVerificationAwareDepositCallback(
3400
3927
  async (smartWallet, chainId, amount, verification) => {
3401
3928
  const cid = chainId;
3402
- const token2 = deps.tokenAddressByChain[cid];
3403
- if (!token2) {
3929
+ const token = deps.tokenAddressByChain[cid];
3930
+ if (!token) {
3404
3931
  throw new OwneyError(
3405
3932
  "CHAIN_UNSUPPORTED",
3406
3933
  `No sponsored WETH configured for chain ${chainId}`
@@ -3411,12 +3938,12 @@ function makeSponsoredWethCallback(deps) {
3411
3938
  const wallet = deps.getWalletClient(cid);
3412
3939
  await ensureWalletOnChain(pub, wallet, cid);
3413
3940
  try {
3414
- const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
3941
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3415
3942
  if (balance < amountWei) {
3416
3943
  throw new OwneyError(
3417
3944
  "DEPOSIT_INSUFFICIENT_BALANCE",
3418
3945
  "Insufficient WETH balance for this deposit.",
3419
- { token: token2, chainId: cid, balance: balance.toString(), amount }
3946
+ { token, chainId: cid, balance: balance.toString(), amount }
3420
3947
  );
3421
3948
  }
3422
3949
  } catch (err) {
@@ -3428,14 +3955,14 @@ function makeSponsoredWethCallback(deps) {
3428
3955
  }
3429
3956
  const allowance = await readPermit2Allowance(
3430
3957
  pub,
3431
- token2,
3958
+ token,
3432
3959
  deps.ownerAddress
3433
3960
  );
3434
3961
  if (allowance < amountWei) {
3435
3962
  throw new OwneyError(
3436
3963
  "PERMIT2_APPROVAL_REQUIRED",
3437
3964
  "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3438
- { token: token2, chainId: cid, allowance: allowance.toString(), amount }
3965
+ { token, chainId: cid, allowance: allowance.toString(), amount }
3439
3966
  );
3440
3967
  }
3441
3968
  const relayer = await get({
@@ -3450,7 +3977,7 @@ function makeSponsoredWethCallback(deps) {
3450
3977
  const typedData = buildPermitTransferFromTypedData({
3451
3978
  chainId: cid,
3452
3979
  message: {
3453
- permitted: { token: token2, amount: amountWei },
3980
+ permitted: { token, amount: amountWei },
3454
3981
  spender: relayer,
3455
3982
  nonce,
3456
3983
  deadline
@@ -3467,13 +3994,17 @@ function makeSponsoredWethCallback(deps) {
3467
3994
  ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
3468
3995
  body: {
3469
3996
  chainId: cid,
3470
- token: token2,
3997
+ token,
3471
3998
  from: deps.ownerAddress,
3472
3999
  to: smartWallet,
3473
4000
  amount,
3474
4001
  nonce: nonce.toString(),
3475
4002
  deadline: deadline.toString(),
3476
- signature
4003
+ signature,
4004
+ ...verification?.agentId === "yieldseeker" ? {
4005
+ yieldseekerUserId: verification.userId,
4006
+ yieldseekerAgentId: verification.yieldseekerAgentId
4007
+ } : {}
3477
4008
  }
3478
4009
  });
3479
4010
  return result.txHash;
@@ -3482,7 +4013,7 @@ function makeSponsoredWethCallback(deps) {
3482
4013
  }
3483
4014
 
3484
4015
  // src/lib/sponsored-calls-deposit.ts
3485
- import { encodeFunctionData, erc20Abi, toHex } from "viem";
4016
+ import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi2, toHex } from "viem";
3486
4017
  var DEFAULT_POLL_INTERVAL_MS = 1500;
3487
4018
  var DEFAULT_MAX_POLLS = 30;
3488
4019
  async function paymasterSupported(provider, owner, chainId) {
@@ -3508,10 +4039,10 @@ function makeSponsoredCallsCallback(deps) {
3508
4039
  }
3509
4040
  return new URL(configured, origin).toString();
3510
4041
  };
3511
- return async (smartWallet, chainId, amount) => {
4042
+ return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
3512
4043
  const cid = chainId;
3513
- const token2 = deps.tokenAddressByChain[cid];
3514
- if (!token2) {
4044
+ const token = deps.tokenAddressByChain[cid];
4045
+ if (!token) {
3515
4046
  throw new OwneyError(
3516
4047
  "CHAIN_UNSUPPORTED",
3517
4048
  `No sponsored token configured for chain ${chainId}`
@@ -3524,11 +4055,37 @@ function makeSponsoredCallsCallback(deps) {
3524
4055
  { chainId }
3525
4056
  );
3526
4057
  }
3527
- const data = encodeFunctionData({
3528
- abi: erc20Abi,
4058
+ const data = encodeFunctionData2({
4059
+ abi: erc20Abi2,
3529
4060
  functionName: "transfer",
3530
4061
  args: [smartWallet, BigInt(amount)]
3531
4062
  });
4063
+ let paymasterUrl = absolutePaymasterUrl();
4064
+ if (verification?.agentId === "yieldseeker") {
4065
+ if (chainId !== 8453) {
4066
+ throw new OwneyError(
4067
+ "CHAIN_UNSUPPORTED",
4068
+ `Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
4069
+ );
4070
+ }
4071
+ const { intent } = await postPaymasterIntent({
4072
+ baseUrl: deps.routingApiBaseUrl,
4073
+ apiKey: deps.apiKey,
4074
+ yieldseekerSignature: verification.signature,
4075
+ body: {
4076
+ chainId,
4077
+ token,
4078
+ from: deps.ownerAddress,
4079
+ to: smartWallet,
4080
+ amount,
4081
+ yieldseekerUserId: verification.userId,
4082
+ yieldseekerAgentId: verification.yieldseekerAgentId
4083
+ }
4084
+ });
4085
+ const url = new URL(paymasterUrl);
4086
+ url.searchParams.set("owneyIntent", intent);
4087
+ paymasterUrl = url.toString();
4088
+ }
3532
4089
  const sendResult = await deps.provider.request({
3533
4090
  method: "wallet_sendCalls",
3534
4091
  params: [
@@ -3537,9 +4094,9 @@ function makeSponsoredCallsCallback(deps) {
3537
4094
  from: deps.ownerAddress,
3538
4095
  chainId: toHex(chainId),
3539
4096
  atomicRequired: false,
3540
- calls: [{ to: token2, value: "0x0", data }],
4097
+ calls: [{ to: token, value: "0x0", data }],
3541
4098
  capabilities: {
3542
- paymasterService: { url: absolutePaymasterUrl() }
4099
+ paymasterService: { url: paymasterUrl }
3543
4100
  }
3544
4101
  }
3545
4102
  ]
@@ -3569,17 +4126,17 @@ function makeSponsoredCallsCallback(deps) {
3569
4126
  `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
3570
4127
  { chainId, callsId }
3571
4128
  );
3572
- };
4129
+ });
3573
4130
  }
3574
4131
 
3575
4132
  // src/client.ts
3576
4133
  function encodeMultiAgentCursor(map) {
3577
4134
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
3578
4135
  }
3579
- function decodeMultiAgentCursor(token2) {
4136
+ function decodeMultiAgentCursor(token) {
3580
4137
  let parsed;
3581
4138
  try {
3582
- const json = Buffer.from(token2, "base64").toString("utf8");
4139
+ const json = Buffer.from(token, "base64").toString("utf8");
3583
4140
  parsed = JSON.parse(json);
3584
4141
  } catch {
3585
4142
  throw new InvalidHistoryCursorError(
@@ -3768,6 +4325,8 @@ var OwneySDK = class {
3768
4325
  if (!onApproved && cached) return cached;
3769
4326
  const provider = this.requireConnectedProvider();
3770
4327
  const callback = makeSponsoredCallsCallback({
4328
+ apiKey: this.apiKey,
4329
+ routingApiBaseUrl: this.routingApiBaseUrl,
3771
4330
  provider,
3772
4331
  ownerAddress: this.state.walletAddress,
3773
4332
  paymasterServiceUrl: this.paymasterServiceUrl,
@@ -4210,15 +4769,15 @@ var OwneySDK = class {
4210
4769
  agent,
4211
4770
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
4212
4771
  }));
4213
- const valid = splits.filter(
4772
+ const valid2 = splits.filter(
4214
4773
  (s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
4215
4774
  );
4216
- if (valid.length === agents.length) {
4775
+ if (valid2.length === agents.length) {
4217
4776
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
4218
4777
  }
4219
4778
  return this.splitDepositAmount(
4220
4779
  totalAmount,
4221
- valid.map((s) => s.agent),
4780
+ valid2.map((s) => s.agent),
4222
4781
  chainId,
4223
4782
  asset,
4224
4783
  exempt
@@ -4251,10 +4810,10 @@ var OwneySDK = class {
4251
4810
  try {
4252
4811
  const balance = await agent.getBalances(state, chainId);
4253
4812
  const target = asset.toLowerCase();
4254
- const token2 = balance.tokens.find(
4813
+ const token = balance.tokens.find(
4255
4814
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
4256
4815
  );
4257
- return !!token2 && Number(token2.amount) > 0;
4816
+ return !!token && Number(token.amount) > 0;
4258
4817
  } catch {
4259
4818
  return false;
4260
4819
  }
@@ -4305,9 +4864,9 @@ var OwneySDK = class {
4305
4864
  const { asset, amount, agentId } = options;
4306
4865
  const state = this.requireState();
4307
4866
  const chainId = this.requireChainId();
4308
- const token2 = asset;
4867
+ const token = asset;
4309
4868
  const assetInfo = SupportedAssets.find(
4310
- (a) => a.chainId === chainId && a.symbol === token2
4869
+ (a) => a.chainId === chainId && a.symbol === token
4311
4870
  );
4312
4871
  if (!assetInfo) {
4313
4872
  throw new OwneyError(
@@ -4321,7 +4880,7 @@ var OwneySDK = class {
4321
4880
  return withFailureReporting(
4322
4881
  this.apiKey,
4323
4882
  agent.id,
4324
- () => agent.withdraw(state, chainId, token2, amount),
4883
+ () => agent.withdraw(state, chainId, token, amount),
4325
4884
  this.routingApiBaseUrl
4326
4885
  );
4327
4886
  }
@@ -4331,7 +4890,7 @@ var OwneySDK = class {
4331
4890
  const agentErrors2 = {};
4332
4891
  for (const agent of eligibleAgents) {
4333
4892
  try {
4334
- results2[agent.id] = await agent.withdraw(state, chainId, token2);
4893
+ results2[agent.id] = await agent.withdraw(state, chainId, token);
4335
4894
  } catch (err) {
4336
4895
  console.error(`withdraw failed for agent "${agent.id}":`, err);
4337
4896
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
@@ -4421,7 +4980,7 @@ var OwneySDK = class {
4421
4980
  results[p.agent.id] = await p.agent.withdraw(
4422
4981
  state,
4423
4982
  chainId,
4424
- token2,
4983
+ token,
4425
4984
  p.planned.toString()
4426
4985
  );
4427
4986
  } catch (err) {
@@ -4772,8 +5331,8 @@ var OwneySDK = class {
4772
5331
  const state = this.requireState();
4773
5332
  const chainId = this.requireChainId();
4774
5333
  this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
4775
- const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
4776
- if (!token2) {
5334
+ const token = SPONSORED_WETH_BY_CHAIN[chainId];
5335
+ if (!token) {
4777
5336
  throw new OwneyError(
4778
5337
  "CHAIN_UNSUPPORTED",
4779
5338
  `No sponsored WETH on chain ${chainId}`
@@ -4786,7 +5345,7 @@ var OwneySDK = class {
4786
5345
  transport: custom3(provider)
4787
5346
  });
4788
5347
  const hash = await wallet.writeContract({
4789
- address: token2,
5348
+ address: token,
4790
5349
  abi: ERC20_ALLOWANCE_ABI,
4791
5350
  functionName: "approve",
4792
5351
  args: [PERMIT2_ADDRESS, MAX_UINT256],
@@ -4911,8 +5470,8 @@ import { SiweMessage as SiweMessage2 } from "siwe";
4911
5470
  import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
4912
5471
 
4913
5472
  // src/agents/zyfai/zyfai.siwx-cache.ts
4914
- var KEY_PREFIX3 = "owney.siwx.session";
4915
- var storage3 = () => {
5473
+ var KEY_PREFIX4 = "owney.siwx.session";
5474
+ var storage4 = () => {
4916
5475
  if (typeof window === "undefined") return null;
4917
5476
  try {
4918
5477
  return window.localStorage;
@@ -4920,8 +5479,8 @@ var storage3 = () => {
4920
5479
  return null;
4921
5480
  }
4922
5481
  };
4923
- var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
4924
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
5482
+ var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
5483
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
4925
5484
  var memorySiwxSessions = /* @__PURE__ */ new Map();
4926
5485
  var readLegacySiwxSession = (store, address) => {
4927
5486
  if (!store) return null;
@@ -4953,16 +5512,16 @@ var readLegacySiwxSession = (store, address) => {
4953
5512
  var readSiwxSession = (address, chainId) => {
4954
5513
  if (typeof window === "undefined") return null;
4955
5514
  const key2 = buildKey3(address);
4956
- const store = storage3();
4957
- let raw = null;
5515
+ const store = storage4();
5516
+ let raw2 = null;
4958
5517
  try {
4959
- raw = store?.getItem(key2) ?? null;
5518
+ raw2 = store?.getItem(key2) ?? null;
4960
5519
  } catch {
4961
- raw = null;
5520
+ raw2 = null;
4962
5521
  }
4963
- if (raw) {
5522
+ if (raw2) {
4964
5523
  try {
4965
- return JSON.parse(raw);
5524
+ return JSON.parse(raw2);
4966
5525
  } catch {
4967
5526
  memorySiwxSessions.delete(key2);
4968
5527
  try {
@@ -4983,7 +5542,7 @@ var writeSiwxSession = (address, _chainId, session) => {
4983
5542
  if (typeof window === "undefined") return;
4984
5543
  const key2 = buildKey3(address);
4985
5544
  memorySiwxSessions.set(key2, session);
4986
- const store = storage3();
5545
+ const store = storage4();
4987
5546
  try {
4988
5547
  store?.setItem(key2, JSON.stringify(session));
4989
5548
  } catch {
@@ -4992,7 +5551,7 @@ var writeSiwxSession = (address, _chainId, session) => {
4992
5551
  var clearSiwxSession = (address, _chainId) => {
4993
5552
  const key2 = buildKey3(address);
4994
5553
  memorySiwxSessions.delete(key2);
4995
- const store = storage3();
5554
+ const store = storage4();
4996
5555
  try {
4997
5556
  store?.removeItem(key2);
4998
5557
  } catch {