shinjuku-shielded 0.3.31 → 0.3.33

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": "c8c82dc4ce0277f8c560adec5cb16119709d00240a8efa4ebbb2a426e5161b6a",
5
- "bytes": 11178235,
6
- "sourceCommit": "206a11827c4c668d65b7e8e2040a344f17aad1d0",
4
+ "sha256": "dae9ba8800e48179602d5595c5bbc71029a758be726e72482d24a52126649ac9",
5
+ "bytes": 11184999,
6
+ "sourceCommit": "5a445fbb0e06e50a3365809a078f3d5b0de03c51",
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.31",
3
+ "version": "0.3.33",
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": "c8c82dc4",
7
- "shinjukuSha256": "c8c82dc4ce0277f8c560adec5cb16119709d00240a8efa4ebbb2a426e5161b6a",
6
+ "shinjukuRelease": "dae9ba88",
7
+ "shinjukuSha256": "dae9ba8800e48179602d5595c5bbc71029a758be726e72482d24a52126649ac9",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -46392,6 +46392,28 @@ var init_ohttp = __esm({
46392
46392
  }
46393
46393
  });
46394
46394
 
46395
+ // wallet/src/pay/phaseTiming.mjs
46396
+ import { AsyncLocalStorage } from "node:async_hooks";
46397
+ function withPhaseScope(mark, prefix, fn) {
46398
+ if (typeof mark !== "function") return fn();
46399
+ const outer = scope.getStore();
46400
+ const write = (phase) => mark(`${prefix}:${phase}`);
46401
+ return scope.run(outer ? (phase) => outer(`${prefix}:${phase}`) : write, fn);
46402
+ }
46403
+ function markPhase(phase) {
46404
+ try {
46405
+ scope.getStore()?.(phase);
46406
+ } catch {
46407
+ }
46408
+ }
46409
+ var scope;
46410
+ var init_phaseTiming = __esm({
46411
+ "wallet/src/pay/phaseTiming.mjs"() {
46412
+ "use strict";
46413
+ scope = new AsyncLocalStorage();
46414
+ }
46415
+ });
46416
+
46395
46417
  // wallet/src/net/proxy.mjs
46396
46418
  import { spawn } from "node:child_process";
46397
46419
  import { randomBytes as randomBytes5 } from "node:crypto";
@@ -46575,7 +46597,15 @@ function describeRpcPost(input, body) {
46575
46597
  const calls = Array.isArray(parsed) ? parsed : [parsed];
46576
46598
  const resendable = calls.length > 0 && calls.every((call3) => typeof call3?.method === "string" && RESENDABLE_METHOD.test(call3.method));
46577
46599
  const method = Array.isArray(parsed) ? "batch" : parsed?.method;
46578
- return { key: `${new URL(input instanceof Request ? input.url : String(input)).origin} ${method ?? ""}`, resendable };
46600
+ return {
46601
+ key: `${new URL(input instanceof Request ? input.url : String(input)).origin} ${method ?? ""}`,
46602
+ resendable,
46603
+ // The method in a timing mark: a JSON-RPC method name only, else "other".
46604
+ mark: typeof method === "string" && /^[A-Za-z]{1,40}$/.test(method) ? method : "other"
46605
+ };
46606
+ }
46607
+ function markRefusal(response, post) {
46608
+ if (response.status === 429 || response.status === 503) markPhase(`rpc-${response.status}-${post?.mark ?? "other"}`);
46579
46609
  }
