@owney/sdk 0.7.25-beta.17 → 0.7.25-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3769,6 +3769,35 @@ function mapYieldseekerEarnings(contexts) {
3769
3769
  tokens
3770
3770
  };
3771
3771
  }
3772
+ function mapYieldseekerDailyEarnings(walletAddress, contexts, days, now = Date.now()) {
3773
+ const today = new Date(now).toISOString().slice(0, 10);
3774
+ const dayCount = { "7D": 7, "14D": 14, "30D": 30 }[days];
3775
+ const cutoff = new Date(Date.parse(today) - dayCount * 864e5).toISOString().slice(0, 10);
3776
+ const assets = contexts.flatMap((context) => {
3777
+ const snapshots = context.historic?.dailyYieldSnapshots;
3778
+ if (!Array.isArray(snapshots)) invalid("historic position", "missing daily yield snapshots");
3779
+ const byDate = /* @__PURE__ */ new Map();
3780
+ for (const snapshot of snapshots) {
3781
+ const time = Date.parse(snapshot.date);
3782
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(snapshot.date) || !Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== snapshot.date) {
3783
+ invalid("historic position", "expected a UTC snapshot date");
3784
+ }
3785
+ if (snapshot.date > today) continue;
3786
+ if (snapshot.baseAssetDecimals !== YIELDSEEKER_ASSET_METADATA[context.asset].decimals) {
3787
+ invalid("historic position", "base asset decimals do not match the selected asset");
3788
+ }
3789
+ const amount = Number(decimal(snapshot.cumulativeYieldBase, snapshot.baseAssetDecimals, "historic position"));
3790
+ if (!Number.isFinite(amount)) invalid("historic position", "expected finite cumulative yield");
3791
+ byDate.set(snapshot.date, { date: snapshot.date, amount });
3792
+ }
3793
+ const sorted = [...byDate.values()].sort((a, b) => a.date.localeCompare(b.date));
3794
+ const window2 = sorted.filter((point) => point.date >= cutoff);
3795
+ if (!window2.length) return [];
3796
+ const opening = sorted.filter((point) => point.date < cutoff).at(-1);
3797
+ return [{ asset: context.asset, points: opening ? [opening, ...window2] : window2 }];
3798
+ }).sort((a, b) => a.asset.localeCompare(b.asset));
3799
+ return { walletAddress, chainId: 8453, assets };
3800
+ }
3772
3801
  function apyForDays(context, days, now) {
3773
3802
  if (days === "7D") return percent(context.snapshot.apy7d);
3774
3803
  if (days === "30D") return percent(context.snapshot.apy30d);
@@ -4273,6 +4302,14 @@ function sponsorError(message, safeToFallback, details = {}) {
4273
4302
  "yieldseeker"
4274
4303
  );
4275
4304
  }
