shinjuku-shielded 0.3.41 → 0.3.43

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/manifest.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema": "carbon-shielded-wallet/bundle-manifest/v1",
3
3
  "file": "shinjuku-wallet.mjs",
4
- "sha256": "387938b415cfece575952e875ecafc7149e29279e202a4e862433f66f664a803",
5
- "bytes": 11230018,
6
- "sourceCommit": "60acd91587cd5576b91001c7dcdabbb1c0f723aa",
4
+ "sha256": "0ff1dea585d7a2ca69e0a37f43684dcb411f5a28eafa84a3f7fee65f5cd13904",
5
+ "bytes": 11235804,
6
+ "sourceCommit": "20e5a0c5b4faa27b1309e57816901b0ba77c78ce",
7
7
  "sourceDirty": false,
8
8
  "installRecordMatchesLockfile": true,
9
9
  "releaseRule": "valid only when two fresh npm ci --ignore-scripts builds on different OSes give the same sha256; installRecordMatchesLockfile true means every entry npm installed equals its package-lock.json entry and every entry not marked optional, devOptional included, is installed: an optional entry (a package for another platform) may be absent",
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.41",
3
+ "version": "0.3.43",
4
4
  "mcpName": "io.github.ShinjukuStaition/shinjuku-mcp",
5
5
  "description": "Shinjuku Shielded, the private x402 facilitator on Solana: the self-custody wallet CLI and its MCP server. Shield, pay, and unshield USDC from any MCP agent, under caps you set.",
6
- "shinjukuRelease": "387938b4",
7
- "shinjukuSha256": "387938b415cfece575952e875ecafc7149e29279e202a4e862433f66f664a803",
6
+ "shinjukuRelease": "0ff1dea5",
7
+ "shinjukuSha256": "0ff1dea585d7a2ca69e0a37f43684dcb411f5a28eafa84a3f7fee65f5cd13904",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -60149,6 +60149,7 @@ var init_sealedCache = __esm({
60149
60149
  });
60150
60150
 
60151
60151
  // wallet/src/recovery/recoveryCache.mjs