46580
46610
  function noteAnswer(response) {
46581
46611
  if (!response.ok) return;
@@ -46607,6 +46637,7 @@ async function rpcFetch(input, init) {
46607
46637
  if (!post?.resendable) {
46608
46638
  const response = await fetch(input, init);
46609
46639
  noteAnswer(response);
46640
+ markRefusal(response, post);
46610
46641
  return response;
46611
46642
  }
46612
46643
  let strikes = 0;
@@ -46617,9 +46648,11 @@ async function rpcFetch(input, init) {
46617
46648
  if (stop2) throw new Error(`rpc_rate_limited: ${stop2}`);
46618
46649
  waited = true;
46619
46650
  }
46651
+ if (waited) markPhase(`rpc-paced-${post.mark}`);
46620
46652
  const circuit = torRotations;
46621
46653
  const response = await fetch(input, init);
46622
46654
  noteAnswer(response);
46655
+ markRefusal(response, post);
46623
46656
  if (response.status !== 429) return response;
46624
46657
  await response.body?.cancel().catch(() => {
46625
46658
  });
@@ -46709,6 +46742,7 @@ var init_proxy = __esm({
46709
46742
  "use strict";
46710
46743
  init_facilitatorOnion();
46711
46744
  init_ohttp();
46745
+ init_phaseTiming();
46712
46746
  TOR_HTTP_TUNNEL = "http://127.0.0.1:9080";
46713
46747
  TOR_CHECK = "https://check.torproject.org/api/ip";
46714
46748
  PROOF_TIMEOUT_MS = 2e4;
@@ -49625,28 +49659,6 @@ var init_serviceUrl = __esm({
49625
49659
  }
49626
49660
  });
49627
49661
 
49628
- // wallet/src/pay/phaseTiming.mjs
49629
- import { AsyncLocalStorage } from "node:async_hooks";
49630
- function withPhaseScope(mark, prefix, fn) {
49631
- if (typeof mark !== "function") return fn();
49632
- const outer = scope.getStore();
49633
- const write = (phase) => mark(`${prefix}:${phase}`);
49634
- return scope.run(outer ? (phase) => outer(`${prefix}:${phase}`) : write, fn);
49635
- }
49636
- function markPhase(phase) {
49637
- try {
49638
- scope.getStore()?.(phase);
49639
- } catch {
49640
- }
49641
- }
49642
- var scope;
49643
- var init_phaseTiming = __esm({
49644
- "wallet/src/pay/phaseTiming.mjs"() {
49645
- "use strict";
49646
- scope = new AsyncLocalStorage();
49647
- }
49648
- });
49649
-
49650
49662
  // wallet/src/anchorClock.mjs
49651
49663
  function observePoolAnchors(state, nowMs = Date.now()) {
49652
49664
  try {
@@ -50445,8 +50457,9 @@ function createMemoReader(baseUrl, { fetchImpl = fetch, pause: pause3 = pauseFor
50445
50457
  };
50446
50458
  const origin = new URL(baseUrl).origin;
50447
50459
  const keyOf2 = (digest2) => `${origin}|${digest2}`;
50448
- let ahead = prefetchedMemosByFetch.get(fetchImpl);
50449
- if (ahead === void 0) prefetchedMemosByFetch.set(fetchImpl, ahead = /* @__PURE__ */ new Map());
50460
+ const aheadKey = typeof fetchImpl?.baseFetch === "function" ? fetchImpl.baseFetch : fetchImpl;
50461
+ let ahead = prefetchedMemosByFetch.get(aheadKey);
50462
+ if (ahead === void 0) prefetchedMemosByFetch.set(aheadKey, ahead = /* @__PURE__ */ new Map());
50450
50463
  const live = (key) => {
50451
50464
  const entry = ahead.get(key);
50452
50465
  if (entry !== void 0 && now() - entry.at > MEMO_PREFETCH_LIFE_MS) ahead.delete(key);
@@ -54852,6 +54865,23 @@ function foldCompactLeaves(leaves, counts) {
54852
54865
  });
54853
54866
  return roots;
54854
54867
  }
54868
+ function foldCompactLeavesReusing(pool2, leaves, counts) {
54869
+ const prior = lastFolds.get(pool2);
54870
+ let common = 0;
54871
+ if (prior !== void 0) {
54872
+ const most = Math.min(prior.leaves.length, leaves.length);
54873
+ while (common < most && prior.leaves[common] === leaves[common]) common += 1;
54874
+ }
54875
+ const known = (at) => prior !== void 0 && at <= common && prior.roots.has(at);
54876
+ const roots = foldCompactLeaves(leaves, new Set([...counts].filter((at) => !known(at))));
54877
+ for (const at of counts) if (known(at)) roots.set(at, prior.roots.get(at));
54878
+ const kept = new Map(roots);
54879
+ if (prior !== void 0) {
54880
+ for (const [at, root] of prior.roots) if (at <= common && !kept.has(at)) kept.set(at, root);
54881
+ }
54882
+ lastFolds.set(pool2, { leaves: leaves.slice(), roots: kept });
54883
+ return roots;
54884
+ }
54855
54885
  function fieldOf(hex4) {
54856
54886
  if (typeof hex4 !== "string" || !HEX64.test(hex4)) throw refuse3("p03_compact_history_record_invalid");
54857
54887
  const value = BigInt(`0x${hex4}`);
@@ -55062,7 +55092,7 @@ function withBoundedOwnReads(connection) {
55062
55092
  }
55063
55093
  });
55064
55094
  }
55065
- async function readPages(history, pool2, needed, first) {
55095
+ async function readPages(history, pool2, needed, first, { wakeSlot } = {}) {
55066
55096
  const records = [];
55067
55097
  const imports = [];
55068
55098
  let initialization;
@@ -55093,7 +55123,19 @@ async function readPages(history, pool2, needed, first) {
55093
55123
  initialization = result.initialization;
55094
55124
  syncedSlot = result.syncedSlot;
55095
55125
  records.push(...result.records);
55096
- if (result.total < needed) throw refuse3("p03_compact_history_source_behind");
55126
+ if (result.total < needed) {
55127
+ if (isIndex(wakeSlot)) {
55128
+ Promise.resolve().then(() => history._rpcRequest("getP03Transitions", [pool2, {
55129
+ commitment: "finalized",
55130
+ fromOrdinal: 0,
55131
+ toOrdinal: 1,
55132
+ minContextSlot: wakeSlot
55133
+ }])).catch(() => {
55134
+ });
55135
+ markPhase("compact-behind-wake");
55136
+ }
55137
+ throw refuse3("p03_compact_history_source_behind");
55138
+ }
55097
55139
  }
55098
55140
  return { records: records.slice(0, needed), imports, initialization, syncedSlot };
55099
55141
  }
@@ -55322,7 +55364,12 @@ async function readCompactP03History({
55322
55364
  knownSnapshot,
55323
55365
  // compactP03HistoryReport: run beside the spot checks once the initialization is verified
55324
55366
  // ({ snapshot, initialization, contexts }); its result comes back as `alongside`.
55325
- alongside
55367
+ alongside,
55368
+ // Called once with the served contexts in chain order, before the fold and every own-RPC
55369
+ // check: they are NOT verified yet. A payment asks the memo service for their memos here
55370
+ // (payAndFetch.mjs), and uses an answer only for a context the verified report names. Its
55371
+ // result and any throw are ignored.
55372
+ onList
55326
55373
  }) {
55327
55374
  const program = new import_web397.PublicKey(programId);
55328
55375
  const poolKey = new import_web397.PublicKey(pool2);
@@ -55349,7 +55396,7 @@ async function readCompactP03History({
55349
55396
  initialization: served.initialization,
55350
55397
  syncedSlot: served.syncedSlot,
55351
55398
  cached: true
55352
- } : await readPages(source, poolString, count2, first);
55399
+ } : await readPages(source, poolString, count2, first, { wakeSlot: snapshot.slot });
55353
55400
  markPhase(page.cached === true ? "compact-list-cached" : "compact-list");
55354
55401
  const binding = {
55355
55402
  programId: programString,
@@ -55375,6 +55422,10 @@ async function readCompactP03History({
55375
55422
  }
55376
55423
  });
55377
55424
  const imports = orderCompactImports(page.imports, contexts, { snapshot, replay });
55425
+ try {
55426
+ onList?.(contexts);
55427
+ } catch {
55428
+ }
55378
55429
  const picks = [];
55379
55430
  const order = [...contexts.keys()];
55380
55431
  const checks = page.cached === true ? 0 : spotChecks;
@@ -55406,7 +55457,7 @@ async function readCompactP03History({
55406
55457
  for (const anchor of anchors) counts.add(Number(anchor.index / SUBTREE_LEAVES3));
55407
55458
  if (appendRoots) for (let at = 1; at <= count2; at += 1) counts.add(at);
55408
55459
  for (const at of counts) if (at > count2) throw refuse3("p03_compact_history_snapshot_invalid");
55409
- roots = foldCompactLeaves(contexts.map((context) => context.outCommitment), counts);
55460
+ roots = foldCompactLeavesReusing(poolString, contexts.map((context) => context.outCommitment), counts);
55410
55461
  if (roots.get(count2) !== snapshot.currentRoot) throw refuse3("p03_compact_history_root_mismatch");
55411
55462
  for (const anchor of anchors) {
55412
55463
  if (roots.get(Number(anchor.index / SUBTREE_LEAVES3)) !== anchor.root) throw refuse3("p03_compact_history_root_mismatch");
@@ -55470,7 +55521,8 @@ async function compactP03HistoryReport({
55470
55521
  forceRead = false,
55471
55522
  freshMs = 0,
55472
55523
  priorAttestation,
55473
- knownSnapshot
55524
+ knownSnapshot,
55525
+ onList
55474
55526
  }) {
55475
55527
  if (deploymentIdentity === void 0) throw new Error("p03_history_deployment_identity_required");
55476
55528
  const program = new import_web397.PublicKey(programId);
@@ -55539,6 +55591,7 @@ async function compactP03HistoryReport({
55539
55591
  pool: pool2,
55540
55592
  alongside: attest,
55541
55593
  ...knownSnapshot !== void 0 ? { knownSnapshot } : {},
55594
+ ...onList !== void 0 ? { onList } : {},
55542
55595
  ...warm ? { served: cached.served } : {},
55543
55596
  ...cached ? { initialization: cached.initialization } : {}
55544
55597
  });
@@ -55616,7 +55669,7 @@ async function tryCompactP03HistoryReport(options) {
55616
55669
  return { ok: false, reason: refusalReason(error), ...typeof error?.detail === "string" ? { detail: error.detail } : {}, error };
55617
55670
  }
55618
55671
  }
55619
- var import_web397, P03_COMPACT_HISTORY_SOURCE_MODEL, P03_COMPACT_PAGE, P03_COMPACT_SPOT_CHECKS, FIELD_MODULUS5, SUBTREE_LEAVES3, LOCAL_DEPTH2, OUTER_DEPTH2, PRIMARY_PROOF_BYTES2, FIELD_BYTES3, ED25519_SPKI_PREFIX2, VERSION_1_PREFIX2, HEX64, BASE58, REPLAY_STORE_INDEXED2, PROFILES2, MOVE_HEAD_BYTES2, MOVE_INSERTED_VALUES2, refuse3, hash4, bitAt, zeroRootsMemo, isSignature, isIndex, IMPORT_KEYS, IMPORT_MAX_VALUES, LIST_ATTEMPTS, OWN_READ_TIMEOUT_MS, BOUNDED_OWN_METHODS;
55672
+ var import_web397, P03_COMPACT_HISTORY_SOURCE_MODEL, P03_COMPACT_PAGE, P03_COMPACT_SPOT_CHECKS, FIELD_MODULUS5, SUBTREE_LEAVES3, LOCAL_DEPTH2, OUTER_DEPTH2, PRIMARY_PROOF_BYTES2, FIELD_BYTES3, ED25519_SPKI_PREFIX2, VERSION_1_PREFIX2, HEX64, BASE58, REPLAY_STORE_INDEXED2, PROFILES2, MOVE_HEAD_BYTES2, MOVE_INSERTED_VALUES2, refuse3, hash4, bitAt, zeroRootsMemo, lastFolds, isSignature, isIndex, IMPORT_KEYS, IMPORT_MAX_VALUES, LIST_ATTEMPTS, OWN_READ_TIMEOUT_MS, BOUNDED_OWN_METHODS;
55620
55673
  var init_compact = __esm({
55621
55674
  "wallet/src/history/compact.mjs"() {
55622
55675
  "use strict";
@@ -55727,6 +55780,7 @@ var init_compact = __esm({
55727
55780
  };
55728
55781
  hash4 = (left, right) => poseidon2([left, right], 3);
55729
55782
  bitAt = (value, level) => Math.floor(value / 2 ** level) % 2;
55783
+ lastFolds = /* @__PURE__ */ new Map();
55730
55784
  isSignature = (value) => typeof value === "string" && value.length >= 64 && value.length <= 88 && BASE58.test(value);
55731
55785
  isIndex = (value) => Number.isSafeInteger(value) && value >= 0;
55732
55786
  IMPORT_KEYS = ["afterOrdinal", "instructionIndex", "record", "signature", "slot", "transactionIndex", "values"];
@@ -56299,7 +56353,7 @@ function prewarmWalletP03History(args) {
56299
56353
  return false;
56300
56354
  }
56301
56355
  }
56302
- async function loadWalletP03HistoryNow({ store, connection: given, programId, pool: pool2, deploymentIdentity, cache, forceRead = false }) {
56356
+ async function loadWalletP03HistoryNow({ store, connection: given, programId, pool: pool2, deploymentIdentity, cache, forceRead = false, onCompactList }) {
56303
56357
  const { connection, source } = resolveHistorySource(given, pool2);
56304
56358
  announceHistorySource(source);
56305
56359
  const split5 = connection?.historyRpc;
@@ -56329,7 +56383,17 @@ async function loadWalletP03HistoryNow({ store, connection: given, programId, po
56329
56383
  }
56330
56384
  }
56331
56385
  if (split5 && cache === void 0 && (source === "pinned" || source === "flag")) {
56332
- const compact = await compactFirst({ store, connection, source, programId, pool: pool2, deploymentIdentity, forceRead, knownSnapshot: movedSnapshot });
56386
+ const compact = await compactFirst({
56387
+ store,
56388
+ connection,
56389
+ source,
56390
+ programId,
56391
+ pool: pool2,
56392
+ deploymentIdentity,
56393
+ forceRead,
56394
+ knownSnapshot: movedSnapshot,
56395
+ onCompactList
56396
+ });
56333
56397
  if (compact !== void 0) {
56334
56398
  if (runKey !== void 0) rememberRunReport(runKey, compact);
56335
56399
  return compact;
@@ -56369,7 +56433,7 @@ async function loadWalletP03HistoryNow({ store, connection: given, programId, po
56369
56433
  if (runKey !== void 0) rememberRunReport(runKey, result.report);
56370
56434
  return result.report;
56371
56435
  }
56372
- async function compactFirst({ store, connection, source, programId, pool: pool2, deploymentIdentity, forceRead, knownSnapshot }) {
56436
+ async function compactFirst({ store, connection, source, programId, pool: pool2, deploymentIdentity, forceRead, knownSnapshot, onCompactList }) {
56373
56437
  const started = Date.now();
56374
56438
  let behindNoticed = false;
56375
56439
  let behindTries = 0;
@@ -56380,6 +56444,8 @@ async function compactFirst({ store, connection, source, programId, pool: pool2,
56380
56444
  pool: pool2,
56381
56445
  deploymentIdentity,
56382
56446
  forceRead,
56447
+ // The served list, unverified, before its fold (compact.mjs onList): a payment's memo prefetch.
56448
+ ...typeof onCompactList === "function" ? { onList: onCompactList } : {},
56383
56449
  cache: walletCompactHistoryCache(store),
56384
56450
  freshMs: HISTORY_WARM_FRESH_MS,
56385
56451
  // The walk cache's attestation, read only when the compact cache holds none.
@@ -60363,7 +60429,7 @@ async function runRecover({ store, connection, programId, digests, serviceUrl: s
60363
60429
  const pool2 = new import_web3102.PublicKey(state.pool);
60364
60430
  const chainProgramId = new import_web3102.PublicKey(programId);
60365
60431
  const poolStateRead = (async () => {
60366
- const poolState3 = await readPoolState2(connection, pool2);
60432
+ const poolState3 = await (deps.poolState ?? readPoolState2(connection, pool2));
60367
60433
  if (!poolState3) throw new Error("pool account not found");
60368
60434
  if (!poolState3.owner.equals(chainProgramId)) throw new Error("pool account owner mismatch");
60369
60435
  markPhase("recover-pool");
@@ -62405,6 +62471,7 @@ var init_errorCodes = __esm({
62405
62471
  "fetch_url_refused",
62406
62472
  "fetch_offer_unsupported",
62407
62473
  "fetch_challenge_invalid",
62474
+ "fetch_seller_unreachable",
62408
62475
  "fetch_session_closed",
62409
62476
  "fetch_max_payment_required",
62410
62477
  "mcp_cap_required",
@@ -71909,6 +71976,9 @@ async function prepareRecoveredPayment({
71909
71976
  const generator = pinnedGenerator4();
71910
71977
  const viewing = deriveWalletViewingAuthority2({ seedBytes: seedBytes5(state), generator });
71911
71978
  const chainTime2 = deps.chainTime === void 0 ? startFinalizedChainTime(connection) : void 0;
71979
+ const poolState2 = deps.recover === void 0 ? readPoolState2(connection, pool2) : void 0;
71980
+ poolState2?.catch(() => {
71981
+ });
71912
71982
  if (deps.provePayment === void 0 && proofTools !== void 0) {
71913
71983
  (deps.prewarmProof ?? ((tools) => setImmediate(() => {
71914
71984
  prewarmP03PaymentProofTools(tools).catch(() => {
@@ -71982,7 +72052,12 @@ async function prepareRecoveredPayment({
71982
72052
  digests,
71983
72053
  serviceUrl: serviceUrl2,
71984
72054
  fresh: true,
71985
- deps: { fetchImpl: deps.fetchImpl, finalizedHistory: history, transitionContextFor: history.transitionContextFor }
72055
+ deps: {
72056
+ fetchImpl: deps.fetchImpl,
72057
+ finalizedHistory: history,
72058
+ transitionContextFor: history.transitionContextFor,
72059
+ ...poolState2 !== void 0 ? { poolState: poolState2 } : {}
72060
+ }
71986
72061
  });
71987
72062
  markPhase("recover");
71988
72063
  const unspentAccounts = recovery.recovered.filter((entry) => entry.kind === "account" && entry.spendStatus === "unspent");
@@ -72225,6 +72300,7 @@ var init_prepareRecoveredPayment = __esm({
72225
72300
  init_recoveryPath();
72226
72301
  init_recoveredPayment();
72227
72302
  init_recover();
72303
+ init_chain2();
72228
72304
  init_notes2();
72229
72305
  init_accountSelection();
72230
72306
  init_phaseTiming();
@@ -91169,6 +91245,7 @@ async function readJson(response, limit) {
91169
91245
  }
91170
91246
  }
91171
91247
  async function readFloatConfig({ origin, fetchImpl = fetch }) {
91248
+ const readAtMs = Date.now();
91172
91249
  let response;
91173
91250
  try {
91174
91251
  response = await fetchImpl(
@@ -91202,7 +91279,7 @@ async function readFloatConfig({ origin, fetchImpl = fetch }) {
91202
91279
  if (config?.schema !== FLOAT_CONFIG_SCHEMA || !DECIMAL3.test(String(config.feeAtomic ?? "")) || !DECIMAL3.test(String(config.largestAtomic ?? "")) || typeof config.network !== "string" || typeof config.asset !== "string" || !prefix || prefix.pathname !== FLOAT_PAY_PATH || prefix.href !== config.payUrlPrefix || !feePayerOk) {
91203
91280
  fail18("p03_float_config_unreadable");
91204
91281
  }
91205
- return {
91282
+ const read = {
91206
91283
  feeAtomic: String(config.feeAtomic),
91207
91284
  largestAtomic: String(config.largestAtomic),
91208
91285
  network: config.network,
@@ -91210,6 +91287,23 @@ async function readFloatConfig({ origin, fetchImpl = fetch }) {
91210
91287
  payUrlPrefix: config.payUrlPrefix,
91211
91288
  pocketFeePayer: config.pocketFeePayer
91212
91289
  };
91290
+ configCache.set(origin, { config: read, readAtMs });
91291
+ return read;
91292
+ }
91293
+ function dropFloatConfigs() {
91294
+ configCache.clear();
91295
+ }
91296
+ async function readFloatOffer({ origin, paymentRequired, store, fetchImpl = fetch }) {
91297
+ const kept = configCache.get(origin);
91298
+ if (kept && Date.now() - kept.readAtMs < CONFIG_CACHE_MS) {
91299
+ try {
91300
+ return { offer: floatOffer({ paymentRequired, store, config: kept.config }), config: kept.config };
91301
+ } catch {
91302
+ configCache.delete(origin);
91303
+ }
91304
+ }
91305
+ const config = await readFloatConfig({ origin, fetchImpl });
91306
+ return { offer: floatOffer({ paymentRequired, store, config }), config };
91213
91307
  }
91214
91308
  function floatOriginOf(floatOrigin, serviceUrl2) {
91215
91309
  const value = floatOrigin ?? serviceUrl2;
@@ -91303,7 +91397,7 @@ function withSellerResource(xPayment, record) {
91303
91397
  return patched;
91304
91398
  }
91305
91399
  function floatFetch(base, seen) {
91306
- return async (target, init = {}) => {
91400
+ const wrapper = async (target, init = {}) => {
91307
91401
  const response = await base(target, keepAliveInit(init));
91308
91402
  const paid = Object.keys(init.headers ?? {}).some((name) => name.toLowerCase() === "payment-signature");
91309
91403
  if (paid || response.status === 402 || response.status < 400) return response;
@@ -91325,6 +91419,7 @@ function floatFetch(base, seen) {
91325
91419
  };
91326
91420
  return new Response(null, { status: 400 });
91327
91421
  };
91422
+ return Object.defineProperty(wrapper, "baseFetch", { value: base });
91328
91423
  }
91329
91424
  function floatRefusalError(refusal2, record) {
91330
91425
  const extra = {
@@ -91471,6 +91566,7 @@ async function payViaFloat({
91471
91566
  continue;
91472
91567
  }
91473
91568
  dropUnsent();
91569
+ dropFloatConfigs();
91474
91570
  throw floatRefusalError(seen.refusal, record);
91475
91571
  }
91476
91572
  if (PENDING_CODES.has(error?.sellerCode) && Date.now() < finishingUntil) {
@@ -91572,7 +91668,7 @@ function paidResult(record, replayed) {
91572
91668
  float: { floatId: record.floatId }
91573
91669
  };
91574
91670
  }
91575
- var import_web3109, FLOAT_PAYMENT_SCHEMA, REGENERATE_CODES, FLOAT_PAYMENT_WAL_PREFIX, FLOAT_CONFIG_SCHEMA, FLOAT_PAY_PATH, MAX_BODY_BYTES, FLOAT_REFUND_ETA_SECONDS, REFUND_SETTLED_AFTER_MS, FINISHING_POLL_MS, FINISHING_LIMIT_MS, SELLER_TIMEOUT_MS, CONFIG_TIMEOUT_MS, DECIMAL3, POSITIVE, PENDING_CODES, hash8, fail18, floatPaymentWalName, plainText;
91671
+ var import_web3109, FLOAT_PAYMENT_SCHEMA, REGENERATE_CODES, FLOAT_PAYMENT_WAL_PREFIX, FLOAT_CONFIG_SCHEMA, FLOAT_PAY_PATH, MAX_BODY_BYTES, FLOAT_REFUND_ETA_SECONDS, REFUND_SETTLED_AFTER_MS, FINISHING_POLL_MS, FINISHING_LIMIT_MS, SELLER_TIMEOUT_MS, CONFIG_TIMEOUT_MS, CONFIG_CACHE_MS, DECIMAL3, POSITIVE, PENDING_CODES, hash8, fail18, floatPaymentWalName, configCache, plainText;
91576
91672
  var init_floatPay = __esm({
91577
91673
  "wallet/src/pay/floatPay.mjs"() {
91578
91674
  "use strict";
@@ -91601,6 +91697,7 @@ var init_floatPay = __esm({
91601
91697
  FINISHING_LIMIT_MS = 6e4;
91602
91698
  SELLER_TIMEOUT_MS = 12e4;
91603
91699
  CONFIG_TIMEOUT_MS = 3e4;
91700
+ CONFIG_CACHE_MS = 6e4;
91604
91701
  DECIMAL3 = /^(0|[1-9][0-9]*)$/;
91605
91702
  POSITIVE = /^[1-9][0-9]*$/;
91606
91703
  PENDING_CODES = /* @__PURE__ */ new Set(["float_paid_finishing", "payment_pending_retry_same_payment"]);
@@ -91609,6 +91706,7 @@ var init_floatPay = __esm({
91609
91706
  throw Object.assign(new Error(code), extra);
91610
91707
  };
91611
91708
  floatPaymentWalName = (requestKey) => `${FLOAT_PAYMENT_WAL_PREFIX}${requestKey}`;
91709
+ configCache = /* @__PURE__ */ new Map();
91612
91710
  plainText = (value) => value.replace(/[^ -~]+/g, " ").trim().slice(0, 300);
91613
91711
  }
91614
91712
  });
@@ -117790,6 +117888,7 @@ init_payResource();
117790
117888
  init_quote2();
117791
117889
  init_floatPay();
117792
117890
  init_walletHistory();
117891
+ init_recover();
117793
117892
  init_keepAlive();
117794
117893
  init_phaseTiming();
117795
117894
  init_chain2();
@@ -118065,8 +118164,8 @@ async function offeredPrice(paymentRequired, store, serviceUrl2) {
118065
118164
  }
118066
118165
  if (rail === "exact" && store) {
118067
118166
  const origin = new URL(serviceUrl2).origin;
118068
- const config = await readFloatConfig({ origin });
118069
- return { rail, amountAtomic: floatOffer({ paymentRequired, store, config }).paidAtomic, floatConfig: { origin, config } };
118167
+ const { offer, config } = await readFloatOffer({ origin, paymentRequired, store });
118168
+ return { rail, amountAtomic: offer.paidAtomic, floatConfig: { origin, config } };
118070
118169
  }
118071
118170
  let highest;
118072
118171
  for (const offer of paymentRequired.accepts) {
@@ -118140,7 +118239,7 @@ async function payAndFetch(session, url, init = {}, opts = {}) {
118140
118239
  let challenge3;
118141
118240
  if (!rerun) {
118142
118241
  if (store !== void 0) startHistoryPrewarm(session, store, mark);
118143
- const response = await fetch(target, keepAliveInit({
118242
+ const response = await unpaidRequest(target, () => keepAliveInit({
118144
118243
  method,
118145
118244
  headers,
118146
118245
  ...body ? { body } : {},
@@ -118183,6 +118282,19 @@ async function payAndFetch(session, url, init = {}, opts = {}) {
118183
118282
  throw error;
118184
118283
  }
118185
118284
  }
118285
+ var UNPAID_NOT_SENT = /* @__PURE__ */ new Set(["UND_ERR_CONNECT_TIMEOUT", "ECONNREFUSED"]);
118286
+ async function unpaidRequest(target, init) {
118287
+ for (let attempt = 0; ; attempt += 1) {
118288
+ try {
118289
+ return await fetch(target, init());
118290
+ } catch (error) {
118291
+ const code = typeof error?.cause?.code === "string" && /^[A-Z][A-Z0-9_]{2,40}$/.test(error.cause.code) ? error.cause.code : void 0;
118292
+ if (attempt === 0 && UNPAID_NOT_SENT.has(code)) continue;
118293
+ const cause = error?.name === "TimeoutError" ? "no answer in 120 s" : code ?? "network error";
118294
+ fail20("fetch_seller_unreachable", `the seller did not answer the first, unpaid request (${cause}); nothing was paid`);
118295
+ }
118296
+ }
118297
+ }
118186
118298
  function startHistoryPrewarm(session, store, mark) {
118187
118299
  try {
118188
118300
  withPhaseScope(mark, "prewarm", () => prewarmWalletP03History({
@@ -118190,7 +118302,16 @@ function startHistoryPrewarm(session, store, mark) {
118190
118302
  connection: connectSplit(session.connection, session.historyUrl),
118191
118303
  programId: session.programId,
118192
118304
  pool: store.state.pool,
118193
- deploymentIdentity: session.proofTools.deploymentIdentity
118305
+ deploymentIdentity: session.proofTools.deploymentIdentity,
118306
+ // The memos the proof's recovery will read, asked when the compact list arrives, beside
118307
+ // its fold, spot checks, and attestation (B2 #3, 2026-10-15: the recovery waited 0.18-0.44 s
118308
+ // for them after the walk, M). The same set the recovery asks (every listed transition
118309
+ // its cache lacks); it takes an answer only for a transition of the verified report
118310
+ // (chain.mjs createMemoReader prefetch; recover.mjs checks each bundle's digest).
118311
+ onCompactList: (contexts) => {
118312
+ markPhase("memo-prefetch");
118313
+ prefetchRecoveryMemos({ store, finalizedHistory: { contexts }, serviceUrl: session.memoServiceUrl, programId: session.programId });
118314
+ }
118194
118315
  }));
118195
118316
  } catch {
118196
118317
  }
@@ -125294,8 +125415,7 @@ async function railOffer(paymentRequired, rail, store, facilitator) {
125294
125415
  }
125295
125416
  try {
125296
125417
  if (!facilitator) throw new Error("p03_float_origin_missing");
125297
- const config = await readFloatConfig({ origin: new URL(facilitator).origin });
125298
- const offer = floatOffer({ paymentRequired, store, config });
125418
+ const { offer } = await readFloatOffer({ origin: new URL(facilitator).origin, paymentRequired, store });
125299
125419
  return { priceAtomic: offer.priceAtomic, feeAtomic: offer.feeAtomic, totalAtomic: offer.paidAtomic };
125300
125420
  } catch (error) {
125301
125421
  const refusal2 = floatRefusal(error) ?? ["p03_float_config_unreadable", "the price through Shinjuku could not be read; nothing was paid", "call x402_preview again in a minute"];
@@ -125546,15 +125666,16 @@ async function runShieldFromElsewhere({ session, depositId, amountAtomic, onStag
125546
125666
  const result = await (steps.submit ?? submitRelayedDeposit)(common);
125547
125667
  return { state: result.state, transaction: typeof result.receipt?.signature === "string" ? result.receipt.signature : null };
125548
125668
  }
125549
- async function refreshWallet({ session, store }) {
125669
+ var walkWalletHistory = (session, store) => loadWalletP03History({
125670
+ store,
125671
+ connection: connectSplit(session.connection, session.historyUrl),
125672
+ programId: session.programId,
125673
+ pool: store.state.pool,
125674
+ deploymentIdentity: session.proofTools.deploymentIdentity
125675
+ });
125676
+ async function refreshWallet({ session, store, history: walked }) {
125550
125677
  const walStates = captureWalStates(store);
125551
- const history = await loadWalletP03History({
125552
- store,
125553
- connection: connectSplit(session.connection, session.historyUrl),
125554
- programId: session.programId,
125555
- pool: store.state.pool,
125556
- deploymentIdentity: session.proofTools.deploymentIdentity
125557
- });
125678
+ const history = walked ?? await walkWalletHistory(session, store);
125558
125679
  const result = await runRecover({
125559
125680
  store,
125560
125681
  connection: session.connection,
@@ -125800,7 +125921,9 @@ function createWalletTools({
125800
125921
  let refreshRun;
125801
125922
  let refreshFailedAt = 0;
125802
125923
  let refreshFailedCode;
125803
- const refreshStep = (store) => (ops.refresh ?? refreshWallet)({ session, store });
125924
+ let refreshInBackground = false;
125925
+ const refreshStep = (store, history) => (ops.refresh ?? refreshWallet)({ session, store, ...history ? { history } : {} });
125926
+ const refreshWalk = ops.refreshWalk ?? (ops.refresh ? void 0 : (store) => walkWalletHistory(session, store));
125804
125927
  const errorCode2 = (error, fallback) => describeError(error, { fallback, safeText: SAFE_TEXT }).split(/[\s:;(]/, 1)[0];
125805
125928
  const underLease = (task) => session.serial(async () => {
125806
125929
  let release;
@@ -125944,9 +126067,27 @@ function createWalletTools({
125944
126067
  deps: ops.oneTimeAdopt && typeof ops.oneTimeAdopt === "object" ? ops.oneTimeAdopt : {}
125945
126068
  });
125946
126069
  if (ops.oneTimeAdopt !== false) adopter.start();
125947
- function refreshShared() {
126070
+ function refreshShared({ background = false } = {}) {
125948
126071
  if (!refreshRun) {
125949
- refreshRun = underLease((store) => refreshStep(store)).then(() => {
126072
+ refreshInBackground = background;
126073
+ refreshRun = (async () => {
126074
+ let early;
126075
+ if (background && refreshWalk) {
126076
+ try {
126077
+ const store = session.openStore();
126078
+ const records = JSON.stringify(captureWalStates(store));
126079
+ const pool2 = store.state.pool;
126080
+ early = { records, pool: pool2, history: await refreshWalk(store) };
126081
+ } catch {
126082
+ early = void 0;
126083
+ }
126084
+ }
126085
+ return underLease((store) => {
126086
+ const same2 = early !== void 0 && early.pool === store.state.pool && early.records === JSON.stringify(captureWalStates(store));
126087
+ if (early !== void 0 && !same2) log("MCP_REFRESH_WALK_AGAIN");
126088
+ return refreshStep(store, same2 ? early.history : void 0);
126089
+ });
126090
+ })().then(() => {
125950
126091
  refreshFailedAt = 0;
125951
126092
  log("MCP_REFRESH_DONE");
125952
126093
  }, (error) => {
@@ -125956,6 +126097,7 @@ function createWalletTools({
125956
126097
  throw error;
125957
126098
  }).finally(() => {
125958
126099
  refreshRun = void 0;
126100
+ refreshInBackground = false;
125959
126101
  });
125960
126102
  }
125961
126103
  return refreshRun;
@@ -125988,7 +126130,7 @@ function createWalletTools({
125988
126130
  }
125989
126131
  if (watched === finalityTarget && status?.confirmationStatus === "finalized") {
125990
126132
  log("MCP_FINALITY_REFRESH");
125991
- await refreshShared().catch(() => {
126133
+ await refreshShared({ background: true }).catch(() => {
125992
126134
  });
125993
126135
  return;
125994
126136
  }
@@ -126013,6 +126155,7 @@ function createWalletTools({
126013
126155
  return;
126014
126156
  }
126015
126157
  if (!stale || refreshFailedAt && Date.now() - refreshFailedAt < REFRESH_RETRY_MS) return;
126158
+ if (finalityWait || refreshRun && refreshInBackground) return;
126016
126159
  await refreshShared().catch(() => {
126017
126160
  });
126018
126161
  }
@@ -126388,6 +126531,8 @@ function createWalletTools({
126388
126531
  );
126389
126532
  } else if (code.startsWith("wallet_spend_refused_")) {
126390
126533
  next = "the wallet file's own budget refused it (init --max-payment / --max-cumulative); nothing was paid. Read wallet_balance detail.budget";
126534
+ } else if (code === "fetch_seller_unreachable") {
126535
+ next = `Nothing was paid and nothing was sent to the seller. Call x402_pay again with request_id "${requestId}" and the same url, method, headers, and body (it never pays twice); if it fails the same way again, the seller is down: tell your owner`;
126391
126536
  } else if (code.startsWith("wallet_quote_refused_") || code.startsWith("fetch_")) {
126392
126537
  next = "nothing was paid; do not retry this URL unchanged";
126393
126538
  } else {
@@ -135601,6 +135746,7 @@ function failureHint(command, reason, deps, values) {
135601
135746
  if (/fetch_offer_unsupported/.test(reason)) {
135602
135747
  return `nothing was paid: the seller's 402 offers no scheme this wallet pays (shielded-exact, or standard exact through Shinjuku). Hidden-amount (confidential) payments are retired since 2026-10-11`;
135603
135748
  }
135749
+ if (/fetch_seller_unreachable/.test(reason)) return "nothing was paid: the seller did not answer the first, unpaid request. Rerun the same command (same --request-id) later; if it fails again, the seller is down";
135604
135750
  if (/fetch_challenge_invalid/.test(reason)) return "nothing was paid: the seller's 402 is not a valid x402 v2 challenge (PAYMENT-REQUIRED header). Do not pay this seller until it sends one";
135605
135751
  if (/fetch_url_refused/.test(reason)) return "nothing was sent: use the https URL. Plain http goes only to a private host (localhost, 10.x, 192.168.x) or to an .onion inside a --tor run";
135606
135752
  if (/fetch_options_invalid/.test(reason)) {