@owney/sdk 0.7.27-beta.0 → 0.7.27-beta.1

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
@@ -5689,7 +5689,7 @@ var YieldseekerAgent = class {
5689
5689
  const initialFunds = funds;
5690
5690
  const idle = initialFunds.idle;
5691
5691
  const maxLiquidity = amount === void 0 ? funds : void 0;
5692
- let positions = [...funds.positions, ...funds.indexedPositions.filter((position2) => !funds.verifiedVaults.includes(position2.yieldOption.address.toLowerCase()))];
5692
+ let positions = [...funds.positions, ...funds.indexedPositions.filter((position2) => !funds.resolvedVaults.includes(position2.yieldOption.address.toLowerCase()))];
5693
5693
  let positionsStatus = "empty";
5694
5694
  const validatePositions = (values) => this.validateWithdrawalPositions(values, chainId, asset);
5695
5695
  validatePositions(positions);
@@ -5703,17 +5703,17 @@ var YieldseekerAgent = class {
5703
5703
  const verifiedPositions = [];
5704
5704
  if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
5705
5705
  let active = true;
5706
- const checks = this.readVaultLiquidity(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
5706
+ const checks = this.readVaultShares(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
5707
5707
  const reads = values.map(async (position2, index) => {
5708
5708
  const address = (0, import_viem13.getAddress)(position2.yieldOption.address);
5709
5709
  try {
5710
- const group = (await checks).slice(index * 3, index * 3 + 3);
5711
- if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
5710
+ const group = (await checks).slice(index * 2, index * 2 + 2);
5711
+ if (!active || group.length !== 2 || group.some((value) => value.status !== "success")) return null;
5712
5712
  this.assertSession(generation, chainId, asset);
5713
- const [underlying, shares, withdrawable] = group.map((value) => value.result);
5714
- if (typeof underlying !== "string" || !(0, import_viem13.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
5715
- const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
5716
- if (shares > 0n && withdrawable === 0n) {
5713
+ const [underlying, shares] = group.map((value) => value.result);
5714
+ if (typeof underlying !== "string" || !(0, import_viem13.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return null;
5715
+ const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: "0" };
5716
+ if (shares > 0n) {
5717
5717
  let positiveValue = false;
5718
5718
  const valuations = await Promise.all(["convertToAssets", "previewRedeem"].map(async (functionName) => {
5719
5719
  try {
@@ -5724,9 +5724,11 @@ var YieldseekerAgent = class {
5724
5724
  args: [shares],
5725
5725
  blockNumber
5726
5726
  });
5727
- if (typeof value !== "bigint" || value < 0n) return null;
5727
+ if (!active || typeof value !== "bigint" || value < 0n) return null;
5728
+ this.assertSession(generation, chainId, asset);
5728
5729
  if (value > 0n && !positiveValue) {
5729
5730
  positiveValue = true;
5731
+ verified.withdrawableAssetsRaw = value.toString();
5730
5732
  verifiedPositions.push(verified);
5731
5733
  }
5732
5734
  return value;
@@ -5734,7 +5736,12 @@ var YieldseekerAgent = class {
5734
5736
  return null;
5735
5737
  }
5736
5738
  }));
5737
- if (positiveValue) return verified;
5739
+ if (positiveValue) {
5740
+ const redemptionValue = valuations[1];
5741
+ if (redemptionValue !== null && redemptionValue > 0n)
5742
+ verified.withdrawableAssetsRaw = redemptionValue.toString();
5743
+ return verified;
5744
+ }
5738
5745
  if (blockNumber === void 0 || valuations.some((value) => value !== 0n)) return null;
5739
5746
  zeroValuePositions.add(verified);
5740
5747
  }
@@ -5897,8 +5904,10 @@ var YieldseekerAgent = class {
5897
5904
  this.assertSession(generation, chainId, asset);
5898
5905
  if (proof.verified) {
5899
5906
  positions = [...proof.positions, ...(catalog ?? []).filter((position2) => !knownBeforeRefresh.has(position2.yieldOption.address.toLowerCase()))];
5900
- } else if (catalog) positions = catalog;
5901
- else {
5907
+ } else if (catalog) {
5908
+ const checked = new Set(proof.positions.map((position2) => position2.yieldOption.address.toLowerCase()));
5909
+ positions = [...proof.positions, ...catalog.filter((position2) => !checked.has(position2.yieldOption.address.toLowerCase()))];
5910
+ } else {
5902
5911
  positionsStatus = "unavailable";
5903
5912
  break;
5904
5913
  }
@@ -6082,7 +6091,7 @@ var YieldseekerAgent = class {
6082
6091
  }
6083
6092
  }
6084
6093
  }
6085
- /** A withdrawal-only liquidity floor; display snapshots never authorize it. */
6094
+ /** Fresh position amounts for withdrawal planning, net of reserved fees. */
6086
6095
  async getWithdrawableBalance(state, chainId, asset, minimumAmount) {
6087
6096
  this.assertChain(chainId);
6088
6097
  this.assertAsset(asset);
@@ -6107,7 +6116,7 @@ var YieldseekerAgent = class {
6107
6116
  );
6108
6117
  return proof.available;
6109
6118
  }
6110
- async readVaultLiquidity(options, wallet, blockNumber) {
6119
+ async readVaultShares(options, wallet, blockNumber) {
6111
6120
  const owner = (0, import_viem13.getAddress)(wallet);
6112
6121
  const batches = Array.from({ length: Math.ceil(options.length / YIELDSEEKER_VAULTS_PER_RPC_BATCH) }, (_, index) => options.slice(index * YIELDSEEKER_VAULTS_PER_RPC_BATCH, (index + 1) * YIELDSEEKER_VAULTS_PER_RPC_BATCH));
6113
6122
  const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
@@ -6117,8 +6126,7 @@ var YieldseekerAgent = class {
6117
6126
  const address = (0, import_viem13.getAddress)(option.address);
6118
6127
  return [
6119
6128
  { address, abi: import_viem13.erc4626Abi, functionName: "asset" },
6120
- { address, abi: import_viem13.erc20Abi, functionName: "balanceOf", args: [owner] },
6121
- { address, abi: import_viem13.erc4626Abi, functionName: "maxWithdraw", args: [owner] }
6129
+ { address, abi: import_viem13.erc20Abi, functionName: "balanceOf", args: [owner] }
6122
6130
  ];
6123
6131
  })
6124
6132
  })));
@@ -6188,7 +6196,7 @@ var YieldseekerAgent = class {
6188
6196
  );
6189
6197
  let active = true;
6190
6198
  let knownProof;
6191
- const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.verifiedVaults.includes(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
6199
+ const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.resolvedVaults.includes(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
6192
6200
  const assertReadCurrent = () => {
6193
6201
  this.assertSession(generation, chainId, context.asset);
6194
6202
  if (!active || this.portfolioVersion(contextKey) !== version) throw unavailable();
@@ -6206,7 +6214,6 @@ var YieldseekerAgent = class {
6206
6214
  for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
6207
6215
  const options = [...candidates.values()];
6208
6216
  if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
6209
- const checksRead = this.readVaultLiquidity(options, wallet, blockNumber).then((value) => ({ value }), (error) => ({ error }));
6210
6217
  const [idle, feeTracker] = await Promise.all([
6211
6218
  client.readContract({
6212
6219
  address: metadata.address,
@@ -6233,7 +6240,7 @@ var YieldseekerAgent = class {
6233
6240
  });
6234
6241
  if (typeof fees !== "bigint" || fees < 0n) throw unavailable();
6235
6242
  const verified = [];
6236
- const verifiedVaults = [];
6243
+ const resolvedVaults = [];
6237
6244
  let deployed = 0n;
6238
6245
  const proof = (complete) => ({
6239
6246
  blockNumber,
@@ -6241,51 +6248,100 @@ var YieldseekerAgent = class {
6241
6248
  fees,
6242
6249
  available: idle + deployed > fees ? idle + deployed - fees : 0n,
6243
6250
  positions: [...verified],
6244
- verifiedVaults: [...verifiedVaults],
6251
+ resolvedVaults: [...resolvedVaults],
6245
6252
  indexedPositions,
6246
6253
  complete
6247
6254
  });
6248
6255
  assertReadCurrent();
6249
6256
  knownProof = proof(false);
6250
6257
  if (requested !== void 0 && knownProof.available >= requested) return knownProof;
6251
- const collect = (options2, results) => {
6258
+ const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
6259
+ const indexedValue = indexedPositions.reduce((sum, position2) => sum + BigInt(position2.withdrawableAssetsRaw), 0n);
6260
+ const missingKnownPosition = previousPositions.some((position2) => !indexed.has(position2.yieldOption.address.toLowerCase()));
6261
+ const unvaluedPosition = indexedPositions.some((position2) => BigInt(position2.assetsRaw) > 0n && BigInt(position2.withdrawableAssetsRaw) === 0n);
6262
+ const indexedAvailable = idle + indexedValue > fees ? idle + indexedValue - fees : 0n;
6263
+ if (indexedValue > 0n && !missingKnownPosition && !unvaluedPosition && (requested === void 0 || indexedAvailable >= requested)) {
6264
+ const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
6265
+ for (const position2 of indexedPositions) if (BigInt(position2.withdrawableAssetsRaw) > 0n)
6266
+ known.add(position2.yieldOption.address.toLowerCase());
6267
+ this.balanceVaults.set(contextKey, known);
6268
+ return {
6269
+ blockNumber,
6270
+ idle,
6271
+ fees,
6272
+ available: indexedAvailable,
6273
+ positions: indexedPositions,
6274
+ resolvedVaults: [...indexed.keys()],
6275
+ indexedPositions,
6276
+ complete: true
6277
+ };
6278
+ }
6279
+ const collect = async (options2, results) => {
6252
6280
  assertReadCurrent();
6253
6281
  const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
6254
6282
  this.balanceVaults.set(contextKey, known);
6255
- let valid2 = true;
6256
- options2.forEach((option, index) => {
6257
- const group = results.slice(index * 3, index * 3 + 3);
6258
- const shareProbe = group[1];
6259
- if (shareProbe?.status === "success" && typeof shareProbe.result === "bigint" && shareProbe.result > 0n)
6260
- known.add(option.address.toLowerCase());
6261
- if (group.length !== 3 || group.some((value) => value.status !== "success")) {
6262
- valid2 = false;
6263
- return;
6264
- }
6265
- const [underlying, shares, withdrawable] = group.map((value) => value.result);
6266
- if (typeof underlying !== "string" || !(0, import_viem13.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
6267
- valid2 = false;
6268
- return;
6269
- }
6270
- verifiedVaults.push(option.address.toLowerCase());
6271
- deployed += withdrawable;
6272
- verified.push({
6283
+ const valid2 = await Promise.all(options2.map(async (option, index) => {
6284
+ const group = results.slice(index * 2, index * 2 + 2);
6285
+ if (group.length !== 2 || group.some((value2) => value2.status !== "success")) return false;
6286
+ const [underlying, shares] = group.map((value2) => value2.result);
6287
+ if (typeof underlying !== "string" || !(0, import_viem13.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return false;
6288
+ if (shares > 0n) known.add(option.address.toLowerCase());
6289
+ const position2 = {
6273
6290
  yieldOption: option,
6274
6291
  assetsRaw: shares.toString(),
6275
6292
  assetsBase: "0",
6276
6293
  assetsUsd: 0,
6277
- withdrawableAssetsRaw: withdrawable.toString(),
6278
- withdrawableAssetsBase: withdrawable.toString()
6279
- });
6280
- });
6281
- return valid2;
6294
+ withdrawableAssetsRaw: "0",
6295
+ withdrawableAssetsBase: "0"
6296
+ };
6297
+ resolvedVaults.push(option.address.toLowerCase());
6298
+ verified.push(position2);
6299
+ knownProof = proof(false);
6300
+ if (shares === 0n) return true;
6301
+ let preview;
6302
+ try {
6303
+ const value2 = await client.readContract({
6304
+ address: (0, import_viem13.getAddress)(option.address),
6305
+ abi: import_viem13.erc4626Abi,
6306
+ functionName: "previewRedeem",
6307
+ args: [shares],
6308
+ blockNumber
6309
+ });
6310
+ if (typeof value2 === "bigint" && value2 >= 0n) preview = value2;
6311
+ } catch {
6312
+ }
6313
+ assertReadCurrent();
6314
+ let value = preview;
6315
+ if (value === void 0 || value === 0n) {
6316
+ try {
6317
+ const converted = await client.readContract({
6318
+ address: (0, import_viem13.getAddress)(option.address),
6319
+ abi: import_viem13.erc4626Abi,
6320
+ functionName: "convertToAssets",
6321
+ args: [shares],
6322
+ blockNumber
6323
+ });
6324
+ if (typeof converted === "bigint" && converted >= 0n && (converted > 0n || preview === 0n)) value = converted;
6325
+ else value = void 0;
6326
+ } catch {
6327
+ value = void 0;
6328
+ }
6329
+ }
6330
+ assertReadCurrent();
6331
+ if (value === void 0) return false;
6332
+ position2.withdrawableAssetsRaw = value.toString();
6333
+ position2.withdrawableAssetsBase = value.toString();
6334
+ deployed += value;
6335
+ knownProof = proof(false);
6336
+ return true;
6337
+ }));
6338
+ return valid2.every(Boolean);
6282
6339
  };
6283
- const checks = await checksRead;
6340
+ const checks = await this.readVaultShares(options, wallet, blockNumber);
6284
6341
  assertReadCurrent();
6285
- if ("error" in checks) throw checks.error;
6286
- const allKnownVerified = collect(options, checks.value);
6342
+ const allKnownVerified = await collect(options, checks);
6287
6343
  knownProof = proof(false);
6288
- const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
6344
+ if (requested !== void 0 && knownProof.available >= requested) return knownProof;
6289
6345
  const discrepancy = verified.some((position2) => {
6290
6346
  const row = indexed.get(position2.yieldOption.address.toLowerCase());
6291
6347
  return row ? BigInt(position2.withdrawableAssetsRaw) < BigInt(row.withdrawableAssetsRaw) : BigInt(position2.assetsRaw) > 0n;
@@ -6305,7 +6361,7 @@ var YieldseekerAgent = class {
6305
6361
  }
6306
6362
  const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
6307
6363
  if (options.length + extra.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
6308
- const allExtraVerified = collect(extra, await this.readVaultLiquidity(extra, wallet, blockNumber));
6364
+ const allExtraVerified = await collect(extra, await this.readVaultShares(extra, wallet, blockNumber));
6309
6365
  assertReadCurrent();
6310
6366
  const result = proof(eligible.size > 0 && allKnownVerified && allExtraVerified);
6311
6367
  if (result.available === 0n && !result.complete && !hasUnverifiedIndexedFunds(result)) throw unavailable();
package/dist/index.d.cts CHANGED
@@ -1913,9 +1913,9 @@ declare class YieldseekerAgent implements IAgent {
1913
1913
  deposit(state: ConnectionState, chainId: number, amount: string, asset: OwneySupportedTokens, depositCallback?: DepositCallback, onProgress?: FundingProgress, onBeforeSubmission?: (evidence: FundingEvidence) => void | Promise<void>): Promise<OwneyDepositResult>;
1914
1914
  withdraw(state: ConnectionState, chainId: number, asset: OwneySupportedTokens, amount?: string, onProgress?: FundingProgress, gasMode?: "auto" | "sponsored" | "paid", onBeforeSubmission?: FundingProgress): Promise<AgentWithdrawResult>;
1915
1915
  private withdrawInternal;
1916
- /** A withdrawal-only liquidity floor; display snapshots never authorize it. */
1916
+ /** Fresh position amounts for withdrawal planning, net of reserved fees. */
1917
1917
  getWithdrawableBalance(state: ConnectionState, chainId: number, asset: OwneySupportedTokens, minimumAmount?: bigint): Promise<bigint>;
1918
- private readVaultLiquidity;
1918
+ private readVaultShares;
1919
1919
  private validateWithdrawalPositions;
1920
1920
  private withdrawalVaultHints;
1921
1921
  private readWithdrawalPositions;
package/dist/index.d.ts CHANGED
@@ -1913,9 +1913,9 @@ declare class YieldseekerAgent implements IAgent {
1913
1913
  deposit(state: ConnectionState, chainId: number, amount: string, asset: OwneySupportedTokens, depositCallback?: DepositCallback, onProgress?: FundingProgress, onBeforeSubmission?: (evidence: FundingEvidence) => void | Promise<void>): Promise<OwneyDepositResult>;
1914
1914
  withdraw(state: ConnectionState, chainId: number, asset: OwneySupportedTokens, amount?: string, onProgress?: FundingProgress, gasMode?: "auto" | "sponsored" | "paid", onBeforeSubmission?: FundingProgress): Promise<AgentWithdrawResult>;
1915
1915
  private withdrawInternal;
1916
- /** A withdrawal-only liquidity floor; display snapshots never authorize it. */
1916
+ /** Fresh position amounts for withdrawal planning, net of reserved fees. */
1917
1917
  getWithdrawableBalance(state: ConnectionState, chainId: number, asset: OwneySupportedTokens, minimumAmount?: bigint): Promise<bigint>;
1918
- private readVaultLiquidity;
1918
+ private readVaultShares;
1919
1919
  private validateWithdrawalPositions;
1920
1920
  private withdrawalVaultHints;
1921
1921
  private readWithdrawalPositions;
package/dist/index.js CHANGED
@@ -5677,7 +5677,7 @@ var YieldseekerAgent = class {
5677
5677
  const initialFunds = funds;
5678
5678
  const idle = initialFunds.idle;
5679
5679
  const maxLiquidity = amount === void 0 ? funds : void 0;
5680
- let positions = [...funds.positions, ...funds.indexedPositions.filter((position2) => !funds.verifiedVaults.includes(position2.yieldOption.address.toLowerCase()))];
5680
+ let positions = [...funds.positions, ...funds.indexedPositions.filter((position2) => !funds.resolvedVaults.includes(position2.yieldOption.address.toLowerCase()))];
5681
5681
  let positionsStatus = "empty";
5682
5682
  const validatePositions = (values) => this.validateWithdrawalPositions(values, chainId, asset);
5683
5683
  validatePositions(positions);
@@ -5691,17 +5691,17 @@ var YieldseekerAgent = class {
5691
5691
  const verifiedPositions = [];
5692
5692
  if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
5693
5693
  let active = true;
5694
- const checks = this.readVaultLiquidity(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
5694
+ const checks = this.readVaultShares(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
5695
5695
  const reads = values.map(async (position2, index) => {
5696
5696
  const address = getAddress5(position2.yieldOption.address);
5697
5697
  try {
5698
- const group = (await checks).slice(index * 3, index * 3 + 3);
5699
- if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
5698
+ const group = (await checks).slice(index * 2, index * 2 + 2);
5699
+ if (!active || group.length !== 2 || group.some((value) => value.status !== "success")) return null;
5700
5700
  this.assertSession(generation, chainId, asset);
5701
- const [underlying, shares, withdrawable] = group.map((value) => value.result);
5702
- if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
5703
- const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
5704
- if (shares > 0n && withdrawable === 0n) {
5701
+ const [underlying, shares] = group.map((value) => value.result);
5702
+ if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return null;
5703
+ const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: "0" };
5704
+ if (shares > 0n) {
5705
5705
  let positiveValue = false;
5706
5706
  const valuations = await Promise.all(["convertToAssets", "previewRedeem"].map(async (functionName) => {
5707
5707
  try {
@@ -5712,9 +5712,11 @@ var YieldseekerAgent = class {
5712
5712
  args: [shares],
5713
5713
  blockNumber
5714
5714
  });
5715
- if (typeof value !== "bigint" || value < 0n) return null;
5715
+ if (!active || typeof value !== "bigint" || value < 0n) return null;
5716
+ this.assertSession(generation, chainId, asset);
5716
5717
  if (value > 0n && !positiveValue) {
5717
5718
  positiveValue = true;
5719
+ verified.withdrawableAssetsRaw = value.toString();
5718
5720
  verifiedPositions.push(verified);
5719
5721
  }
5720
5722
  return value;
@@ -5722,7 +5724,12 @@ var YieldseekerAgent = class {
5722
5724
  return null;
5723
5725
  }
5724
5726
  }));
5725
- if (positiveValue) return verified;
5727
+ if (positiveValue) {
5728
+ const redemptionValue = valuations[1];
5729
+ if (redemptionValue !== null && redemptionValue > 0n)
5730
+ verified.withdrawableAssetsRaw = redemptionValue.toString();
5731
+ return verified;
5732
+ }
5726
5733
  if (blockNumber === void 0 || valuations.some((value) => value !== 0n)) return null;
5727
5734
  zeroValuePositions.add(verified);
5728
5735
  }
@@ -5885,8 +5892,10 @@ var YieldseekerAgent = class {
5885
5892
  this.assertSession(generation, chainId, asset);
5886
5893
  if (proof.verified) {
5887
5894
  positions = [...proof.positions, ...(catalog ?? []).filter((position2) => !knownBeforeRefresh.has(position2.yieldOption.address.toLowerCase()))];
5888
- } else if (catalog) positions = catalog;
5889
- else {
5895
+ } else if (catalog) {
5896
+ const checked = new Set(proof.positions.map((position2) => position2.yieldOption.address.toLowerCase()));
5897
+ positions = [...proof.positions, ...catalog.filter((position2) => !checked.has(position2.yieldOption.address.toLowerCase()))];
5898
+ } else {
5890
5899
  positionsStatus = "unavailable";
5891
5900
  break;
5892
5901
  }
@@ -6070,7 +6079,7 @@ var YieldseekerAgent = class {
6070
6079
  }
6071
6080
  }
6072
6081
  }
6073
- /** A withdrawal-only liquidity floor; display snapshots never authorize it. */
6082
+ /** Fresh position amounts for withdrawal planning, net of reserved fees. */
6074
6083
  async getWithdrawableBalance(state, chainId, asset, minimumAmount) {
6075
6084
  this.assertChain(chainId);
6076
6085
  this.assertAsset(asset);
@@ -6095,7 +6104,7 @@ var YieldseekerAgent = class {
6095
6104
  );
6096
6105
  return proof.available;
6097
6106
  }
6098
- async readVaultLiquidity(options, wallet, blockNumber) {
6107
+ async readVaultShares(options, wallet, blockNumber) {
6099
6108
  const owner = getAddress5(wallet);
6100
6109
  const batches = Array.from({ length: Math.ceil(options.length / YIELDSEEKER_VAULTS_PER_RPC_BATCH) }, (_, index) => options.slice(index * YIELDSEEKER_VAULTS_PER_RPC_BATCH, (index + 1) * YIELDSEEKER_VAULTS_PER_RPC_BATCH));
6101
6110
  const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
@@ -6105,8 +6114,7 @@ var YieldseekerAgent = class {
6105
6114
  const address = getAddress5(option.address);
6106
6115
  return [
6107
6116
  { address, abi: erc4626Abi2, functionName: "asset" },
6108
- { address, abi: erc20Abi5, functionName: "balanceOf", args: [owner] },
6109
- { address, abi: erc4626Abi2, functionName: "maxWithdraw", args: [owner] }
6117
+ { address, abi: erc20Abi5, functionName: "balanceOf", args: [owner] }
6110
6118
  ];
6111
6119
  })
6112
6120
  })));
@@ -6176,7 +6184,7 @@ var YieldseekerAgent = class {
6176
6184
  );
6177
6185
  let active = true;
6178
6186
  let knownProof;
6179
- const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.verifiedVaults.includes(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
6187
+ const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.resolvedVaults.includes(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
6180
6188
  const assertReadCurrent = () => {
6181
6189
  this.assertSession(generation, chainId, context.asset);
6182
6190
  if (!active || this.portfolioVersion(contextKey) !== version) throw unavailable();
@@ -6194,7 +6202,6 @@ var YieldseekerAgent = class {
6194
6202
  for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
6195
6203
  const options = [...candidates.values()];
6196
6204
  if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
6197
- const checksRead = this.readVaultLiquidity(options, wallet, blockNumber).then((value) => ({ value }), (error) => ({ error }));
6198
6205
  const [idle, feeTracker] = await Promise.all([
6199
6206
  client.readContract({
6200
6207
  address: metadata.address,
@@ -6221,7 +6228,7 @@ var YieldseekerAgent = class {
6221
6228
  });
6222
6229
  if (typeof fees !== "bigint" || fees < 0n) throw unavailable();
6223
6230
  const verified = [];
6224
- const verifiedVaults = [];
6231
+ const resolvedVaults = [];
6225
6232
  let deployed = 0n;
6226
6233
  const proof = (complete) => ({
6227
6234
  blockNumber,
@@ -6229,51 +6236,100 @@ var YieldseekerAgent = class {
6229
6236
  fees,
6230
6237
  available: idle + deployed > fees ? idle + deployed - fees : 0n,
6231
6238
  positions: [...verified],
6232
- verifiedVaults: [...verifiedVaults],
6239
+ resolvedVaults: [...resolvedVaults],
6233
6240
  indexedPositions,
6234
6241
  complete
6235
6242
  });
6236
6243
  assertReadCurrent();
6237
6244
  knownProof = proof(false);
6238
6245
  if (requested !== void 0 && knownProof.available >= requested) return knownProof;
6239
- const collect = (options2, results) => {
6246
+ const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
6247
+ const indexedValue = indexedPositions.reduce((sum, position2) => sum + BigInt(position2.withdrawableAssetsRaw), 0n);
6248
+ const missingKnownPosition = previousPositions.some((position2) => !indexed.has(position2.yieldOption.address.toLowerCase()));
6249
+ const unvaluedPosition = indexedPositions.some((position2) => BigInt(position2.assetsRaw) > 0n && BigInt(position2.withdrawableAssetsRaw) === 0n);
6250
+ const indexedAvailable = idle + indexedValue > fees ? idle + indexedValue - fees : 0n;
6251
+ if (indexedValue > 0n && !missingKnownPosition && !unvaluedPosition && (requested === void 0 || indexedAvailable >= requested)) {
6252
+ const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
6253
+ for (const position2 of indexedPositions) if (BigInt(position2.withdrawableAssetsRaw) > 0n)
6254
+ known.add(position2.yieldOption.address.toLowerCase());
6255
+ this.balanceVaults.set(contextKey, known);
6256
+ return {
6257
+ blockNumber,
6258
+ idle,
6259
+ fees,
6260
+ available: indexedAvailable,
6261
+ positions: indexedPositions,
6262
+ resolvedVaults: [...indexed.keys()],
6263
+ indexedPositions,
6264
+ complete: true
6265
+ };
6266
+ }
6267
+ const collect = async (options2, results) => {
6240
6268
  assertReadCurrent();
6241
6269
  const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
6242
6270
  this.balanceVaults.set(contextKey, known);
6243
- let valid2 = true;
6244
- options2.forEach((option, index) => {
6245
- const group = results.slice(index * 3, index * 3 + 3);
6246
- const shareProbe = group[1];
6247
- if (shareProbe?.status === "success" && typeof shareProbe.result === "bigint" && shareProbe.result > 0n)
6248
- known.add(option.address.toLowerCase());
6249
- if (group.length !== 3 || group.some((value) => value.status !== "success")) {
6250
- valid2 = false;
6251
- return;
6252
- }
6253
- const [underlying, shares, withdrawable] = group.map((value) => value.result);
6254
- if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
6255
- valid2 = false;
6256
- return;
6257
- }
6258
- verifiedVaults.push(option.address.toLowerCase());
6259
- deployed += withdrawable;
6260
- verified.push({
6271
+ const valid2 = await Promise.all(options2.map(async (option, index) => {
6272
+ const group = results.slice(index * 2, index * 2 + 2);
6273
+ if (group.length !== 2 || group.some((value2) => value2.status !== "success")) return false;
6274
+ const [underlying, shares] = group.map((value2) => value2.result);
6275
+ if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return false;
6276
+ if (shares > 0n) known.add(option.address.toLowerCase());
6277
+ const position2 = {
6261
6278
  yieldOption: option,
6262
6279
  assetsRaw: shares.toString(),
6263
6280
  assetsBase: "0",
6264
6281
  assetsUsd: 0,
6265
- withdrawableAssetsRaw: withdrawable.toString(),
6266
- withdrawableAssetsBase: withdrawable.toString()
6267
- });
6268
- });
6269
- return valid2;
6282
+ withdrawableAssetsRaw: "0",
6283
+ withdrawableAssetsBase: "0"
6284
+ };
6285
+ resolvedVaults.push(option.address.toLowerCase());
6286
+ verified.push(position2);
6287
+ knownProof = proof(false);
6288
+ if (shares === 0n) return true;
6289
+ let preview;
6290
+ try {
6291
+ const value2 = await client.readContract({
6292
+ address: getAddress5(option.address),
6293
+ abi: erc4626Abi2,
6294
+ functionName: "previewRedeem",
6295
+ args: [shares],
6296
+ blockNumber
6297
+ });
6298
+ if (typeof value2 === "bigint" && value2 >= 0n) preview = value2;
6299
+ } catch {
6300
+ }
6301
+ assertReadCurrent();
6302
+ let value = preview;
6303
+ if (value === void 0 || value === 0n) {
6304
+ try {
6305
+ const converted = await client.readContract({
6306
+ address: getAddress5(option.address),
6307
+ abi: erc4626Abi2,
6308
+ functionName: "convertToAssets",
6309
+ args: [shares],
6310
+ blockNumber
6311
+ });
6312
+ if (typeof converted === "bigint" && converted >= 0n && (converted > 0n || preview === 0n)) value = converted;
6313
+ else value = void 0;
6314
+ } catch {
6315
+ value = void 0;
6316
+ }
6317
+ }
6318
+ assertReadCurrent();
6319
+ if (value === void 0) return false;
6320
+ position2.withdrawableAssetsRaw = value.toString();
6321
+ position2.withdrawableAssetsBase = value.toString();
6322
+ deployed += value;
6323
+ knownProof = proof(false);
6324
+ return true;
6325
+ }));
6326
+ return valid2.every(Boolean);
6270
6327
  };
6271
- const checks = await checksRead;
6328
+ const checks = await this.readVaultShares(options, wallet, blockNumber);
6272
6329
  assertReadCurrent();
6273
- if ("error" in checks) throw checks.error;
6274
- const allKnownVerified = collect(options, checks.value);
6330
+ const allKnownVerified = await collect(options, checks);
6275
6331
  knownProof = proof(false);
6276
- const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
6332
+ if (requested !== void 0 && knownProof.available >= requested) return knownProof;
6277
6333
  const discrepancy = verified.some((position2) => {
6278
6334
  const row = indexed.get(position2.yieldOption.address.toLowerCase());
6279
6335
  return row ? BigInt(position2.withdrawableAssetsRaw) < BigInt(row.withdrawableAssetsRaw) : BigInt(position2.assetsRaw) > 0n;
@@ -6293,7 +6349,7 @@ var YieldseekerAgent = class {
6293
6349
  }
6294
6350
  const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
6295
6351
  if (options.length + extra.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
6296
- const allExtraVerified = collect(extra, await this.readVaultLiquidity(extra, wallet, blockNumber));
6352
+ const allExtraVerified = await collect(extra, await this.readVaultShares(extra, wallet, blockNumber));
6297
6353
  assertReadCurrent();
6298
6354
  const result = proof(eligible.size > 0 && allKnownVerified && allExtraVerified);
6299
6355
  if (result.available === 0n && !result.complete && !hasUnverifiedIndexedFunds(result)) throw unavailable();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.27-beta.0",
3
+ "version": "0.7.27-beta.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",