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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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,346 @@ 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;
2643
+ }
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;
2542
2648
  }
2543
- function mapYieldseekerProfile(value) {
2544
- const profile = requireEnvelope(
2545
- value,
2546
- "profile",
2547
- "profile"
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) {
2807
+ if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
2622
2808
  return {
2623
2809
  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
- }))
2810
+ action: actionType(action.actionType),
2811
+ date: action.createdDate,
2812
+ oldApy: null,
2813
+ newApy: null,
2814
+ transactions: [
2815
+ {
2816
+ txHashes: transactionHashes(action.details),
2817
+ chainId: 8453
2818
+ }
2819
+ ],
2820
+ rebalanceLog: []
2647
2821
  };
2648
2822
  }
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");
2823
+ function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses) {
2824
+ const from = movement.fromAddress.toLowerCase();
2825
+ const to = movement.toAddress.toLowerCase();
2826
+ const owner = ownerAddress.toLowerCase();
2827
+ const agentWallet = wallet.walletAddress.toLowerCase();
2828
+ const baseAsset = agent.assetAddress.toLowerCase();
2829
+ if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
2830
+ return void 0;
2831
+ }
2832
+ let action;
2833
+ if (from === owner && to === agentWallet) {
2834
+ action = "Top up";
2835
+ } else if (from === agentWallet && to === owner) {
2836
+ action = "Withdraw";
2837
+ } else if (from === agentWallet && vaultAddresses.has(to)) {
2838
+ action = "Deposit";
2839
+ }
2840
+ if (!action) return void 0;
2841
+ return {
2842
+ agent: "yieldseeker",
2843
+ action,
2844
+ date: movement.blockDate,
2845
+ oldApy: null,
2846
+ newApy: null,
2847
+ transactions: [
2848
+ {
2849
+ txHashes: [movement.transactionHash],
2850
+ chainId: agent.chainId,
2851
+ tokenSymbol: asset,
2852
+ amount: decimal(
2853
+ movement.assetAmount,
2854
+ YIELDSEEKER_ASSET_METADATA[asset].decimals,
2855
+ "historic position"
2856
+ )
2857
+ }
2858
+ ],
2859
+ rebalanceLog: []
2860
+ };
2861
+ }
2862
+ function mapYieldseekerHistory(contexts, options) {
2863
+ const entries = contexts.flatMap((context) => {
2864
+ const vaultAddresses = new Set(
2865
+ context.positions.map(
2866
+ (position2) => position2.yieldOption.address.toLowerCase()
2867
+ )
2868
+ );
2869
+ return [
2870
+ ...(context.historic?.movements ?? []).map(
2871
+ (movement) => movementEntry(
2872
+ movement,
2873
+ context.wallet,
2874
+ context.agent,
2875
+ context.asset,
2876
+ options.ownerAddress,
2877
+ vaultAddresses
2878
+ )
2879
+ ),
2880
+ ...(context.actions ?? []).map(actionEntry)
2881
+ ].filter((entry) => entry !== void 0);
2882
+ });
2883
+ const filtered = entries.filter(
2884
+ (entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
2885
+ ).filter((entry, index, all) => {
2886
+ const transactionHash = entry.transactions[0]?.txHashes[0];
2887
+ if (!transactionHash) return true;
2888
+ return all.findIndex(
2889
+ (candidate) => candidate.action === entry.action && candidate.transactions[0]?.txHashes[0] === transactionHash
2890
+ ) === index;
2891
+ }).sort((left, right) => right.date.localeCompare(left.date));
2892
+ return {
2893
+ data: filtered.slice(0, options.limit),
2894
+ // v1 returns the whole action/movement collection and defines no cursor.
2895
+ // Report a terminal page so callers never loop over the same prefix.
2896
+ hasMore: false
2897
+ };
2898
+ }
2899
+ function mapYieldseekerProfile(address, contexts) {
2900
+ const protocols = /* @__PURE__ */ new Set();
2901
+ for (const context of contexts) {
2902
+ for (const current of context.positions) {
2903
+ if (current.yieldOption?.provider) {
2904
+ protocols.add(String(current.yieldOption.provider));
2905
+ }
2906
+ }
2657
2907
  }
2658
2908
  return {
2659
- data: history.data.map(historyItem),
2660
- hasMore: history.hasMore,
2661
- ...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
2909
+ address,
2910
+ smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
2911
+ chains: contexts.length > 0 ? [8453] : [],
2912
+ hasActiveSessionKey: contexts.some(
2913
+ (context) => context.wallet.initializedDate != null
2914
+ ),
2915
+ protocols: [...protocols]
2662
2916
  };
2663
2917
  }
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");
2918
+ function mapYieldseekerAgentApy(options, days) {
2919
+ const perAsset = {};
2920
+ const all = [];
2921
+ for (const entry of options) {
2922
+ const apys = entry.yieldOptions.map(
2923
+ (option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
2924
+ ).filter(Number.isFinite);
2925
+ if (apys.length === 0) continue;
2926
+ const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
2927
+ perAsset[entry.asset] = average;
2928
+ all.push(average);
2672
2929
  }
2673
2930
  return {
2674
- averageApy: agentApy.averageApy,
2675
- ...agentApy.detailedApys ? {
2676
- detailedApys: {
2677
- apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
2678
- }
2679
- } : {}
2931
+ averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
2932
+ detailedApys: { apyPerAsset: { 8453: perAsset } }
2680
2933
  };
2681
2934
  }
2682
2935
 
2683
2936
  // src/agents/yieldseeker/yieldseeker.agent.ts
2937
+ var OWNEY_AGENT_NAME = "owney";
2938
+ var YIELDSEEKER_AGENT_WALLET_ABI = [
2939
+ {
2940
+ type: "function",
2941
+ name: "withdrawAssetToUser",
2942
+ stateMutability: "nonpayable",
2943
+ inputs: [
2944
+ { name: "recipient", type: "address" },
2945
+ { name: "asset", type: "address" },
2946
+ { name: "amount", type: "uint256" }
2947
+ ],
2948
+ outputs: []
2949
+ },
2950
+ {
2951
+ type: "function",
2952
+ name: "withdrawAllAssetToUser",
2953
+ stateMutability: "nonpayable",
2954
+ inputs: [
2955
+ { name: "recipient", type: "address" },
2956
+ { name: "asset", type: "address" }
2957
+ ],
2958
+ outputs: []
2959
+ }
2960
+ ];
2684
2961
  function query(params) {
2685
2962
  const search = new URLSearchParams();
2686
2963
  for (const [key2, value] of Object.entries(params)) {
@@ -2698,7 +2975,7 @@ var YieldseekerAgent = class {
2698
2975
  chainId: 8453,
2699
2976
  chain: "BASE",
2700
2977
  assets: [
2701
- { symbol: "USDC", minDepositAmount: "1" },
2978
+ { symbol: "USDC", minDepositAmount: "10000000" },
2702
2979
  { symbol: "WETH", minDepositAmount: "1" }
2703
2980
  ]
2704
2981
  }
@@ -2707,7 +2984,9 @@ var YieldseekerAgent = class {
2707
2984
  auth;
2708
2985
  transactionExecutor;
2709
2986
  unwindReceiptWaiter;
2710
- activated = /* @__PURE__ */ new Set();
2987
+ agentContexts = /* @__PURE__ */ new Map();
2988
+ users = /* @__PURE__ */ new Map();
2989
+ pendingAgents = /* @__PURE__ */ new Map();
2711
2990
  constructor(owneyApiKey, options = {}) {
2712
2991
  this.api = new YieldseekerApiClient(
2713
2992
  owneyApiKey,
@@ -2720,14 +2999,19 @@ var YieldseekerAgent = class {
2720
2999
  }
2721
3000
  async disconnect() {
2722
3001
  this.auth.clear();
2723
- this.activated.clear();
3002
+ for (const key2 of this.users.keys()) {
3003
+ const [walletAddress, chainId] = key2.split(":");
3004
+ clearYieldseekerIdentity(walletAddress, Number(chainId));
3005
+ }
3006
+ this.users.clear();
3007
+ this.agentContexts.clear();
3008
+ this.pendingAgents.clear();
2724
3009
  }
2725
3010
  async activateAgent(state, chainId, asset) {
2726
3011
  this.assertChain(chainId);
2727
- await this.auth.getToken(state, chainId);
2728
- if (asset === "USDC" || asset === "WETH") {
2729
- await this.ensureActivated(state, chainId, asset);
2730
- }
3012
+ const targetAsset = asset ?? "USDC";
3013
+ this.assertAsset(targetAsset);
3014
+ await this.ensureAgent(state, chainId, targetAsset);
2731
3015
  }
2732
3016
  async deposit(state, chainId, amount, asset, depositCallback) {
2733
3017
  this.assertChain(chainId);
@@ -2740,42 +3024,48 @@ var YieldseekerAgent = class {
2740
3024
  this.id
2741
3025
  );
2742
3026
  }
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
- });
3027
+ const context = await this.ensureAgent(state, chainId, asset);
3028
+ let txHash;
3029
+ try {
3030
+ if (depositCallback) {
3031
+ provideDepositVerificationContext(depositCallback, {
3032
+ agentId: "yieldseeker",
3033
+ signature: await this.auth.getToken(state, chainId),
3034
+ userId: context.user.userId,
3035
+ yieldseekerAgentId: context.agent.agentId
3036
+ });
3037
+ txHash = await depositCallback(
3038
+ context.wallet.walletAddress,
3039
+ chainId,
3040
+ amount
3041
+ );
3042
+ await this.waitForReceipt(state, chainId, txHash);
3043
+ } else {
3044
+ txHash = await this.submitTransaction(state, chainId, {
3045
+ from: getAddress2(state.walletAddress),
3046
+ to: YIELDSEEKER_ASSET_METADATA[asset].address,
3047
+ data: encodeFunctionData({
3048
+ abi: erc20Abi,
3049
+ functionName: "transfer",
3050
+ args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
3051
+ }),
3052
+ value: "0",
3053
+ chainId
3054
+ });
3055
+ }
3056
+ await this.deployAfterFunding(state, chainId, context);
3057
+ } finally {
3058
+ await this.refreshSnapshotAfterMovement(
3059
+ state,
3060
+ chainId,
3061
+ context,
3062
+ "deposit"
3063
+ );
2774
3064
  }
2775
3065
  return {
2776
3066
  txHash,
2777
- smartWallet: deposit.smartWallet,
2778
- amount: deposit.amount
3067
+ smartWallet: context.wallet.walletAddress,
3068
+ amount
2779
3069
  };
2780
3070
  }
2781
3071
  async withdraw(state, chainId, asset, amount) {
@@ -2789,198 +3079,455 @@ var YieldseekerAgent = class {
2789
3079
  this.id
2790
3080
  );
2791
3081
  }
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) {
3082
+ const context = await this.findAgent(state, chainId, asset);
3083
+ if (!context) {
2811
3084
  throw new OwneyError(
2812
3085
  "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
- },
3086
+ `No Yieldseeker ${asset} agent exists for this wallet.`,
3087
+ { asset, available: "0" },
2819
3088
  this.id
2820
3089
  );
2821
3090
  }
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
3091
+ try {
3092
+ const portfolio = await this.loadPortfolioContext(
3093
+ state,
3094
+ chainId,
3095
+ context
3096
+ );
3097
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3098
+ const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
3099
+ ([address]) => address.toLowerCase() === metadata.address.toLowerCase()
3100
+ );
3101
+ const idle = BigInt(idleEntry?.[1] ?? "0");
3102
+ const deployed = portfolio.positions.reduce(
3103
+ (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
3104
+ 0n
3105
+ );
3106
+ const totalAvailable = idle + deployed;
3107
+ const requested = amount === void 0 ? totalAvailable : BigInt(amount);
3108
+ if (requested > totalAvailable) {
3109
+ throw new OwneyError(
3110
+ "WITHDRAW_INSUFFICIENT_BALANCE",
3111
+ `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
3112
+ {
3113
+ asset,
3114
+ requested: requested.toString(),
3115
+ available: totalAvailable.toString()
3116
+ },
3117
+ this.id
3118
+ );
3119
+ }
3120
+ let remaining = requested > idle ? requested - idle : 0n;
3121
+ for (const position2 of portfolio.positions) {
3122
+ if (remaining === 0n) break;
3123
+ const available = BigInt(position2.withdrawableAssetsRaw);
3124
+ if (available <= 0n) continue;
3125
+ const assetsRaw = available < remaining ? available : remaining;
3126
+ const response = await this.walletRequest(
3127
+ state,
3128
+ chainId,
3129
+ this.agentPath(context, "withdraw-from-position"),
3130
+ {
3131
+ method: "POST",
3132
+ body: {
3133
+ chainId,
3134
+ vaultAddress: position2.yieldOption.address,
3135
+ assetsRaw: assetsRaw.toString()
3136
+ }
3137
+ }
3138
+ );
3139
+ if (!this.isTransactionHash(response?.transactionHash)) {
3140
+ throw this.invalidResponse("position withdrawal");
3141
+ }
3142
+ await this.waitForReceipt(state, chainId, response.transactionHash);
3143
+ remaining -= assetsRaw;
3144
+ }
3145
+ if (remaining > 0n) {
3146
+ throw this.invalidResponse("yield positions", {
3147
+ reason: "Withdrawable positions could not cover the request.",
3148
+ remaining: remaining.toString()
2833
3149
  });
2834
3150
  }
2835
- const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
2836
- unwindPlans.push({
2837
- vaultAddress: getAddress2(position2.protocolId),
2838
- amount: unwindAmount
3151
+ const account = getAddress2(state.walletAddress);
3152
+ const txHash = await this.submitTransaction(state, chainId, {
3153
+ from: account,
3154
+ to: getAddress2(context.wallet.walletAddress),
3155
+ data: amount === void 0 ? encodeFunctionData({
3156
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3157
+ functionName: "withdrawAllAssetToUser",
3158
+ args: [account, metadata.address]
3159
+ }) : encodeFunctionData({
3160
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
3161
+ functionName: "withdrawAssetToUser",
3162
+ args: [account, metadata.address, requested]
3163
+ }),
3164
+ value: "0",
3165
+ chainId
2839
3166
  });
2840
- remainingToUnwind -= unwindAmount;
3167
+ return {
3168
+ txHash,
3169
+ type: amount === void 0 ? "full" : "partial",
3170
+ amount: requested.toString()
3171
+ };
3172
+ } finally {
3173
+ await this.refreshSnapshotAfterMovement(
3174
+ state,
3175
+ chainId,
3176
+ context,
3177
+ "withdrawal"
3178
+ );
2841
3179
  }
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
- });
3180
+ }
3181
+ async getBalances(state, chainId) {
3182
+ this.assertChain(chainId);
3183
+ return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
3184
+ }
3185
+ async getEarnings(state, chainId) {
3186
+ this.assertChain(chainId);
3187
+ return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
3188
+ }
3189
+ async getAccountApy(state, chainId, days, tokenSymbol) {
3190
+ this.assertChain(chainId);
3191
+ const asset = tokenSymbol?.toUpperCase();
3192
+ if (asset !== void 0) this.assertAsset(asset);
3193
+ const contexts = await this.loadPortfolio(state, chainId, {
3194
+ ...asset ? { asset } : {},
3195
+ historic: true
3196
+ });
3197
+ return mapYieldseekerApy(state.walletAddress, contexts, days);
3198
+ }
3199
+ async getHistory(state, chainId, options) {
3200
+ this.assertChain(chainId);
3201
+ const asset = options?.tokenSymbol?.toUpperCase();
3202
+ if (asset !== void 0) this.assertAsset(asset);
3203
+ const contexts = await this.loadPortfolio(state, chainId, {
3204
+ ...asset ? { asset } : {},
3205
+ historic: true,
3206
+ actions: true
3207
+ });
3208
+ return mapYieldseekerHistory(contexts, {
3209
+ limit: options?.limit ?? 10,
3210
+ ownerAddress: state.walletAddress,
3211
+ ...options?.fromDate ? { fromDate: options.fromDate } : {},
3212
+ ...options?.toDate ? { toDate: options.toDate } : {}
3213
+ });
3214
+ }
3215
+ async getUserProfile(state, chainId) {
3216
+ this.assertChain(chainId);
3217
+ return mapYieldseekerProfile(
3218
+ state.walletAddress,
3219
+ await this.loadPortfolio(state, chainId, {})
3220
+ );
3221
+ }
3222
+ async getAgentApy(days, options) {
3223
+ this.assertOptionalChain(options?.chainId);
3224
+ const requested = options?.tokenSymbol?.toUpperCase();
3225
+ if (requested !== void 0) this.assertAsset(requested);
3226
+ const assets = requested ? [requested] : ["USDC", "WETH"];
3227
+ const values = await Promise.all(
3228
+ assets.map(async (asset) => {
3229
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3230
+ const response = await this.api.request(
3231
+ `/chains/8453/assets/${metadata.address}/yield-options`
3232
+ );
3233
+ if (!Array.isArray(response?.yieldOptions)) {
3234
+ throw this.invalidResponse("yield options");
3235
+ }
3236
+ return { asset, yieldOptions: response.yieldOptions };
3237
+ })
3238
+ );
3239
+ return mapYieldseekerAgentApy(values, days);
3240
+ }
3241
+ userKey(state, chainId) {
3242
+ return `${state.walletAddress.toLowerCase()}:${chainId}`;
3243
+ }
3244
+ contextKey(state, chainId, asset) {
3245
+ return `${this.userKey(state, chainId)}:${asset}`;
3246
+ }
3247
+ async resolveUser(state, chainId) {
3248
+ const key2 = this.userKey(state, chainId);
3249
+ const inMemory = this.users.get(key2);
3250
+ if (inMemory) return inMemory;
3251
+ const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
3252
+ if (persisted) {
3253
+ this.users.set(key2, persisted);
3254
+ return persisted;
2848
3255
  }
2849
- for (const plan of unwindPlans) {
2850
- const response2 = await this.walletRequest(
3256
+ const walletAddress = getAddress2(state.walletAddress);
3257
+ let user = null;
3258
+ try {
3259
+ const login = await this.providerRequest(
2851
3260
  state,
2852
3261
  chainId,
2853
- "/positions/unwind",
3262
+ "/users/login-with-wallet",
3263
+ { method: "POST", body: { walletAddress } }
3264
+ );
3265
+ user = login?.user ?? null;
3266
+ if (!user) {
3267
+ throw this.invalidResponse("wallet login", {
3268
+ reason: "A successful login returned no user."
3269
+ });
3270
+ }
3271
+ } catch (error) {
3272
+ if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
3273
+ if (error instanceof OwneyError) throw error;
3274
+ throw this.mapApiError(error);
3275
+ }
3276
+ let created;
3277
+ try {
3278
+ created = await this.providerRequest(
3279
+ state,
3280
+ chainId,
3281
+ "/users",
3282
+ {
3283
+ method: "POST",
3284
+ body: {
3285
+ walletAddress,
3286
+ username: `owney-${walletAddress.slice(2).toLowerCase()}`
3287
+ }
3288
+ }
3289
+ );
3290
+ } catch (createError) {
3291
+ throw this.mapApiError(createError);
3292
+ }
3293
+ user = created?.user ?? null;
3294
+ }
3295
+ if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
3296
+ throw this.invalidResponse("wallet identity");
3297
+ }
3298
+ const resolved = { userId: user.userId };
3299
+ this.users.set(key2, resolved);
3300
+ writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
3301
+ return resolved;
3302
+ }
3303
+ forgetUser(state, chainId) {
3304
+ this.users.delete(this.userKey(state, chainId));
3305
+ clearYieldseekerIdentity(state.walletAddress, chainId);
3306
+ }
3307
+ async ensureAgent(state, chainId, asset) {
3308
+ const key2 = this.contextKey(state, chainId, asset);
3309
+ const cached = this.agentContexts.get(key2);
3310
+ if (cached) return cached;
3311
+ const pending = this.pendingAgents.get(key2);
3312
+ if (pending) return pending;
3313
+ const request = this.resolveAgent(state, chainId, asset, true).then(
3314
+ (context) => {
3315
+ if (!context) throw this.invalidResponse("agent creation");
3316
+ this.agentContexts.set(key2, context);
3317
+ return context;
3318
+ }
3319
+ );
3320
+ this.pendingAgents.set(key2, request);
3321
+ try {
3322
+ return await request;
3323
+ } finally {
3324
+ this.pendingAgents.delete(key2);
3325
+ }
3326
+ }
3327
+ async findAgent(state, chainId, asset) {
3328
+ const key2 = this.contextKey(state, chainId, asset);
3329
+ const cached = this.agentContexts.get(key2);
3330
+ if (cached) return cached;
3331
+ const context = await this.resolveAgent(state, chainId, asset, false);
3332
+ if (context) this.agentContexts.set(key2, context);
3333
+ return context;
3334
+ }
3335
+ async resolveAgent(state, chainId, asset, createIfMissing) {
3336
+ const user = await this.resolveUser(state, chainId);
3337
+ const response = await this.walletRequest(
3338
+ state,
3339
+ chainId,
3340
+ `/users/${user.userId}/agents`
3341
+ );
3342
+ if (!Array.isArray(response?.agents)) {
3343
+ throw this.invalidResponse("agent list");
3344
+ }
3345
+ const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3346
+ let agent = response.agents.find(
3347
+ (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3348
+ );
3349
+ if (!agent && createIfMissing) {
3350
+ const created = await this.walletRequest(
3351
+ state,
3352
+ chainId,
3353
+ `/users/${user.userId}/agents`,
2854
3354
  {
2855
3355
  method: "POST",
2856
3356
  body: {
3357
+ name: OWNEY_AGENT_NAME,
3358
+ emoji: "\u{1F989}",
2857
3359
  chainId,
2858
- asset,
2859
- vaultAddress: plan.vaultAddress,
2860
- amount: plan.amount.toString()
3360
+ assetAddress: metadata.address,
3361
+ type: "vault",
3362
+ rulePreset: null
2861
3363
  }
2862
3364
  }
2863
3365
  );
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);
3366
+ agent = created?.agent;
2869
3367
  }
2870
- const response = await this.walletRequest(
3368
+ if (!agent) return null;
3369
+ this.assertAgent(agent);
3370
+ const walletResponse = await this.walletRequest(
2871
3371
  state,
2872
3372
  chainId,
2873
- "/withdrawals",
2874
- {
2875
- method: "POST",
2876
- body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
2877
- }
3373
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
2878
3374
  );
2879
- const withdrawal = response?.withdrawal;
2880
- if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
2881
- throw this.invalidResponse("withdrawal");
3375
+ if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
3376
+ throw this.invalidResponse("agent wallet");
2882
3377
  }
2883
- const txHash = await this.submitTransaction(
3378
+ return { user, agent, wallet: walletResponse.agentWallet, asset };
3379
+ }
3380
+ async loadPortfolio(state, chainId, options) {
3381
+ const user = await this.resolveUser(state, chainId);
3382
+ const response = await this.walletRequest(
2884
3383
  state,
2885
3384
  chainId,
2886
- withdrawal.transaction
3385
+ `/users/${user.userId}/agents`
2887
3386
  );
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(
3387
+ if (!Array.isArray(response?.agents)) {
3388
+ throw this.invalidResponse("agent list");
3389
+ }
3390
+ const contexts = [];
3391
+ for (const agent of response.agents) {
3392
+ const asset = this.assetForAgent(agent);
3393
+ if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3394
+ continue;
3395
+ }
3396
+ this.assertAgent(agent);
3397
+ const walletResponse = await this.walletRequest(
2897
3398
  state,
2898
3399
  chainId,
2899
- `/balances${query({ chainId })}`
3400
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
3401
+ );
3402
+ if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
3403
+ throw this.invalidResponse("agent wallet");
3404
+ }
3405
+ const context = {
3406
+ user,
3407
+ agent,
3408
+ wallet: walletResponse.agentWallet,
3409
+ asset
3410
+ };
3411
+ this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3412
+ contexts.push(context);
3413
+ }
3414
+ return Promise.all(
3415
+ contexts.map(
3416
+ (context) => this.loadPortfolioContext(state, chainId, context, options)
2900
3417
  )
2901
3418
  );
2902
3419
  }
2903
- async getEarnings(state, chainId) {
2904
- return mapYieldseekerEarnings(
2905
- await this.walletRequest(
3420
+ async loadPortfolioContext(state, chainId, context, options = {}) {
3421
+ const [snapshot, positions, historic, actions] = await Promise.all([
3422
+ this.walletRequest(
2906
3423
  state,
2907
3424
  chainId,
2908
- `/earnings${query({ chainId })}`
2909
- )
2910
- );
3425
+ `${this.agentPath(context, "snapshot")}${query({
3426
+ shouldOnlyUseRecentValue: true,
3427
+ shouldAllowStaleOnError: true
3428
+ })}`
3429
+ ),
3430
+ this.walletRequest(
3431
+ state,
3432
+ chainId,
3433
+ this.agentPath(context, "yield-positions")
3434
+ ),
3435
+ options.historic ? this.walletRequest(
3436
+ state,
3437
+ chainId,
3438
+ this.agentPath(context, "wallet/historic-position")
3439
+ ) : Promise.resolve(void 0),
3440
+ options.actions ? this.walletRequest(
3441
+ state,
3442
+ chainId,
3443
+ this.agentPath(context, "actions")
3444
+ ) : Promise.resolve(void 0)
3445
+ ]);
3446
+ if (!snapshot?.agentSnapshot) {
3447
+ throw this.invalidResponse("agent snapshot");
3448
+ }
3449
+ if (!Array.isArray(positions?.yieldPositions)) {
3450
+ throw this.invalidResponse("yield positions");
3451
+ }
3452
+ return {
3453
+ ...context,
3454
+ snapshot: snapshot.agentSnapshot,
3455
+ positions: positions.yieldPositions,
3456
+ ...historic?.position ? { historic: historic.position } : {},
3457
+ ...actions?.actions ? { actions: actions.actions } : {}
3458
+ };
2911
3459
  }
2912
- async getAccountApy(state, chainId, days, tokenSymbol) {
2913
- return mapYieldseekerApy(
2914
- await this.walletRequest(
3460
+ async deployAfterFunding(state, chainId, context) {
3461
+ if (context.wallet.initializedDate != null) return;
3462
+ try {
3463
+ const deployed = await this.walletRequest(
2915
3464
  state,
2916
3465
  chainId,
2917
- `/apy${query({ chainId, days, tokenSymbol })}`
2918
- )
2919
- );
3466
+ this.agentPath(context, "deploy"),
3467
+ { method: "POST", body: {} }
3468
+ );
3469
+ if (deployed?.agentWallet) {
3470
+ context.wallet = deployed.agentWallet;
3471
+ }
3472
+ } catch (error) {
3473
+ console.warn(
3474
+ "[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
3475
+ error
3476
+ );
3477
+ }
2920
3478
  }
2921
- async getHistory(state, chainId, options) {
2922
- return mapYieldseekerHistory(
2923
- await this.walletRequest(
3479
+ async refreshSnapshotAfterMovement(state, chainId, context, movement) {
3480
+ try {
3481
+ const response = await this.walletRequest(
2924
3482
  state,
2925
3483
  chainId,
2926
- `/history${query({
2927
- chainId,
2928
- limit: options?.limit ?? 10,
2929
- cursor: options?.cursor,
2930
- fromDate: options?.fromDate,
2931
- toDate: options?.toDate
3484
+ `${this.agentPath(context, "snapshot")}${query({
3485
+ shouldForceRefresh: true
2932
3486
  })}`
2933
- )
2934
- );
2935
- }
2936
- async getUserProfile(state, chainId) {
2937
- return mapYieldseekerProfile(
2938
- await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
2939
- );
3487
+ );
3488
+ if (!response?.agentSnapshot) {
3489
+ throw this.invalidResponse("agent snapshot refresh");
3490
+ }
3491
+ } catch (error) {
3492
+ console.warn(
3493
+ `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3494
+ error
3495
+ );
3496
+ }
2940
3497
  }
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
- );
3498
+ agentPath(context, suffix) {
3499
+ return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
2952
3500
  }
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");
3501
+ async walletRequest(state, chainId, path, options = {}) {
3502
+ try {
3503
+ return await this.providerRequest(state, chainId, path, options);
3504
+ } catch (error) {
3505
+ throw this.mapApiError(error);
2964
3506
  }
2965
- this.activated.add(key2);
2966
3507
  }
2967
- async walletRequest(state, chainId, path, options = {}) {
3508
+ async providerRequest(state, chainId, path, options = {}) {
2968
3509
  this.assertChain(chainId);
3510
+ const request = (signature2) => this.api.request(path, {
3511
+ ...options,
3512
+ signature: signature2
3513
+ });
2969
3514
  let signature = await this.auth.getToken(state, chainId);
2970
3515
  try {
2971
- return await this.api.request(path, { ...options, signature });
3516
+ return await request(signature);
2972
3517
  } catch (error) {
2973
3518
  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);
3519
+ if (error.providerCode === "NO_USER") throw error;
3520
+ if (!error.isAuthenticationError) throw error;
3521
+ this.auth.clear(state, chainId);
3522
+ signature = await this.auth.getToken(state, chainId);
3523
+ try {
3524
+ return await request(signature);
3525
+ } catch (retryError) {
3526
+ if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
3527
+ this.forgetUser(state, chainId);
2981
3528
  }
3529
+ throw retryError;
2982
3530
  }
2983
- throw this.mapApiError(error);
2984
3531
  }
2985
3532
  }
2986
3533
  mapApiError(error) {
@@ -3008,15 +3555,8 @@ var YieldseekerAgent = class {
3008
3555
  if (this.transactionExecutor) {
3009
3556
  return this.transactionExecutor(state, chainId, transaction);
3010
3557
  }
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
- }
3558
+ this.assertTransaction(transaction, state, chainId);
3014
3559
  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
3560
  const walletClient = createWalletClient2({
3021
3561
  account,
3022
3562
  chain: base3,
@@ -3052,7 +3592,7 @@ var YieldseekerAgent = class {
3052
3592
  }
3053
3593
  return hash;
3054
3594
  }
3055
- async waitForUnwindReceipt(state, chainId, transactionHash) {
3595
+ async waitForReceipt(state, chainId, transactionHash) {
3056
3596
  if (this.unwindReceiptWaiter) {
3057
3597
  await this.unwindReceiptWaiter(state, chainId, transactionHash);
3058
3598
  return;
@@ -3068,15 +3608,32 @@ var YieldseekerAgent = class {
3068
3608
  if (receipt.status !== "success") {
3069
3609
  throw new OwneyError(
3070
3610
  "AGENT_TRANSACTION_REVERTED",
3071
- `Yieldseeker position unwind reverted (${transactionHash}).`,
3611
+ `Yieldseeker transaction reverted (${transactionHash}).`,
3072
3612
  { transactionHash },
3073
3613
  this.id
3074
3614
  );
3075
3615
  }
3076
3616
  }
3077
- positionMatchesChain(chain, chainId) {
3078
- const normalized = chain.trim().toUpperCase();
3079
- return normalized === String(chainId) || normalized === "BASE";
3617
+ assertTransaction(transaction, state, chainId) {
3618
+ 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)) {
3619
+ throw this.invalidResponse("transaction");
3620
+ }
3621
+ }
3622
+ assertAgent(agent) {
3623
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
3624
+ throw this.invalidResponse("agent");
3625
+ }
3626
+ }
3627
+ isOwneyAgent(agent) {
3628
+ return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
3629
+ }
3630
+ assetForAgent(agent) {
3631
+ for (const asset of ["USDC", "WETH"]) {
3632
+ if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
3633
+ return asset;
3634
+ }
3635
+ }
3636
+ return null;
3080
3637
  }
3081
3638
  isTransactionHash(value) {
3082
3639
  return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
@@ -3098,8 +3655,12 @@ var YieldseekerAgent = class {
3098
3655
  if (asset !== "USDC" && asset !== "WETH") {
3099
3656
  throw new OwneyError(
3100
3657
  "ASSET_UNSUPPORTED",
3101
- `Yieldseeker does not support asset ${asset}.`,
3102
- { asset, supportedAssets: ["USDC", "WETH"] },
3658
+ `Yieldseeker does not support asset ${asset} in the Owney rollout.`,
3659
+ {
3660
+ asset,
3661
+ supportedAssets: ["USDC", "WETH"],
3662
+ providerAlsoAdvertises: ["cbBTC"]
3663
+ },
3103
3664
  this.id
3104
3665
  );
3105
3666
  }
@@ -3212,7 +3773,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
3212
3773
  }
3213
3774
 
3214
3775
  // src/lib/helpers/withdraw-helper.ts
3215
- import { parseUnits as parseUnits2 } from "viem";
3776
+ import { parseUnits } from "viem";
3216
3777
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
3217
3778
  const target = asset.toUpperCase();
3218
3779
  return agents.map((agent) => {
@@ -3220,7 +3781,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
3220
3781
  const tokenBalance = agentBalance?.tokens.find(
3221
3782
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
3222
3783
  );
3223
- let balance = tokenBalance ? parseUnits2(tokenBalance.amount, decimals) : 0n;
3784
+ let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
3224
3785
  if (agent.balanceComposition === "tokens-plus-positions") {
3225
3786
  const chainNameById = {
3226
3787
  1: "ETHEREUM",
@@ -3239,7 +3800,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
3239
3800
  } catch {
3240
3801
  }
3241
3802
  }
3242
- balance += parseUnits2(position2.amount, decimals);
3803
+ balance += parseUnits(position2.amount, decimals);
3243
3804
  }
3244
3805
  }
3245
3806
  return { agent, balance };
@@ -3321,11 +3882,11 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
3321
3882
  return Number.isFinite(total) && total > 0 ? total : 0;
3322
3883
  }
3323
3884
  const normalizedToken = tokenSymbol.toUpperCase();
3324
- return balance.tokens.reduce((total, token2) => {
3325
- if (Number(token2.chainId) !== chainId || String(token2.asset).toUpperCase() !== normalizedToken) {
3885
+ return balance.tokens.reduce((total, token) => {
3886
+ if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
3326
3887
  return total;
3327
3888
  }
3328
- const amount = Number(token2.amount);
3889
+ const amount = Number(token.amount);
3329
3890
  return Number.isFinite(amount) && amount > 0 ? total + amount : total;
3330
3891
  }, 0);
3331
3892
  }
@@ -3399,8 +3960,8 @@ function makeSponsoredWethCallback(deps) {
3399
3960
  return makeVerificationAwareDepositCallback(
3400
3961
  async (smartWallet, chainId, amount, verification) => {
3401
3962
  const cid = chainId;
3402
- const token2 = deps.tokenAddressByChain[cid];
3403
- if (!token2) {
3963
+ const token = deps.tokenAddressByChain[cid];
3964
+ if (!token) {
3404
3965
  throw new OwneyError(
3405
3966
  "CHAIN_UNSUPPORTED",
3406
3967
  `No sponsored WETH configured for chain ${chainId}`
@@ -3411,12 +3972,12 @@ function makeSponsoredWethCallback(deps) {
3411
3972
  const wallet = deps.getWalletClient(cid);
3412
3973
  await ensureWalletOnChain(pub, wallet, cid);
3413
3974
  try {
3414
- const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
3975
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
3415
3976
  if (balance < amountWei) {
3416
3977
  throw new OwneyError(
3417
3978
  "DEPOSIT_INSUFFICIENT_BALANCE",
3418
3979
  "Insufficient WETH balance for this deposit.",
3419
- { token: token2, chainId: cid, balance: balance.toString(), amount }
3980
+ { token, chainId: cid, balance: balance.toString(), amount }
3420
3981
  );
3421
3982
  }
3422
3983
  } catch (err) {
@@ -3428,14 +3989,14 @@ function makeSponsoredWethCallback(deps) {
3428
3989
  }
3429
3990
  const allowance = await readPermit2Allowance(
3430
3991
  pub,
3431
- token2,
3992
+ token,
3432
3993
  deps.ownerAddress
3433
3994
  );
3434
3995
  if (allowance < amountWei) {
3435
3996
  throw new OwneyError(
3436
3997
  "PERMIT2_APPROVAL_REQUIRED",
3437
3998
  "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
3438
- { token: token2, chainId: cid, allowance: allowance.toString(), amount }
3999
+ { token, chainId: cid, allowance: allowance.toString(), amount }
3439
4000
  );
3440
4001
  }
3441
4002
  const relayer = await get({
@@ -3450,7 +4011,7 @@ function makeSponsoredWethCallback(deps) {
3450
4011
  const typedData = buildPermitTransferFromTypedData({
3451
4012
  chainId: cid,
3452
4013
  message: {
3453
- permitted: { token: token2, amount: amountWei },
4014
+ permitted: { token, amount: amountWei },
3454
4015
  spender: relayer,
3455
4016
  nonce,
3456
4017
  deadline
@@ -3467,13 +4028,17 @@ function makeSponsoredWethCallback(deps) {
3467
4028
  ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
3468
4029
  body: {
3469
4030
  chainId: cid,
3470
- token: token2,
4031
+ token,
3471
4032
  from: deps.ownerAddress,
3472
4033
  to: smartWallet,
3473
4034
  amount,
3474
4035
  nonce: nonce.toString(),
3475
4036
  deadline: deadline.toString(),
3476
- signature
4037
+ signature,
4038
+ ...verification?.agentId === "yieldseeker" ? {
4039
+ yieldseekerUserId: verification.userId,
4040
+ yieldseekerAgentId: verification.yieldseekerAgentId
4041
+ } : {}
3477
4042
  }
3478
4043
  });
3479
4044
  return result.txHash;
@@ -3482,7 +4047,7 @@ function makeSponsoredWethCallback(deps) {
3482
4047
  }
3483
4048
 
3484
4049
  // src/lib/sponsored-calls-deposit.ts
3485
- import { encodeFunctionData, erc20Abi, toHex } from "viem";
4050
+ import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi2, toHex } from "viem";
3486
4051
  var DEFAULT_POLL_INTERVAL_MS = 1500;
3487
4052
  var DEFAULT_MAX_POLLS = 30;
3488
4053
  async function paymasterSupported(provider, owner, chainId) {
@@ -3508,10 +4073,10 @@ function makeSponsoredCallsCallback(deps) {
3508
4073
  }
3509
4074
  return new URL(configured, origin).toString();
3510
4075
  };
3511
- return async (smartWallet, chainId, amount) => {
4076
+ return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
3512
4077
  const cid = chainId;
3513
- const token2 = deps.tokenAddressByChain[cid];
3514
- if (!token2) {
4078
+ const token = deps.tokenAddressByChain[cid];
4079
+ if (!token) {
3515
4080
  throw new OwneyError(
3516
4081
  "CHAIN_UNSUPPORTED",
3517
4082
  `No sponsored token configured for chain ${chainId}`
@@ -3524,11 +4089,37 @@ function makeSponsoredCallsCallback(deps) {
3524
4089
  { chainId }
3525
4090
  );
3526
4091
  }
3527
- const data = encodeFunctionData({
3528
- abi: erc20Abi,
4092
+ const data = encodeFunctionData2({
4093
+ abi: erc20Abi2,
3529
4094
  functionName: "transfer",
3530
4095
  args: [smartWallet, BigInt(amount)]
3531
4096
  });
4097
+ let paymasterUrl = absolutePaymasterUrl();
4098
+ if (verification?.agentId === "yieldseeker") {
4099
+ if (chainId !== 8453) {
4100
+ throw new OwneyError(
4101
+ "CHAIN_UNSUPPORTED",
4102
+ `Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
4103
+ );
4104
+ }
4105
+ const { intent } = await postPaymasterIntent({
4106
+ baseUrl: deps.routingApiBaseUrl,
4107
+ apiKey: deps.apiKey,
4108
+ yieldseekerSignature: verification.signature,
4109
+ body: {
4110
+ chainId,
4111
+ token,
4112
+ from: deps.ownerAddress,
4113
+ to: smartWallet,
4114
+ amount,
4115
+ yieldseekerUserId: verification.userId,
4116
+ yieldseekerAgentId: verification.yieldseekerAgentId
4117
+ }
4118
+ });
4119
+ const url = new URL(paymasterUrl);
4120
+ url.searchParams.set("owneyIntent", intent);
4121
+ paymasterUrl = url.toString();
4122
+ }
3532
4123
  const sendResult = await deps.provider.request({
3533
4124
  method: "wallet_sendCalls",
3534
4125
  params: [
@@ -3537,9 +4128,9 @@ function makeSponsoredCallsCallback(deps) {
3537
4128
  from: deps.ownerAddress,
3538
4129
  chainId: toHex(chainId),
3539
4130
  atomicRequired: false,
3540
- calls: [{ to: token2, value: "0x0", data }],
4131
+ calls: [{ to: token, value: "0x0", data }],
3541
4132
  capabilities: {
3542
- paymasterService: { url: absolutePaymasterUrl() }
4133
+ paymasterService: { url: paymasterUrl }
3543
4134
  }
3544
4135
  }
3545
4136
  ]
@@ -3569,17 +4160,17 @@ function makeSponsoredCallsCallback(deps) {
3569
4160
  `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
3570
4161
  { chainId, callsId }
3571
4162
  );
3572
- };
4163
+ });
3573
4164
  }
3574
4165
 
3575
4166
  // src/client.ts
3576
4167
  function encodeMultiAgentCursor(map) {
3577
4168
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
3578
4169
  }
3579
- function decodeMultiAgentCursor(token2) {
4170
+ function decodeMultiAgentCursor(token) {
3580
4171
  let parsed;
3581
4172
  try {
3582
- const json = Buffer.from(token2, "base64").toString("utf8");
4173
+ const json = Buffer.from(token, "base64").toString("utf8");
3583
4174
  parsed = JSON.parse(json);
3584
4175
  } catch {
3585
4176
  throw new InvalidHistoryCursorError(
@@ -3768,6 +4359,8 @@ var OwneySDK = class {
3768
4359
  if (!onApproved && cached) return cached;
3769
4360
  const provider = this.requireConnectedProvider();
3770
4361
  const callback = makeSponsoredCallsCallback({
4362
+ apiKey: this.apiKey,
4363
+ routingApiBaseUrl: this.routingApiBaseUrl,
3771
4364
  provider,
3772
4365
  ownerAddress: this.state.walletAddress,
3773
4366
  paymasterServiceUrl: this.paymasterServiceUrl,
@@ -4210,15 +4803,15 @@ var OwneySDK = class {
4210
4803
  agent,
4211
4804
  amount: i === agents.length - 1 ? perAgent + remainder : perAgent
4212
4805
  }));
4213
- const valid = splits.filter(
4806
+ const valid2 = splits.filter(
4214
4807
  (s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
4215
4808
  );
4216
- if (valid.length === agents.length) {
4809
+ if (valid2.length === agents.length) {
4217
4810
  return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
4218
4811
  }
4219
4812
  return this.splitDepositAmount(
4220
4813
  totalAmount,
4221
- valid.map((s) => s.agent),
4814
+ valid2.map((s) => s.agent),
4222
4815
  chainId,
4223
4816
  asset,
4224
4817
  exempt
@@ -4251,10 +4844,10 @@ var OwneySDK = class {
4251
4844
  try {
4252
4845
  const balance = await agent.getBalances(state, chainId);
4253
4846
  const target = asset.toLowerCase();
4254
- const token2 = balance.tokens.find(
4847
+ const token = balance.tokens.find(
4255
4848
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
4256
4849
  );
4257
- return !!token2 && Number(token2.amount) > 0;
4850
+ return !!token && Number(token.amount) > 0;
4258
4851
  } catch {
4259
4852
  return false;
4260
4853
  }
@@ -4305,9 +4898,9 @@ var OwneySDK = class {
4305
4898
  const { asset, amount, agentId } = options;
4306
4899
  const state = this.requireState();
4307
4900
  const chainId = this.requireChainId();
4308
- const token2 = asset;
4901
+ const token = asset;
4309
4902
  const assetInfo = SupportedAssets.find(
4310
- (a) => a.chainId === chainId && a.symbol === token2
4903
+ (a) => a.chainId === chainId && a.symbol === token
4311
4904
  );
4312
4905
  if (!assetInfo) {
4313
4906
  throw new OwneyError(
@@ -4321,7 +4914,7 @@ var OwneySDK = class {
4321
4914
  return withFailureReporting(
4322
4915
  this.apiKey,
4323
4916
  agent.id,
4324
- () => agent.withdraw(state, chainId, token2, amount),
4917
+ () => agent.withdraw(state, chainId, token, amount),
4325
4918
  this.routingApiBaseUrl
4326
4919
  );
4327
4920
  }
@@ -4331,7 +4924,7 @@ var OwneySDK = class {
4331
4924
  const agentErrors2 = {};
4332
4925
  for (const agent of eligibleAgents) {
4333
4926
  try {
4334
- results2[agent.id] = await agent.withdraw(state, chainId, token2);
4927
+ results2[agent.id] = await agent.withdraw(state, chainId, token);
4335
4928
  } catch (err) {
4336
4929
  console.error(`withdraw failed for agent "${agent.id}":`, err);
4337
4930
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
@@ -4421,7 +5014,7 @@ var OwneySDK = class {
4421
5014
  results[p.agent.id] = await p.agent.withdraw(
4422
5015
  state,
4423
5016
  chainId,
4424
- token2,
5017
+ token,
4425
5018
  p.planned.toString()
4426
5019
  );
4427
5020
  } catch (err) {
@@ -4772,8 +5365,8 @@ var OwneySDK = class {
4772
5365
  const state = this.requireState();
4773
5366
  const chainId = this.requireChainId();
4774
5367
  this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
4775
- const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
4776
- if (!token2) {
5368
+ const token = SPONSORED_WETH_BY_CHAIN[chainId];
5369
+ if (!token) {
4777
5370
  throw new OwneyError(
4778
5371
  "CHAIN_UNSUPPORTED",
4779
5372
  `No sponsored WETH on chain ${chainId}`
@@ -4786,7 +5379,7 @@ var OwneySDK = class {
4786
5379
  transport: custom3(provider)
4787
5380
  });
4788
5381
  const hash = await wallet.writeContract({
4789
- address: token2,
5382
+ address: token,
4790
5383
  abi: ERC20_ALLOWANCE_ABI,
4791
5384
  functionName: "approve",
4792
5385
  args: [PERMIT2_ADDRESS, MAX_UINT256],
@@ -4911,8 +5504,8 @@ import { SiweMessage as SiweMessage2 } from "siwe";
4911
5504
  import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
4912
5505
 
4913
5506
  // src/agents/zyfai/zyfai.siwx-cache.ts
4914
- var KEY_PREFIX3 = "owney.siwx.session";
4915
- var storage3 = () => {
5507
+ var KEY_PREFIX4 = "owney.siwx.session";
5508
+ var storage4 = () => {
4916
5509
  if (typeof window === "undefined") return null;
4917
5510
  try {
4918
5511
  return window.localStorage;
@@ -4920,8 +5513,8 @@ var storage3 = () => {
4920
5513
  return null;
4921
5514
  }
4922
5515
  };
4923
- var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
4924
- var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
5516
+ var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
5517
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
4925
5518
  var memorySiwxSessions = /* @__PURE__ */ new Map();
4926
5519
  var readLegacySiwxSession = (store, address) => {
4927
5520
  if (!store) return null;
@@ -4953,16 +5546,16 @@ var readLegacySiwxSession = (store, address) => {
4953
5546
  var readSiwxSession = (address, chainId) => {
4954
5547
  if (typeof window === "undefined") return null;
4955
5548
  const key2 = buildKey3(address);
4956
- const store = storage3();
4957
- let raw = null;
5549
+ const store = storage4();
5550
+ let raw2 = null;
4958
5551
  try {
4959
- raw = store?.getItem(key2) ?? null;
5552
+ raw2 = store?.getItem(key2) ?? null;
4960
5553
  } catch {
4961
- raw = null;
5554
+ raw2 = null;
4962
5555
  }
4963
- if (raw) {
5556
+ if (raw2) {
4964
5557
  try {
4965
- return JSON.parse(raw);
5558
+ return JSON.parse(raw2);
4966
5559
  } catch {
4967
5560
  memorySiwxSessions.delete(key2);
4968
5561
  try {
@@ -4983,7 +5576,7 @@ var writeSiwxSession = (address, _chainId, session) => {
4983
5576
  if (typeof window === "undefined") return;
4984
5577
  const key2 = buildKey3(address);
4985
5578
  memorySiwxSessions.set(key2, session);
4986
- const store = storage3();
5579
+ const store = storage4();
4987
5580
  try {
4988
5581
  store?.setItem(key2, JSON.stringify(session));
4989
5582
  } catch {
@@ -4992,7 +5585,7 @@ var writeSiwxSession = (address, _chainId, session) => {
4992
5585
  var clearSiwxSession = (address, _chainId) => {
4993
5586
  const key2 = buildKey3(address);
4994
5587
  memorySiwxSessions.delete(key2);
4995
- const store = storage3();
5588
+ const store = storage4();
4996
5589
  try {
4997
5590
  store?.removeItem(key2);
4998
5591
  } catch {