60152
+ import { performance as performance2 } from "node:perf_hooks";
60152
60153
  function contextBinding(context) {
60153
60154
  return [
60154
60155
  context.transitionKind,
@@ -60200,7 +60201,28 @@ function decodeOutput(value) {
60200
60201
  VERIFIED_OPENINGS.add(opening);
60201
60202
  return { ...output2, leafIndex: decimal3(value.leafIndex) };
60202
60203
  }
60203
- function openRecoveryCache(store, { programId, pool: pool2 }) {
60204
+ function openRecoveryCache(store, ids) {
60205
+ const run5 = openRecoveryCacheSteps(store, ids);
60206
+ let step = run5.next();
60207
+ while (!step.done) step = run5.next();
60208
+ return step.value;
60209
+ }
60210
+ async function openRecoveryCacheSliced(store, ids) {
60211
+ const run5 = openRecoveryCacheSteps(store, ids);
60212
+ let started = performance2.now();
60213
+ let step = run5.next();
60214
+ while (!step.done) {
60215
+ if (performance2.now() - started >= 25) {
60216
+ await new Promise((resolve38) => {
60217
+ setImmediate(resolve38);
60218
+ });
60219
+ started = performance2.now();
60220
+ }
60221
+ step = run5.next();
60222
+ }
60223
+ return step.value;
60224
+ }
60225
+ function* openRecoveryCacheSteps(store, { programId, pool: pool2 }) {
60204
60226
  const file = sealedCacheFile(store, { file: RECOVERY_CACHE_FILE, schema: RECOVERY_CACHE_SCHEMA, keyLabel: KEY_LABEL });
60205
60227
  if (!file) return void 0;
60206
60228
  const read = file.read();
@@ -60217,6 +60239,7 @@ function openRecoveryCache(store, { programId, pool: pool2 }) {
60217
60239
  entryKey(memo2.digest, memo2.binding),
60218
60240
  { digest: memo2.digest, binding: memo2.binding, outputs: memo2.outputs.map(decodeOutput) }
60219
60241
  );
60242
+ yield;
60220
60243
  }
60221
60244
  } catch {
60222
60245
  entries.clear();
@@ -60282,14 +60305,14 @@ var init_recoveryCache = __esm({
60282
60305
 
60283
60306
  // wallet/src/recovery/memoDecryptPool.mjs
60284
60307
  import { availableParallelism } from "node:os";
60285
- import { performance as performance2 } from "node:perf_hooks";
60308
+ import { performance as performance3 } from "node:perf_hooks";
60286
60309
  import { Worker as Worker2 } from "node:worker_threads";
60287
60310
  function sliceClock() {
60288
- let started = performance2.now();
60311
+ let started = performance3.now();
60289
60312
  return async () => {
60290
- if (performance2.now() - started < SLICE_MS) return;
60313
+ if (performance3.now() - started < SLICE_MS) return;
60291
60314
  await yieldTurn();
60292
- started = performance2.now();
60315
+ started = performance3.now();
60293
60316
  };
60294
60317
  }
60295
60318
  async function sharedXOnMainThread(xs, eta) {
@@ -60618,7 +60641,7 @@ var init_successorHistories = __esm({
60618
60641
  // wallet/src/recovery/recover.mjs
60619
60642
  import { createHash as createHash15 } from "node:crypto";
60620
60643
  import { join as join10, resolve as resolve10 } from "node:path";
60621
- import { performance as performance3 } from "node:perf_hooks";
60644
+ import { performance as performance4 } from "node:perf_hooks";
60622
60645
  import { fileURLToPath as fileURLToPath5 } from "node:url";
60623
60646
  function pinnedGenerator2(profileVector) {
60624
60647
  if (profileVector?.payerKeys?.generator) return profileVector.payerKeys.generator;
@@ -60689,18 +60712,18 @@ function walMemoBundles(store) {
60689
60712
  }
60690
60713
  async function walMemoBundlesSliced(store) {
60691
60714
  const bundles = /* @__PURE__ */ new Map();
60692
- let started = performance3.now();
60715
+ let started = performance4.now();
60693
60716
  for (const name of store.listWal()) {
60694
60717
  const record = store.readWal(name);
60695
60718
  if (record?.memoBundleHex) {
60696
60719
  const bytes2 = Uint8Array.from(Buffer.from(hexOf2(record.memoBundleHex), "hex"));
60697
60720
  bundles.set(hexOf2(record.memoDigest ?? bundleDigest2(bytes2)), bytes2);
60698
60721
  }
60699
- if (performance3.now() - started >= 25) {
60722
+ if (performance4.now() - started >= 25) {
60700
60723
  await new Promise((resolve38) => {
60701
60724
  setImmediate(resolve38);
60702
60725
  });
60703
- started = performance3.now();
60726
+ started = performance4.now();
60704
60727
  }
60705
60728
  }
60706
60729
  return bundles;
@@ -60779,7 +60802,7 @@ async function legacyOutputs({ store, digests, serviceUrl: serviceUrl2, fresh, d
60779
60802
  return { entries, bundlesExamined: bundles.length };
60780
60803
  }
60781
60804
  async function syncedOutputs({ store, finalizedHistory: finalizedHistory2, digests, serviceUrl: serviceUrl2, fresh, deps, chainProgramId, pool: pool2, eta, decrypt }) {
60782
- const cache = deps.recoveryCache !== void 0 ? deps.recoveryCache : openRecoveryCache(store, { programId: chainProgramId.toBase58(), pool: pool2.toBase58() });
60805
+ const cache = deps.recoveryCache !== void 0 ? deps.recoveryCache : await openRecoveryCacheSliced(store, { programId: chainProgramId.toBase58(), pool: pool2.toBase58() });
60783
60806
  const contexts = finalizedHistory2.contexts;
60784
60807
  const found = /* @__PURE__ */ new Map();
60785
60808
  const pending = [];
@@ -60816,13 +60839,13 @@ async function syncedOutputs({ store, finalizedHistory: finalizedHistory2, diges
60816
60839
  }
60817
60840
  }
60818
60841
  const items = [];
60819
- let sliceStart = performance3.now();
60842
+ let sliceStart = performance4.now();
60820
60843
  for (const context of chunk) {
60821
- if (performance3.now() - sliceStart >= 25) {
60844
+ if (performance4.now() - sliceStart >= 25) {
60822
60845
  await new Promise((resolve38) => {
60823
60846
  setImmediate(resolve38);
60824
60847
  });
60825
- sliceStart = performance3.now();
60848
+ sliceStart = performance4.now();
60826
60849
  }
60827
60850
  const digest2 = hexOf2(context.memoDigest);
60828
60851
  const fromService = served.get(digest2);
@@ -60977,8 +61000,15 @@ async function runRecover({ store, connection, programId, digests, serviceUrl: s
60977
61000
  }
60978
61001
  markPhase("recover-memos");
60979
61002
  const recovered = [];
61003
+ let replayStarted = performance4.now();
60980
61004
  for (const { digest: digest2, context, outputs } of read.entries) {
60981
61005
  for (const output2 of outputs) {
61006
+ if (performance4.now() - replayStarted >= 25) {
61007
+ await new Promise((resolve38) => {
61008
+ setImmediate(resolve38);
61009
+ });
61010
+ replayStarted = performance4.now();
61011
+ }
60982
61012
  const replayValue = replayValueOf(
60983
61013
  output2.kind,
60984
61014
  context.poolDomain,
@@ -72513,8 +72543,15 @@ function respendNoteSubset({ accounts, notes, outputIndex, amountAtomic, refused
72513
72543
  return void 0;
72514
72544
  }
72515
72545
  function exitReservedNoteCommitments(store) {
72546
+ const run5 = exitReservedNoteCommitmentSteps(store);
72547
+ let step = run5.next();
72548
+ while (!step.done) step = run5.next();
72549
+ return step.value;
72550
+ }
72551
+ function* exitReservedNoteCommitmentSteps(store) {
72516
72552
  const reserved = /* @__PURE__ */ new Set();
72517
72553
  for (const name of typeof store?.listWal === "function" ? store.listWal() : []) {
72554
+ yield;
72518
72555
  const selfPay2 = name.startsWith("self-pay-");
72519
72556
  if (!selfPay2 && !name.startsWith("note-exit-")) continue;
72520
72557
  if (name.startsWith("self-pay-merge-")) continue;
@@ -72568,7 +72605,7 @@ function startFinalizedChainTime(connection) {
72568
72605
  return time + Math.floor(age / 1e3);
72569
72606
  };
72570
72607
  }
72571
- async function everyAccountHeld({ store, connection, programId, serviceUrl: serviceUrl2, reservedReplayValues, deps = {} }) {
72608
+ async function everyAccountHeld({ store, connection, programId, serviceUrl: serviceUrl2, reservedReplayValues, amountAtomic, deps = {} }) {
72572
72609
  if (typeof deps.walkHistory !== "function") fail15("walk_history_required");
72573
72610
  const viewing = deriveWalletViewingAuthority2({ seedBytes: seedBytes5(store.state), generator: pinnedGenerator4() });
72574
72611
  const history = assertAuthenticatedFinalizedP03History(await withRecentWalletHistory(() => deps.walkHistory()));
@@ -72579,14 +72616,31 @@ async function everyAccountHeld({ store, connection, programId, serviceUrl: serv
72579
72616
  serviceUrl: serviceUrl2,
72580
72617
  deps: { fetchImpl: deps.fetchImpl, finalizedHistory: history, transitionContextFor: history.transitionContextFor }
72581
72618
  });
72582
- return recovery.recovered.filter((entry) => entry.kind === "account" && entry.spendStatus === "unspent").every((entry) => {
72619
+ const poolDomain = history.contexts[0]?.poolDomain;
72620
+ const held = (entry) => {
72583
72621
  if (entry.leafIndex === void 0 || entry.leafIndex === null) return true;
72584
72622
  try {
72585
- return reservedReplayValues.has(accountNullifier(entry.commitment, viewing.eta, entry.leafIndex).toString());
72623
+ const value = entry.kind === "account" ? accountNullifier(entry.commitment, viewing.eta, entry.leafIndex) : noteNullifier2(poolDomain, entry.commitment, viewing.eta, entry.leafIndex);
72624
+ return reservedReplayValues.has(value.toString());
72586
72625
  } catch {
72587
72626
  return true;
72588
72627
  }
72589
- });
72628
+ };
72629
+ const unspent = (kind) => recovery.recovered.filter((entry) => entry.kind === kind && entry.spendStatus === "unspent");
72630
+ const unspentAccounts = unspent("account");
72631
+ const unheldAccounts = unspentAccounts.filter((entry) => !held(entry));
72632
+ if (unheldAccounts.length === 0) return true;
72633
+ if (amountAtomic === void 0) return false;
72634
+ const unspentNotes = unspent("note");
72635
+ const unheldNotes = unspentNotes.filter((entry) => !held(entry));
72636
+ const anyHeld = unheldAccounts.length !== unspentAccounts.length || unheldNotes.length !== unspentNotes.length && (() => {
72637
+ const exitReserved = exitReservedNoteCommitments(store);
72638
+ return unspentNotes.some((entry) => held(entry) && !exitReserved.has(BigInt(entry.commitment).toString()));
72639
+ })();
72640
+ if (!anyHeld) return false;
72641
+ const largestAccount = unheldAccounts.reduce((max, entry) => BigInt(entry.account.b) > max ? BigInt(entry.account.b) : max, 0n);
72642
+ const notesValue = unheldNotes.reduce((sum2, entry) => sum2 + BigInt(entry.note.b), 0n);
72643
+ return largestAccount + notesValue < BigInt(amountAtomic);
72590
72644
  }
72591
72645
  async function prepareRecoveredPayment({
72592
72646
  store,
@@ -93185,6 +93239,7 @@ async function payResourceOnce({
93185
93239
  programId,
93186
93240
  serviceUrl: serviceUrl2,
93187
93241
  reservedReplayValues: held,
93242
+ amountAtomic: maximumAtomic,
93188
93243
  deps: { fetchImpl: deps.fetchImpl, walkHistory: finality.walkHistory }
93189
93244
  }));
93190
93245
  } catch {
@@ -96715,12 +96770,33 @@ async function createSellerAdapter({
96715
96770
  const signingBytes = Buffer.from(config.signingKey.slice(2), "hex");
96716
96771
  const signerPublicKey = `0x${Buffer.from(secp256k1.getPublicKey(signingBytes, true)).toString("hex")}`;
96717
96772
  deriveShieldedSolanaP03Recipient({ eta: config.eta, resourceNonce: randomBytes17(32), poolDomain: BigInt(profile.poolDomain) });
96773
+ const ecdhScalar = config.eta % SHIELDED_SOLANA_P03_JUBJUB_SCALAR_MODULUS;
96774
+ const sharedXOf = (point) => shieldedSolanaP03ScalarMultiply(point, ecdhScalar).x;
96775
+ const sharedXByPoint = /* @__PURE__ */ new Map();
96776
+ let precomputesRunning = 0;
96718
96777
  const scanner = new ShieldedSolanaP03MemoScanner({ ecdh: {
96719
- deriveSharedX: (point) => shieldedSolanaP03ScalarMultiply(
96720
- point,
96721
- config.eta % SHIELDED_SOLANA_P03_JUBJUB_SCALAR_MODULUS
96722
- ).x
96778
+ deriveSharedX: (point) => sharedXByPoint.get(`${point.x}:${point.y}`) ?? sharedXOf(point)
96723
96779
  } });
96780
+ async function precomputeMemoSharedX(memoBundle, owned) {
96781
+ let sliceStart = performance.now();
96782
+ for (let offset = 0; offset + MEMO_RECORD_BYTES2 <= memoBundle.length; offset += MEMO_RECORD_BYTES2) {
96783
+ if (performance.now() - sliceStart >= MEMO_PRECOMPUTE_SLICE_MS) {
96784
+ await new Promise((resolveTurn) => {
96785
+ setImmediate(resolveTurn);
96786
+ });
96787
+ sliceStart = performance.now();
96788
+ }
96789
+ if (owned.done) return;
96790
+ try {
96791
+ const point = shieldedSolanaP03CurvePointFromX(BigInt(`0x${Buffer.from(memoBundle.subarray(offset, offset + 32)).toString("hex")}`));
96792
+ const key = `${point.x}:${point.y}`;
96793
+ if (sharedXByPoint.has(key)) continue;
96794
+ sharedXByPoint.set(key, sharedXOf(point));
96795
+ owned.keys.push(key);
96796
+ } catch {
96797
+ }
96798
+ }
96799
+ }
96724
96800
  const readChainAnchor = createAnchorReader({ connection: memoizeFinalizedReads(connection), profile });
96725
96801
  let anchorRequest;
96726
96802
  const requestFacilitatorAnchor = () => anchorRequest ??= (async () => {
@@ -97123,15 +97199,32 @@ async function createSellerAdapter({
97123
97199
  try {
97124
97200
  if (!offer.outboundJson) {
97125
97201
  const parsed = parseSolanaP03HttpPaymentPayload(payment.payload);
97126
- const auth = await issuer(offer.resource, payment.accepted.amount).authenticate({
97127
- requirements: payment.accepted,
97128
- resource: payment.resource,
97129
- publicInput7: parsed.publicInputs[7]
97130
- });
97131
- mark("intake-authenticated");
97132
- const intakeRead = readAnchor();
97133
- intakeRead.catch(() => void 0);
97134
- const recoveredNote = await scanner.recoverMerchantNoteYielding({ parsed, authenticatedQuote: auth });
97202
+ const quoteIssuer = issuer(offer.resource, payment.accepted.amount);
97203
+ const authInput = { requirements: payment.accepted, resource: payment.resource, publicInput7: parsed.publicInputs[7] };
97204
+ await quoteIssuer.authenticateForClaim(authInput);
97205
+ const authenticated = quoteIssuer.authenticate(authInput);
97206
+ authenticated.catch(() => void 0);
97207
+ const owned = { keys: [], done: false };
97208
+ let precomputed;
97209
+ if (precomputesRunning < MEMO_PRECOMPUTES_MAX) {
97210
+ precomputesRunning += 1;
97211
+ precomputed = precomputeMemoSharedX(parsed.memoBundle, owned).catch(() => void 0).finally(() => {
97212
+ precomputesRunning -= 1;
97213
+ });
97214
+ }
97215
+ let recoveredNote;
97216
+ let intakeRead;
97217
+ try {
97218
+ const auth = await authenticated;
97219
+ mark("intake-authenticated");
97220
+ intakeRead = readAnchor();
97221
+ intakeRead.catch(() => void 0);
97222
+ await precomputed;
97223
+ recoveredNote = await scanner.recoverMerchantNoteYielding({ parsed, authenticatedQuote: auth });
97224
+ } finally {
97225
+ owned.done = true;
97226
+ for (const key of owned.keys) sharedXByPoint.delete(key);
97227
+ }
97135
97228
  mark("intake-note-recovered");
97136
97229
  const chain = await intakeRead;
97137
97230
  intakeChain = chain;
@@ -97360,7 +97453,7 @@ async function createSellerAdapter({
97360
97453
  }));
97361
97454
  return { server, handle, close, signerPublicKey, settledTransactions, offerSummaries };
97362
97455
  }
97363
- var import_web3120, BINDING, SCHEMA2, OFFERS_LAYOUT, SNAPSHOT_EVERY_MS, LOADER2, canonical4, storeProfileIdentity, jsonSafe, bigint, encodeHeader, PAYMENT_REFUSED, FACILITATOR_UNDERFUNDED, DEFINITE_VERIFY_REFUSALS, definiteVerifyRefusal, refusedBeforeSettlement, ANCHOR_REQUEST_TIMEOUT_MS2, ANCHOR_POLL_ATTEMPTS, ANCHOR_POLL_MS, PRE_SETTLE_REUSE_MARGIN_S, PRE_SETTLE_REUSE_MAX_AGE_MS, FINALIZED_MEMO_MAX_AGE_MS, pause, FOLLOW_UP_DELAYS_MS, FOLLOW_UP_RESETTLE_FROM, CONFIRMED_TRANSACTION_READ;
97456
+ var import_web3120, BINDING, SCHEMA2, OFFERS_LAYOUT, SNAPSHOT_EVERY_MS, LOADER2, canonical4, storeProfileIdentity, jsonSafe, bigint, encodeHeader, PAYMENT_REFUSED, FACILITATOR_UNDERFUNDED, DEFINITE_VERIFY_REFUSALS, definiteVerifyRefusal, refusedBeforeSettlement, ANCHOR_REQUEST_TIMEOUT_MS2, ANCHOR_POLL_ATTEMPTS, ANCHOR_POLL_MS, MEMO_RECORD_BYTES2, MEMO_PRECOMPUTE_SLICE_MS, MEMO_PRECOMPUTES_MAX, PRE_SETTLE_REUSE_MARGIN_S, PRE_SETTLE_REUSE_MAX_AGE_MS, FINALIZED_MEMO_MAX_AGE_MS, pause, FOLLOW_UP_DELAYS_MS, FOLLOW_UP_RESETTLE_FROM, CONFIRMED_TRANSACTION_READ;
97364
97457
  var init_service = __esm({
97365
97458
  "wallet/src/seller/service.mjs"() {
97366
97459
  "use strict";
@@ -97411,6 +97504,9 @@ var init_service = __esm({
97411
97504
  ANCHOR_REQUEST_TIMEOUT_MS2 = 9e4;
97412
97505
  ANCHOR_POLL_ATTEMPTS = 15;
97413
97506
  ANCHOR_POLL_MS = 2e3;
97507
+ MEMO_RECORD_BYTES2 = 200;
97508
+ MEMO_PRECOMPUTE_SLICE_MS = 8;
97509
+ MEMO_PRECOMPUTES_MAX = 2;
97414
97510
  PRE_SETTLE_REUSE_MARGIN_S = 60;
97415
97511
  PRE_SETTLE_REUSE_MAX_AGE_MS = 2e4;
97416
97512
  FINALIZED_MEMO_MAX_AGE_MS = 1e3;
@@ -126745,7 +126841,18 @@ function createWalletTools({
126745
126841
  ...share ? { share } : {}
126746
126842
  });
126747
126843
  let lastWriterEndedAt = 0;
126748
- const refreshWalk = ops.refreshWalk ?? (ops.refresh ? void 0 : (store) => walkWalletHistory(session, store));
126844
+ let paysInFlight = 0;
126845
+ let refreshDeferred = false;
126846
+ let refreshForeground = false;
126847
+ let refreshRunDeferred = false;
126848
+ const REFRESH_DEFERRED = /* @__PURE__ */ Symbol("deferred refresh");
126849
+ const runDeferredRefresh = () => {
126850
+ if (!refreshDeferred || paysInFlight !== 0 || finalityWait || refreshRun || finalityClosed) return;
126851
+ refreshDeferred = false;
126852
+ refreshShared({ background: true }).catch(() => {
126853
+ });
126854
+ };
126855
+ const refreshWalk = ops.refreshWalk ?? (ops.refresh ? void 0 : (store, share) => walkWalletHistory(session, store, share));
126749
126856
  const errorCode2 = (error, fallback) => describeError(error, { fallback, safeText: SAFE_TEXT }).split(/[\s:;(]/, 1)[0];
126750
126857
  const underLease = (task) => session.serial(async () => {
126751
126858
  let release;
@@ -126844,10 +126951,16 @@ function createWalletTools({
126844
126951
  };
126845
126952
  const withPoolSwitch = (tool) => ({ ...tool, run: async (...args) => {
126846
126953
  let output2;
126954
+ const paying = tool.name === "x402_pay";
126955
+ if (paying) paysInFlight += 1;
126847
126956
  try {
126848
126957
  output2 = await tool.run(...args);
126849
126958
  } finally {
126850
126959
  if (tool.annotations?.readOnlyHint !== true) lastWriterEndedAt = Date.now();
126960
+ if (paying) {
126961
+ paysInFlight -= 1;
126962
+ runDeferredRefresh();
126963
+ }
126851
126964
  }
126852
126965
  if (!succession || !output2 || output2.isError || typeof output2.text !== "string") return output2;
126853
126966
  const read = readSwitch();
@@ -126896,9 +127009,20 @@ function createWalletTools({
126896
127009
  });
126897
127010
  if (ops.oneTimeAdopt !== false) adopter.start();
126898
127011
  function refreshShared({ background = false } = {}) {
127012
+ if (refreshRun && !background && refreshRunDeferred) return refreshRun.then(() => refreshShared(), () => refreshShared());
127013
+ if (refreshRun && !background) refreshForeground = true;
126899
127014
  if (!refreshRun) {
126900
127015
  refreshInBackground = background;
127016
+ refreshForeground = !background;
127017
+ refreshRunDeferred = false;
126901
127018
  const calledAt = Date.now();
127019
+ const deferNow = () => {
127020
+ if (!background || refreshForeground || paysInFlight === 0) return false;
127021
+ refreshRunDeferred = true;
127022
+ refreshDeferred = true;
127023
+ log("MCP_REFRESH_DEFERRED");
127024
+ return true;
127025
+ };
126902
127026
  refreshRun = (async () => {
126903
127027
  let early;
126904
127028
  if (background && refreshWalk) {
@@ -126906,18 +127030,21 @@ function createWalletTools({
126906
127030
  const store = session.openStore();
126907
127031
  const records = JSON.stringify(captureWalStates(store));
126908
127032
  const pool2 = store.state.pool;
126909
- early = { records, pool: pool2, history: await refreshWalk(store) };
127033
+ early = { records, pool: pool2, history: await refreshWalk(store, { notBeforeMs: Math.max(calledAt, lastWriterEndedAt) }) };
126910
127034
  } catch {
126911
127035
  early = void 0;
126912
127036
  }
126913
127037
  }
126914
127038
  return underLease((store) => {
127039
+ if (deferNow()) return REFRESH_DEFERRED;
127040
+ refreshDeferred = false;
126915
127041
  const same2 = early !== void 0 && early.pool === store.state.pool && early.records === JSON.stringify(captureWalStates(store));
126916
127042
  if (early !== void 0 && !same2) log("MCP_REFRESH_WALK_AGAIN");
126917
127043
  const share = early !== void 0 && !same2 ? { notBeforeMs: Math.max(calledAt, lastWriterEndedAt) } : void 0;
126918
127044
  return refreshStep(store, same2 ? early.history : void 0, share);
126919
127045
  });
126920
- })().then(() => {
127046
+ })().then((result) => {
127047
+ if (result === REFRESH_DEFERRED) return;
126921
127048
  refreshFailedAt = 0;
126922
127049
  log("MCP_REFRESH_DONE");
126923
127050
  }, (error) => {
@@ -126926,8 +127053,12 @@ function createWalletTools({
126926
127053
  log(`MCP_REFRESH_FAILED ${refreshFailedCode}`);
126927
127054
  throw error;
126928
127055
  }).finally(() => {
127056
+ const deferred = refreshRunDeferred;
126929
127057
  refreshRun = void 0;
126930
127058
  refreshInBackground = false;
127059
+ refreshForeground = false;
127060
+ refreshRunDeferred = false;
127061
+ if (deferred || refreshDeferred) runDeferredRefresh();
126931
127062
  });
126932
127063
  }
126933
127064
  return refreshRun;
@@ -126998,6 +127129,7 @@ function createWalletTools({
126998
127129
  finalityWait = void 0;
126999
127130
  if (finalityClosed || finalityTarget === watched) finalityTarget = void 0;
127000
127131
  else startFinalityWait();
127132
+ runDeferredRefresh();
127001
127133
  });
127002
127134
  }
127003
127135
  async function refreshIfStale() {
@@ -127418,7 +127550,7 @@ function createWalletTools({
127418
127550
  let refresh = {};
127419
127551
  const recentlyFailed = refreshFailedAt && Date.now() - refreshFailedAt < REFRESH_RETRY_MS && !refreshRun;
127420
127552
  if (report.split?.shieldedAtomic != null && report.status === "stale") {
127421
- if (!recentlyFailed && !finalityWait) refreshShared().catch(() => {
127553
+ if (!recentlyFailed && (!finalityWait || refreshDeferred)) refreshShared().catch(() => {
127422
127554
  });
127423
127555
  refresh = { refreshing: true };
127424
127556
  } else if (report.split?.shieldedAtomic == null && recentlyFailed) {