4305
+ function assertIntentFresh(expiresAt) {
4306
+ if (!Number.isFinite(expiresAt) || Date.now() + 5e3 >= expiresAt * 1e3)
4307
+ throw sponsorError(
4308
+ "The withdrawal sponsorship request expired. Please try again.",
4309
+ true,
4310
+ { failureCode: "SPONSOR_INTENT_EXPIRED" }
4311
+ );
4312
+ }
4276
4313
  function providerReason(value) {
4277
4314
  if (typeof value !== "string") return void 0;
4278
4315
  return value.replace(/https?:\/\/\S+/gi, "[url]").replace(/0x[0-9a-fA-F]{40,}/g, "[hex]").replace(/[A-Za-z0-9_+=\/-]{48,}/g, "[token]").slice(0, 300);
@@ -4561,6 +4598,7 @@ async function submitEoa(input) {
4561
4598
  }
4562
4599
  const userOpHash = operationHash(operation);
4563
4600
  await assertOwnerSession(input.state, input.owner);
4601
+ assertIntentFresh(input.expiresAt);
4564
4602
  const wallet = (0, import_viem11.createWalletClient)({
4565
4603
  account: input.owner,
4566
4604
  chain: import_chains3.base,
@@ -4569,6 +4607,7 @@ async function submitEoa(input) {
4569
4607
  const signature = await wallet.signMessage({ message: { raw: userOpHash } });
4570
4608
  operation = { ...operation, signature };
4571
4609
  await assertOwnerSession(input.state, input.owner);
4610
+ assertIntentFresh(input.expiresAt);
4572
4611
  const userOperation = {
4573
4612
  sender: operation.sender,
4574
4613
  nonce: operation.nonce,
@@ -4646,6 +4685,7 @@ async function submitSmart(input) {
4646
4685
  throw sponsorError("This account cannot use a paymaster on Base.", true);
4647
4686
  await assertOwnerSession(input.state, input.owner);
4648
4687
  let sent;
4688
+ assertIntentFresh(input.expiresAt);
4649
4689
  const requestedId = (0, import_viem11.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
4650
4690
  savePending(input.onPending, { callsId: requestedId }, true);
4651
4691
  await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
@@ -4744,7 +4784,8 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
4744
4784
  action: "withdraw",
4745
4785
  refreshYieldseekerSignature: input.refreshYieldseekerSignature
4746
4786
  });
4747
- const args = { ...input, owner, rpcUrl };
4787
+ assertIntentFresh(intent.expiresAt);
4788
+ const args = { ...input, owner, rpcUrl, expiresAt: intent.expiresAt };
4748
4789
  return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
4749
4790
  }
4750
4791
  async function submitYieldseekerSponsoredUpgrade(input) {
@@ -4764,7 +4805,8 @@ async function submitYieldseekerSponsoredUpgrade(input) {
4764
4805
  });
4765
4806
  if (intent.ownerPath !== "smart")
4766
4807
  throw sponsorError("V1 upgrade sponsorship requires a Base Account owner.", true);
4767
- return submitSmart({ ...input, owner, rpcUrl });
4808
+ assertIntentFresh(intent.expiresAt);
4809
+ return submitSmart({ ...input, owner, rpcUrl, expiresAt: intent.expiresAt });
4768
4810
  }
4769
4811
 
4770
4812
  // src/agents/yieldseeker/yieldseeker.agent.ts
@@ -6097,6 +6139,17 @@ var YieldseekerAgent = class {
6097
6139
  });
6098
6140
  return mapYieldseekerApy(state.walletAddress, contexts, days);
6099
6141
  }
6142
+ async getDailyEarnings(state, chainId, days, tokenSymbol) {
6143
+ this.assertChain(chainId);
6144
+ const asset = tokenSymbol?.toUpperCase();
6145
+ if (asset !== void 0) this.assertAsset(asset);
6146
+ const contexts = await this.loadActivityPortfolio(state, chainId, {
6147
+ ...asset ? { asset } : {},
6148
+ historic: true,
6149
+ includePositions: false
6150
+ });
6151
+ return mapYieldseekerDailyEarnings(state.walletAddress, contexts, days);
6152
+ }
6100
6153
  async getHistory(state, chainId, options) {
6101
6154
  this.assertChain(chainId);
6102
6155
  const asset = options?.tokenSymbol?.toUpperCase();
@@ -6607,7 +6660,7 @@ var YieldseekerAgent = class {
6607
6660
  }
6608
6661
  async loadPortfolioParts(state, chainId, context, options, contextKey) {
6609
6662
  const [positions, historic, actions] = await Promise.all([
6610
- this.cachedRead(
6663
+ options.includePositions === false ? Promise.resolve({ yieldPositions: [] }) : this.cachedRead(
6611
6664
  `positions:${contextKey}`,
6612
6665
  this.portfolioCacheMs(contextKey),
6613
6666
  async () => {
@@ -7230,6 +7283,43 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
7230
7283
  return out;
7231
7284
  }
7232
7285
 
7286
+ // src/lib/helpers/daily-earnings-helper.ts
7287
+ function aggregateDailyEarnings(series, tokenSymbol) {
7288
+ const wanted = tokenSymbol?.toUpperCase();
7289
+ const byAsset = /* @__PURE__ */ new Map();
7290
+ for (const account of series) {
7291
+ for (const { asset, points } of account.assets) {
7292
+ const symbol = asset.toUpperCase();
7293
+ if (!points.length || wanted && symbol !== wanted) continue;
7294
+ const components = byAsset.get(symbol) ?? [];
7295
+ components.push([...points].sort((a, b) => a.date.localeCompare(b.date)));
7296
+ byAsset.set(symbol, components);
7297
+ }
7298
+ }
7299
+ return [...byAsset.entries()].map(([asset, components]) => {
7300
+ const dates = [...new Set(components.flatMap((points2) => points2.map((point) => point.date)))].sort();
7301
+ const cursors = components.map((points2) => ({
7302
+ index: 0,
7303
+ // Providers can have different lifetime/window offsets and start dates.
7304
+ // Remove each opening reading before combining their earned amounts.
7305
+ baseline: points2[0].amount,
7306
+ current: points2[0].amount
7307
+ }));
7308
+ const points = dates.map((date) => {
7309
+ let amount = 0;
7310
+ components.forEach((component, index) => {
7311
+ const cursor = cursors[index];
7312
+ while (cursor.index < component.length && component[cursor.index].date <= date) {
7313
+ cursor.current = component[cursor.index++].amount;
7314
+ }
7315
+ amount += cursor.current - cursor.baseline;
7316
+ });
7317
+ return { date, amount };
7318
+ });
7319
+ return { asset, points };
7320
+ }).sort((a, b) => a.asset.localeCompare(b.asset));
7321
+ }
7322
+
7233
7323
  // src/client.ts
7234
7324
  var import_viem17 = require("viem");
7235
7325
  var import_chains5 = require("viem/chains");
@@ -9612,10 +9702,8 @@ var OwneySDK = class {
9612
9702
  * @returns {AccountAgentApy} for a single agent, or {OwneyAccountApy} with totalApy and per-agent breakdown
9613
9703
  */
9614
9704
  /**
9615
- * Daily cumulative NET earnings for the selected chain/asset, backing the
9616
- * "recent earnings" subline. Net is computed as Zyfai's own
9617
- * `lifetime + unrealized + current x 0.9`, so the figure reconciles with the
9618
- * balance headline rather than reading ~11% high. (ROUT-452)
9705
+ * Daily cumulative earnings in the selected asset's units, backing the
9706
+ * "recent earnings" subline. Each adapter uses its provider's earnings basis.
9619
9707
  *
9620
9708
  * Unlike getAccountApy this does NOT blend across agents: earnings are
9621
9709
  * summed, not weighted, and an agent that fails to report must not silently
@@ -9648,7 +9736,7 @@ var OwneySDK = class {
9648
9736
  const entries = [...this.getActiveAgents().entries()].filter(
9649
9737
  ([, agent]) => agent.getDailyEarnings
9650
9738
  );
9651
- const series = await Promise.all(
9739
+ const settled = await Promise.allSettled(
9652
9740
  entries.map(
9653
9741
  ([, agent]) => this.readAgent(
9654
9742
  agent,
@@ -9658,23 +9746,15 @@ var OwneySDK = class {
9658
9746
  )
9659
9747
  )
9660
9748
  );
9661
- const byAsset = /* @__PURE__ */ new Map();
9662
- for (const s of series) {
9663
- for (const { asset, points } of s.assets) {
9664
- const byDate = byAsset.get(asset) ?? /* @__PURE__ */ new Map();
9665
- for (const point of points) {
9666
- byDate.set(point.date, (byDate.get(point.date) ?? 0) + point.amount);
9667
- }
9668
- byAsset.set(asset, byDate);
9669
- }
9749
+ const series = settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
9750
+ if (entries.length && !series.length) {
9751
+ const failure = settled.find((result) => result.status === "rejected");
9752
+ if (failure?.status === "rejected") throw failure.reason;
9670
9753
  }
9671
9754
  return {
9672
9755
  walletAddress: series[0]?.walletAddress ?? state.walletAddress ?? "",
9673
9756
  chainId,
9674
- assets: [...byAsset.entries()].map(([asset, byDate]) => ({
9675
- asset,
9676
- points: [...byDate.entries()].map(([date, amount]) => ({ date, amount })).sort((a, b) => a.date.localeCompare(b.date))
9677
- })).sort((a, b) => a.asset.localeCompare(b.asset))
9757
+ assets: aggregateDailyEarnings(series, tokenSymbol)
9678
9758
  };
9679
9759
  }
9680
9760
  async getAccountApy({
package/dist/index.d.cts CHANGED
@@ -169,9 +169,9 @@ interface IAgent {
169
169
  */
170
170
  tokenSymbol?: string): Promise<AccountAgentApy>;
171
171
  /**
172
- * Daily cumulative NET earnings for the window, scoped to a chain/asset.
173
- * Backs the "recent earnings" subline, whose figure must reconcile with the
174
- * balance headline directly above it. (ROUT-452)
172
+ * Daily cumulative earnings in token units, scoped to a chain/asset, using
173
+ * the provider's earnings basis. Include an opening snapshot when available
174
+ * so the "recent earnings" subline can difference the endpoints. (ROUT-452)
175
175
  */
176
176
  getDailyEarnings?(state: ConnectionState, chainId: number, days: LookbackDays, tokenSymbol?: string): Promise<AccountDailyEarnings>;
177
177
  getHistory(state: ConnectionState, chainId: number, options?: HistoryFilters): Promise<OwneyAgentHistory>;
@@ -658,15 +658,15 @@ interface AgentApy {
658
658
  interface OwneyAgentApy {
659
659
  agentApy: Record<AgentId, AgentApy>;
660
660
  }
661
- /** One day's cumulative NET earnings for a single asset. */
661
+ /** One day's cumulative earnings for a single asset. */
662
662
  interface DailyEarningsPoint {
663
663
  /** ISO date string for the day, e.g. "2026-09-01". */
664
664
  date: string;
665
665
  /**
666
- * Earnings accumulated from the start of the returned series up to and
667
- * including this day, net of the agent's performance fee, denominated in the
668
- * asset's own units. A running total of daily deltas, so it measures the
669
- * window — not the wallet's lifetime.
666
+ * Cumulative earnings in the asset's own units, using the provider's earnings
667
+ * basis. A provider may return lifetime totals or a running window total;
668
+ * difference the last and opening points to measure earnings over the window.
669
+ * Aggregate series normalize each component's opening value before summing.
670
670
  */
671
671
  amount: number;
672
672
  }
@@ -675,8 +675,9 @@ interface AssetDailyEarnings {
675
675
  /** Asset symbol, e.g. "USDC" / "WETH". */
676
676
  asset: string;
677
677
  /**
678
- * Ascending by date. Days the wallet held nothing of this asset are absent
679
- * rather than zero — a zero would read as "earned nothing that day".
678
+ * Ascending by date, including an opening snapshot before the requested
679
+ * window when available. Provider gaps remain absent; aggregates carry the
680
+ * most recent cumulative value across dates reported by other providers.
680
681
  */
681
682
  points: DailyEarningsPoint[];
682
683
  }
@@ -954,10 +955,8 @@ declare class OwneySDK {
954
955
  * @returns {AccountAgentApy} for a single agent, or {OwneyAccountApy} with totalApy and per-agent breakdown
955
956
  */
956
957
  /**
957
- * Daily cumulative NET earnings for the selected chain/asset, backing the
958
- * "recent earnings" subline. Net is computed as Zyfai's own
959
- * `lifetime + unrealized + current x 0.9`, so the figure reconciles with the
960
- * balance headline rather than reading ~11% high. (ROUT-452)
958
+ * Daily cumulative earnings in the selected asset's units, backing the
959
+ * "recent earnings" subline. Each adapter uses its provider's earnings basis.
961
960
  *
962
961
  * Unlike getAccountApy this does NOT blend across agents: earnings are
963
962
  * summed, not weighted, and an agent that fails to report must not silently
@@ -1146,6 +1145,7 @@ declare class YieldseekerAgent implements IAgent {
1146
1145
  hasExistingDepositFunds(state: ConnectionState, chainId: number, asset: OwneySupportedTokens): Promise<true | undefined>;
1147
1146
  getEarnings(state: ConnectionState, chainId: number): Promise<AgentEarnings>;
1148
1147
  getAccountApy(state: ConnectionState, chainId: number, days: DailyApyDays, tokenSymbol?: string): Promise<AccountAgentApy>;
1148
+ getDailyEarnings(state: ConnectionState, chainId: number, days: LookbackDays, tokenSymbol?: string): Promise<AccountDailyEarnings>;
1149
1149
  getHistory(state: ConnectionState, chainId: number, options?: HistoryFilters): Promise<OwneyAgentHistory>;
1150
1150
  getUserProfile(state: ConnectionState, chainId: number): Promise<AgentUserProfile>;
1151
1151
  getAgentApy(days: DailyApyDays, options?: AgentApyOptions): Promise<AgentApy>;
package/dist/index.d.ts CHANGED
@@ -169,9 +169,9 @@ interface IAgent {
169
169
  */
170
170
  tokenSymbol?: string): Promise<AccountAgentApy>;
171
171
  /**
172
- * Daily cumulative NET earnings for the window, scoped to a chain/asset.
173
- * Backs the "recent earnings" subline, whose figure must reconcile with the
174
- * balance headline directly above it. (ROUT-452)
172
+ * Daily cumulative earnings in token units, scoped to a chain/asset, using
173
+ * the provider's earnings basis. Include an opening snapshot when available
174
+ * so the "recent earnings" subline can difference the endpoints. (ROUT-452)
175
175
  */
176
176
  getDailyEarnings?(state: ConnectionState, chainId: number, days: LookbackDays, tokenSymbol?: string): Promise<AccountDailyEarnings>;
177
177
  getHistory(state: ConnectionState, chainId: number, options?: HistoryFilters): Promise<OwneyAgentHistory>;
@@ -658,15 +658,15 @@ interface AgentApy {
658
658
  interface OwneyAgentApy {
659
659
  agentApy: Record<AgentId, AgentApy>;
660
660
  }
661
- /** One day's cumulative NET earnings for a single asset. */
661
+ /** One day's cumulative earnings for a single asset. */
662
662
  interface DailyEarningsPoint {
663
663
  /** ISO date string for the day, e.g. "2026-09-01". */
664
664
  date: string;
665
665
  /**
666
- * Earnings accumulated from the start of the returned series up to and
667
- * including this day, net of the agent's performance fee, denominated in the
668
- * asset's own units. A running total of daily deltas, so it measures the
669
- * window — not the wallet's lifetime.
666
+ * Cumulative earnings in the asset's own units, using the provider's earnings
667
+ * basis. A provider may return lifetime totals or a running window total;
668
+ * difference the last and opening points to measure earnings over the window.
669
+ * Aggregate series normalize each component's opening value before summing.
670
670
  */
671
671
  amount: number;
672
672
  }
@@ -675,8 +675,9 @@ interface AssetDailyEarnings {
675
675
  /** Asset symbol, e.g. "USDC" / "WETH". */
676
676
  asset: string;
677
677
  /**
678
- * Ascending by date. Days the wallet held nothing of this asset are absent
679
- * rather than zero — a zero would read as "earned nothing that day".
678
+ * Ascending by date, including an opening snapshot before the requested
679
+ * window when available. Provider gaps remain absent; aggregates carry the
680
+ * most recent cumulative value across dates reported by other providers.
680
681
  */
681
682
  points: DailyEarningsPoint[];
682
683
  }
@@ -954,10 +955,8 @@ declare class OwneySDK {
954
955
  * @returns {AccountAgentApy} for a single agent, or {OwneyAccountApy} with totalApy and per-agent breakdown
955
956
  */
956
957
  /**
957
- * Daily cumulative NET earnings for the selected chain/asset, backing the
958
- * "recent earnings" subline. Net is computed as Zyfai's own
959
- * `lifetime + unrealized + current x 0.9`, so the figure reconciles with the
960
- * balance headline rather than reading ~11% high. (ROUT-452)
958
+ * Daily cumulative earnings in the selected asset's units, backing the
959
+ * "recent earnings" subline. Each adapter uses its provider's earnings basis.
961
960
  *
962
961
  * Unlike getAccountApy this does NOT blend across agents: earnings are
963
962
  * summed, not weighted, and an agent that fails to report must not silently
@@ -1146,6 +1145,7 @@ declare class YieldseekerAgent implements IAgent {
1146
1145
  hasExistingDepositFunds(state: ConnectionState, chainId: number, asset: OwneySupportedTokens): Promise<true | undefined>;
1147
1146
  getEarnings(state: ConnectionState, chainId: number): Promise<AgentEarnings>;
1148
1147
  getAccountApy(state: ConnectionState, chainId: number, days: DailyApyDays, tokenSymbol?: string): Promise<AccountAgentApy>;
1148
+ getDailyEarnings(state: ConnectionState, chainId: number, days: LookbackDays, tokenSymbol?: string): Promise<AccountDailyEarnings>;
1149
1149
  getHistory(state: ConnectionState, chainId: number, options?: HistoryFilters): Promise<OwneyAgentHistory>;
1150
1150
  getUserProfile(state: ConnectionState, chainId: number): Promise<AgentUserProfile>;
1151
1151
  getAgentApy(days: DailyApyDays, options?: AgentApyOptions): Promise<AgentApy>;
package/dist/index.js CHANGED
@@ -3755,6 +3755,35 @@ function mapYieldseekerEarnings(contexts) {
3755
3755
  tokens
3756
3756
  };
3757
3757
  }
3758
+ function mapYieldseekerDailyEarnings(walletAddress, contexts, days, now = Date.now()) {
3759
+ const today = new Date(now).toISOString().slice(0, 10);
3760
+ const dayCount = { "7D": 7, "14D": 14, "30D": 30 }[days];
3761
+ const cutoff = new Date(Date.parse(today) - dayCount * 864e5).toISOString().slice(0, 10);
3762
+ const assets = contexts.flatMap((context) => {
3763
+ const snapshots = context.historic?.dailyYieldSnapshots;
3764
+ if (!Array.isArray(snapshots)) invalid("historic position", "missing daily yield snapshots");
3765
+ const byDate = /* @__PURE__ */ new Map();
3766
+ for (const snapshot of snapshots) {
3767
+ const time = Date.parse(snapshot.date);
3768
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(snapshot.date) || !Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== snapshot.date) {
3769
+ invalid("historic position", "expected a UTC snapshot date");
3770
+ }
3771
+ if (snapshot.date > today) continue;
3772
+ if (snapshot.baseAssetDecimals !== YIELDSEEKER_ASSET_METADATA[context.asset].decimals) {
3773
+ invalid("historic position", "base asset decimals do not match the selected asset");
3774
+ }
3775
+ const amount = Number(decimal(snapshot.cumulativeYieldBase, snapshot.baseAssetDecimals, "historic position"));
3776
+ if (!Number.isFinite(amount)) invalid("historic position", "expected finite cumulative yield");
3777
+ byDate.set(snapshot.date, { date: snapshot.date, amount });
3778
+ }
3779
+ const sorted = [...byDate.values()].sort((a, b) => a.date.localeCompare(b.date));
3780
+ const window2 = sorted.filter((point) => point.date >= cutoff);
3781
+ if (!window2.length) return [];
3782
+ const opening = sorted.filter((point) => point.date < cutoff).at(-1);
3783
+ return [{ asset: context.asset, points: opening ? [opening, ...window2] : window2 }];
3784
+ }).sort((a, b) => a.asset.localeCompare(b.asset));
3785
+ return { walletAddress, chainId: 8453, assets };
3786
+ }
3758
3787
  function apyForDays(context, days, now) {
3759
3788
  if (days === "7D") return percent(context.snapshot.apy7d);
3760
3789
  if (days === "30D") return percent(context.snapshot.apy30d);
@@ -4268,6 +4297,14 @@ function sponsorError(message, safeToFallback, details = {}) {
4268
4297
  "yieldseeker"
4269
4298
  );
4270
4299
  }
4300
+ function assertIntentFresh(expiresAt) {
4301
+ if (!Number.isFinite(expiresAt) || Date.now() + 5e3 >= expiresAt * 1e3)
4302
+ throw sponsorError(
4303
+ "The withdrawal sponsorship request expired. Please try again.",
4304
+ true,
4305
+ { failureCode: "SPONSOR_INTENT_EXPIRED" }
4306
+ );
4307
+ }
4271
4308
  function providerReason(value) {
4272
4309
  if (typeof value !== "string") return void 0;
4273
4310
  return value.replace(/https?:\/\/\S+/gi, "[url]").replace(/0x[0-9a-fA-F]{40,}/g, "[hex]").replace(/[A-Za-z0-9_+=\/-]{48,}/g, "[token]").slice(0, 300);
@@ -4556,6 +4593,7 @@ async function submitEoa(input) {
4556
4593
  }
4557
4594
  const userOpHash = operationHash(operation);
4558
4595
  await assertOwnerSession(input.state, input.owner);
4596
+ assertIntentFresh(input.expiresAt);
4559
4597
  const wallet = createWalletClient2({
4560
4598
  account: input.owner,
4561
4599
  chain: base3,
@@ -4564,6 +4602,7 @@ async function submitEoa(input) {
4564
4602
  const signature = await wallet.signMessage({ message: { raw: userOpHash } });
4565
4603
  operation = { ...operation, signature };
4566
4604
  await assertOwnerSession(input.state, input.owner);
4605
+ assertIntentFresh(input.expiresAt);
4567
4606
  const userOperation = {
4568
4607
  sender: operation.sender,
4569
4608
  nonce: operation.nonce,
@@ -4641,6 +4680,7 @@ async function submitSmart(input) {
4641
4680
  throw sponsorError("This account cannot use a paymaster on Base.", true);
4642
4681
  await assertOwnerSession(input.state, input.owner);
4643
4682
  let sent;
4683
+ assertIntentFresh(input.expiresAt);
4644
4684
  const requestedId = toHex(globalThis.crypto.getRandomValues(new Uint8Array(32)));
4645
4685
  savePending(input.onPending, { callsId: requestedId }, true);
4646
4686
  await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
@@ -4739,7 +4779,8 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
4739
4779
  action: "withdraw",
4740
4780
  refreshYieldseekerSignature: input.refreshYieldseekerSignature
4741
4781
  });
4742
- const args = { ...input, owner, rpcUrl };
4782
+ assertIntentFresh(intent.expiresAt);
4783
+ const args = { ...input, owner, rpcUrl, expiresAt: intent.expiresAt };
4743
4784
  return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
4744
4785
  }
4745
4786
  async function submitYieldseekerSponsoredUpgrade(input) {
@@ -4759,7 +4800,8 @@ async function submitYieldseekerSponsoredUpgrade(input) {
4759
4800
  });
4760
4801
  if (intent.ownerPath !== "smart")
4761
4802
  throw sponsorError("V1 upgrade sponsorship requires a Base Account owner.", true);
4762
- return submitSmart({ ...input, owner, rpcUrl });
4803
+ assertIntentFresh(intent.expiresAt);
4804
+ return submitSmart({ ...input, owner, rpcUrl, expiresAt: intent.expiresAt });
4763
4805
  }
4764
4806
 
4765
4807
  // src/agents/yieldseeker/yieldseeker.agent.ts
@@ -6092,6 +6134,17 @@ var YieldseekerAgent = class {
6092
6134
  });
6093
6135
  return mapYieldseekerApy(state.walletAddress, contexts, days);
6094
6136
  }
6137
+ async getDailyEarnings(state, chainId, days, tokenSymbol) {
6138
+ this.assertChain(chainId);
6139
+ const asset = tokenSymbol?.toUpperCase();
6140
+ if (asset !== void 0) this.assertAsset(asset);
6141
+ const contexts = await this.loadActivityPortfolio(state, chainId, {
6142
+ ...asset ? { asset } : {},
6143
+ historic: true,
6144
+ includePositions: false
6145
+ });
6146
+ return mapYieldseekerDailyEarnings(state.walletAddress, contexts, days);
6147
+ }
6095
6148
  async getHistory(state, chainId, options) {
6096
6149
  this.assertChain(chainId);
6097
6150
  const asset = options?.tokenSymbol?.toUpperCase();
@@ -6602,7 +6655,7 @@ var YieldseekerAgent = class {
6602
6655
  }
6603
6656
  async loadPortfolioParts(state, chainId, context, options, contextKey) {
6604
6657
  const [positions, historic, actions] = await Promise.all([
6605
- this.cachedRead(
6658
+ options.includePositions === false ? Promise.resolve({ yieldPositions: [] }) : this.cachedRead(
6606
6659
  `positions:${contextKey}`,
6607
6660
  this.portfolioCacheMs(contextKey),
6608
6661
  async () => {
@@ -7225,6 +7278,43 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
7225
7278
  return out;
7226
7279
  }
7227
7280
 
7281
+ // src/lib/helpers/daily-earnings-helper.ts
7282
+ function aggregateDailyEarnings(series, tokenSymbol) {
7283
+ const wanted = tokenSymbol?.toUpperCase();
7284
+ const byAsset = /* @__PURE__ */ new Map();
7285
+ for (const account of series) {
7286
+ for (const { asset, points } of account.assets) {
7287
+ const symbol = asset.toUpperCase();
7288
+ if (!points.length || wanted && symbol !== wanted) continue;
7289
+ const components = byAsset.get(symbol) ?? [];
7290
+ components.push([...points].sort((a, b) => a.date.localeCompare(b.date)));
7291
+ byAsset.set(symbol, components);
7292
+ }
7293
+ }
7294
+ return [...byAsset.entries()].map(([asset, components]) => {
7295
+ const dates = [...new Set(components.flatMap((points2) => points2.map((point) => point.date)))].sort();
7296
+ const cursors = components.map((points2) => ({
7297
+ index: 0,
7298
+ // Providers can have different lifetime/window offsets and start dates.
7299
+ // Remove each opening reading before combining their earned amounts.
7300
+ baseline: points2[0].amount,
7301
+ current: points2[0].amount
7302
+ }));
7303
+ const points = dates.map((date) => {
7304
+ let amount = 0;
7305
+ components.forEach((component, index) => {
7306
+ const cursor = cursors[index];
7307
+ while (cursor.index < component.length && component[cursor.index].date <= date) {
7308
+ cursor.current = component[cursor.index++].amount;
7309
+ }
7310
+ amount += cursor.current - cursor.baseline;
7311
+ });
7312
+ return { date, amount };
7313
+ });
7314
+ return { asset, points };
7315
+ }).sort((a, b) => a.asset.localeCompare(b.asset));
7316
+ }
7317
+
7228
7318
  // src/client.ts
7229
7319
  import {
7230
7320
  parseUnits as parseUnits3,
@@ -9616,10 +9706,8 @@ var OwneySDK = class {
9616
9706
  * @returns {AccountAgentApy} for a single agent, or {OwneyAccountApy} with totalApy and per-agent breakdown
9617
9707
  */
9618
9708
  /**
9619
- * Daily cumulative NET earnings for the selected chain/asset, backing the
9620
- * "recent earnings" subline. Net is computed as Zyfai's own
9621
- * `lifetime + unrealized + current x 0.9`, so the figure reconciles with the
9622
- * balance headline rather than reading ~11% high. (ROUT-452)
9709
+ * Daily cumulative earnings in the selected asset's units, backing the
9710
+ * "recent earnings" subline. Each adapter uses its provider's earnings basis.
9623
9711
  *
9624
9712
  * Unlike getAccountApy this does NOT blend across agents: earnings are
9625
9713
  * summed, not weighted, and an agent that fails to report must not silently
@@ -9652,7 +9740,7 @@ var OwneySDK = class {
9652
9740
  const entries = [...this.getActiveAgents().entries()].filter(
9653
9741
  ([, agent]) => agent.getDailyEarnings
9654
9742
  );
9655
- const series = await Promise.all(
9743
+ const settled = await Promise.allSettled(
9656
9744
  entries.map(
9657
9745
  ([, agent]) => this.readAgent(
9658
9746
  agent,
@@ -9662,23 +9750,15 @@ var OwneySDK = class {
9662
9750
  )
9663
9751
  )
9664
9752
  );
9665
- const byAsset = /* @__PURE__ */ new Map();
9666
- for (const s of series) {
9667
- for (const { asset, points } of s.assets) {
9668
- const byDate = byAsset.get(asset) ?? /* @__PURE__ */ new Map();
9669
- for (const point of points) {
9670
- byDate.set(point.date, (byDate.get(point.date) ?? 0) + point.amount);
9671
- }
9672
- byAsset.set(asset, byDate);
9673
- }
9753
+ const series = settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
9754
+ if (entries.length && !series.length) {
9755
+ const failure = settled.find((result) => result.status === "rejected");
9756
+ if (failure?.status === "rejected") throw failure.reason;
9674
9757
  }
9675
9758
  return {
9676
9759
  walletAddress: series[0]?.walletAddress ?? state.walletAddress ?? "",
9677
9760
  chainId,
9678
- assets: [...byAsset.entries()].map(([asset, byDate]) => ({
9679
- asset,
9680
- points: [...byDate.entries()].map(([date, amount]) => ({ date, amount })).sort((a, b) => a.date.localeCompare(b.date))
9681
- })).sort((a, b) => a.asset.localeCompare(b.asset))
9761
+ assets: aggregateDailyEarnings(series, tokenSymbol)
9682
9762
  };
9683
9763
  }
9684
9764
  async getAccountApy({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.25-beta.17",
3
+ "version": "0.7.25-beta.19",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",