shinjuku-shielded 0.3.39 → 0.3.41

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": "5f5ca189829786779ba9e05c237bb6edf057a06d4745f0a601212136aa0bf68f",
5
- "bytes": 11216201,
6
- "sourceCommit": "c72d3c75917cf35191aa263f3e30878779ed814b",
4
+ "sha256": "387938b415cfece575952e875ecafc7149e29279e202a4e862433f66f664a803",
5
+ "bytes": 11230018,
6
+ "sourceCommit": "60acd91587cd5576b91001c7dcdabbb1c0f723aa",
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.39",
3
+ "version": "0.3.41",
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": "5f5ca189",
7
- "shinjukuSha256": "5f5ca189829786779ba9e05c237bb6edf057a06d4745f0a601212136aa0bf68f",
6
+ "shinjukuRelease": "387938b4",
7
+ "shinjukuSha256": "387938b415cfece575952e875ecafc7149e29279e202a4e862433f66f664a803",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -42330,6 +42330,54 @@ var init_pool_denomination = __esm({
42330
42330
  }
42331
42331
  });
42332
42332
 
42333
+ // spikes/shielded-x402-wallet/lib/rpc-url.mjs
42334
+ function flagValue(args, name) {
42335
+ const index = args.indexOf(name);
42336
+ if (index === -1) return void 0;
42337
+ const value = args[index + 1];
42338
+ if (!value || value.startsWith("--")) throw new Error(`rpc_flag_value_missing: ${name} requires a value`);
42339
+ return value;
42340
+ }
42341
+ function resolveRpcUrl(args, { env = process.env, warn = (line) => console.error(line), fallback } = {}) {
42342
+ const file = flagValue(args, "--rpc-file");
42343
+ const inline = flagValue(args, "--rpc");
42344
+ if (file !== void 0 && inline !== void 0) throw new Error("rpc_flags_conflict: use --rpc-file or --rpc, not both");
42345
+ if (file !== void 0) {
42346
+ let text3;
42347
+ try {
42348
+ text3 = readFileSync(file, "utf8");
42349
+ } catch (error) {
42350
+ throw new Error(`rpc_file_unreadable: --rpc-file could not be read (${typeof error?.code === "string" ? error.code : "read error"})`);
42351
+ }
42352
+ const value = text3.split(/\r?\n/)[0].trim();
42353
+ if (!value) throw new Error("rpc_file_empty: --rpc-file holds no RPC URL on its first line");
42354
+ return value;
42355
+ }
42356
+ if (inline !== void 0) {
42357
+ if (rpcUrlLooksKeyed(inline)) {
42358
+ warn("warning: --rpc shows this RPC URL, key included, to anyone who can list processes or read shell history; put the URL in a private file and pass --rpc-file <file>");
42359
+ }
42360
+ return inline;
42361
+ }
42362
+ return env.UNDERMARKET_SOLANA_RPC_URL || fallback;
42363
+ }
42364
+ function rpcUrlLooksKeyed(value) {
42365
+ let url;
42366
+ try {
42367
+ url = new URL(value);
42368
+ } catch {
42369
+ return true;
42370
+ }
42371
+ if (url.search || url.username || url.password) return true;
42372
+ return url.pathname.split("/").some((segment) => segment.length >= 16 && /^[A-Za-z0-9_-]+$/.test(segment) && /[0-9]/.test(segment) && /[A-Za-z]/.test(segment));
42373
+ }
42374
+ var init_rpc_url = __esm({
42375
+ "spikes/shielded-x402-wallet/lib/rpc-url.mjs"() {
42376
+ "use strict";
42377
+ init_fs();
42378
+ }
42379
+ });
42380
+
42333
42381
  // spikes/shielded-x402-wallet/lib/prove.mjs
42334
42382
  import { createHash as createHash4 } from "node:crypto";
42335
42383
  import { join as join2, resolve as resolve2 } from "node:path";
@@ -42507,7 +42555,7 @@ function readRpcFileLine(path) {
42507
42555
  return void 0;
42508
42556
  }
42509
42557
  }
42510
- function resolveRpcUrl({ rpcFile, rpc: rpc2, env = process.env, origin, readFile: readFile5, notice } = {}) {
42558
+ function resolveRpcUrl2({ rpcFile, rpc: rpc2, env = process.env, origin, readFile: readFile5, notice } = {}) {
42511
42559
  if (rpcFile !== void 0) {
42512
42560
  const url2 = (readFile5 ?? readRpcFileLine)(rpcFile);
42513
42561
  if (!url2) throw new Error("rpc_file_unreadable: --rpc-file could not be read, or its first line is empty");
@@ -48378,10 +48426,21 @@ function walNames(directory) {
48378
48426
  });
48379
48427
  }
48380
48428
  function bindWalDirectory(directory, key, claim = true) {
48381
- mkdirSync(directory, { recursive: true, mode: 448 });
48382
48429
  const ownerPath = join3(directory, WAL_OWNER_FILE);
48430
+ const verified = verifiedWalOwners.get(key);
48431
+ try {
48432
+ if (verified?.get(resolve3(directory)) === ownerStatId(ownerPath)) return;
48433
+ } catch {
48434
+ }
48435
+ mkdirSync(directory, { recursive: true, mode: 448 });
48383
48436
  const identity = createHmac("sha256", key).update(WAL_OWNER_SCHEMA).digest("hex");
48384
48437
  const verifyOwner = () => {
48438
+ let statId;
48439
+ try {
48440
+ statId = ownerStatId(ownerPath);
48441
+ } catch {
48442
+ statId = void 0;
48443
+ }
48385
48444
  let owner;
48386
48445
  try {
48387
48446
  owner = JSON.parse(readFileSync(ownerPath, "utf8"));
@@ -48391,6 +48450,10 @@ function bindWalDirectory(directory, key, claim = true) {
48391
48450
  if (owner?.schema !== WAL_OWNER_SCHEMA || owner.identity !== identity || Object.keys(owner).sort().join() !== "identity,schema") {
48392
48451
  throw new WalletStoreError("wallet_wal_owner_mismatch");
48393
48452
  }
48453
+ if (statId === void 0) return;
48454
+ let byDirectory = verifiedWalOwners.get(key);
48455
+ if (!byDirectory) verifiedWalOwners.set(key, byDirectory = /* @__PURE__ */ new Map());
48456
+ byDirectory.set(resolve3(directory), statId);
48394
48457
  };
48395
48458
  if (existsSync(ownerPath)) {
48396
48459
  verifyOwner();
@@ -48523,7 +48586,7 @@ function openWalletStore(path, passphrase2) {
48523
48586
  };
48524
48587
  return store;
48525
48588
  }
48526
- var STORE_SCHEMA, WAL_SCHEMA, SCRYPT, WAL_OWNER_FILE, WAL_OWNER_SCHEMA, WalletStoreError, KEY_CACHE_LIMIT, keyCacheSecret, keyCache, keyStats, keyCacheId, rememberKey, assertPassphrase, assertWalName;
48589
+ var STORE_SCHEMA, WAL_SCHEMA, SCRYPT, WAL_OWNER_FILE, WAL_OWNER_SCHEMA, WalletStoreError, KEY_CACHE_LIMIT, keyCacheSecret, keyCache, keyStats, keyCacheId, rememberKey, verifiedWalOwners, ownerStatId, assertPassphrase, assertWalName;
48527
48590
  var init_store = __esm({
48528
48591
  "wallet/src/store/store.mjs"() {
48529
48592
  "use strict";
@@ -48549,6 +48612,11 @@ var init_store = __esm({
48549
48612
  keyCache.set(id, key);
48550
48613
  while (keyCache.size > KEY_CACHE_LIMIT) keyCache.delete(keyCache.keys().next().value);
48551
48614
  };
48615
+ verifiedWalOwners = /* @__PURE__ */ new WeakMap();
48616
+ ownerStatId = (path) => {
48617
+ const stat = statSync(path, { bigint: true });
48618
+ return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
48619
+ };
48552
48620
  assertPassphrase = (passphrase2) => {
48553
48621
  if (typeof passphrase2 !== "string" || passphrase2.length < 8) {
48554
48622
  throw new WalletStoreError("wallet_passphrase_too_short: the wallet passphrase must be at least 8 characters");
@@ -54782,12 +54850,23 @@ async function prepareFinalizedP03RecoveryFold(history, { sliceMs = 20 } = {}) {
54782
54850
  if (REBUILT_BY_REPORT.has(authenticated)) return;
54783
54851
  const running = FOLDS_IN_FLIGHT.get(authenticated);
54784
54852
  if (running !== void 0) return running;
54853
+ if (foldInFlight !== void 0) markPhase("fold-wait");
54854
+ while (foldInFlight !== void 0) {
54855
+ await foldInFlight.catch(() => {
54856
+ });
54857
+ if (REBUILT_BY_REPORT.has(authenticated)) return;
54858
+ const joined = FOLDS_IN_FLIGHT.get(authenticated);
54859
+ if (joined !== void 0) return joined;
54860
+ }
54785
54861
  const folding = foldInSlices(authenticated, sliceMs);
54786
54862
  FOLDS_IN_FLIGHT.set(authenticated, folding);
54863
+ foldInFlight = folding;
54787
54864
  try {
54788
- return await folding;
54865
+ await folding;
54866
+ markPhase("fold-done");
54789
54867
  } finally {
54790
54868
  FOLDS_IN_FLIGHT.delete(authenticated);
54869
+ if (foldInFlight === folding) foldInFlight = void 0;
54791
54870
  }
54792
54871
  }
54793
54872
  async function foldInSlices(authenticated, sliceMs) {
@@ -54816,6 +54895,7 @@ function* rebuildOrderedPrefixSteps(history) {
54816
54895
  while (resumed < prior.leaves.length && contexts[resumed].outCommitment === prior.leaves[resumed] && contexts[resumed].appendRoot === prior.roots[resumed + 1]) resumed += 1;
54817
54896
  if (resumed !== prior.leaves.length) resumed = 0;
54818
54897
  }
54898
+ markPhase(resumed > 0 ? "fold-reused" : "fold-cold");
54819
54899
  let nodes = resumed > 0 ? prior.nodes : zeroRoots2.slice(0, OUTER_DEPTH);
54820
54900
  let root = resumed > 0 ? prior.roots[resumed] : zeroRoots2[OUTER_DEPTH];
54821
54901
  const roots = resumed > 0 ? prior.roots.slice(0, resumed + 1) : [root];
@@ -54990,7 +55070,7 @@ function canonicalField(value, label) {
54990
55070
  }
54991
55071
  return value;
54992
55072
  }
54993
- var FIELD_MODULUS4, SUBTREE_LEAVES2, LOCAL_DEPTH, OUTER_DEPTH, TREE_DEPTH, P03_RECOVERY_PATH_SOURCE_MODEL, P03_PINNED_POSEIDON_PARAMETERS_SHA256, P03_PINNED_ZERO_LEAF, hashCalls, hash3, REBUILT_BY_REPORT, lastFold, FOLDS_IN_FLIGHT, ZERO_UPPER_ROOTS, LOCAL_ZERO_ROOTS, lastOuter;
55073
+ var FIELD_MODULUS4, SUBTREE_LEAVES2, LOCAL_DEPTH, OUTER_DEPTH, TREE_DEPTH, P03_RECOVERY_PATH_SOURCE_MODEL, P03_PINNED_POSEIDON_PARAMETERS_SHA256, P03_PINNED_ZERO_LEAF, hashCalls, hash3, REBUILT_BY_REPORT, lastFold, FOLDS_IN_FLIGHT, foldInFlight, ZERO_UPPER_ROOTS, LOCAL_ZERO_ROOTS, lastOuter;
54994
55074
  var init_recoveryPath = __esm({
54995
55075
  "wallet/src/recovery/recoveryPath.mjs"() {
54996
55076
  "use strict";
@@ -54998,6 +55078,7 @@ var init_recoveryPath = __esm({
54998
55078
  init_reference2();
54999
55079
  init_notes2();
55000
55080
  init_walk();
55081
+ init_phaseTiming();
55001
55082
  FIELD_MODULUS4 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
55002
55083
  SUBTREE_LEAVES2 = 128n;
55003
55084
  LOCAL_DEPTH = 7;
@@ -56109,6 +56190,7 @@ async function loadOrExtendFinalizedP03History({
56109
56190
  cached = null;
56110
56191
  fallbacks += 1;
56111
56192
  }
56193
+ let laggingRetries = 0;
56112
56194
  for (let laggingTry = 0; cached; laggingTry += 1) {
56113
56195
  try {
56114
56196
  const extended = await extendFromCache({
@@ -56132,11 +56214,14 @@ async function loadOrExtendFinalizedP03History({
56132
56214
  mode: extended.mode,
56133
56215
  fallbacks,
56134
56216
  coverage: extended.coverageMode,
56217
+ laggingRetries,
56135
56218
  ...extended.window ? { window: extended.window } : {}
56136
56219
  };
56137
56220
  } catch (error) {
56138
56221
  onEvent("extension_failed", error);
56222
+ if (isProgramDataIdentityError(error)) throw error;
56139
56223
  if (isLaggingNodeRpcError(error) && laggingTry < EXTENSION_LAGGING_RETRY_MS.length) {
56224
+ laggingRetries += 1;
56140
56225
  await new Promise((resolve38) => setTimeout(resolve38, EXTENSION_LAGGING_RETRY_MS[laggingTry]));
56141
56226
  continue;
56142
56227
  }
@@ -56158,7 +56243,7 @@ async function loadOrExtendFinalizedP03History({
56158
56243
  await beforePersist?.(full);
56159
56244
  await persistCache(cache, full, programIdBase58, poolBase58);
56160
56245
  remember(full);
56161
- return { report: full.report, mode: "full", fallbacks, coverage: full.coverageMode };
56246
+ return { report: full.report, mode: "full", fallbacks, coverage: full.coverageMode, laggingRetries };
56162
56247
  }
56163
56248
  async function extendFromCache({ connection, programId, pool: pool2, deploymentIdentity, cached: stored, concurrency, lighterWalk, historySource, forceRead = false, liveRead }) {
56164
56249
  const program = new import_web398.PublicKey(programId);
@@ -56558,6 +56643,23 @@ function walletCompactHistoryCache(store) {
56558
56643
  async function loadWalletP03History(args) {
56559
56644
  const runKey = lastRunReportKey(args.store, args.programId, args.pool, args.deploymentIdentity, args.cache);
56560
56645
  const prewarm = runKey === void 0 ? void 0 : prewarmedWalks.get(runKey);
56646
+ const share = args.share;
56647
+ if (share !== void 0) {
56648
+ if (prewarm !== void 0 && strictScope.getStore() !== true && args.forceRead !== true && Number.isFinite(share.notBeforeMs) && prewarm.startedAt >= share.notBeforeMs && Date.now() - prewarm.startedAt <= HISTORY_PREWARM_JOIN_MS) {
56649
+ try {
56650
+ const report3 = await prewarm.promise;
56651
+ PREWARMED_REPORTS.add(report3);
56652
+ markPhase("walk-prewarm-shared");
56653
+ return report3;
56654
+ } catch {
56655
+ }
56656
+ }
56657
+ const { share: _share, ...walkArgs } = args;
56658
+ const startedAt2 = Date.now();
56659
+ const report2 = await loadWalletP03HistoryNow(walkArgs);
56660
+ if (runKey !== void 0) recentLoads.set(runKey, { startedAt: startedAt2, report: report2 });
56661
+ return report2;
56662
+ }
56561
56663
  if (prewarm !== void 0) {
56562
56664
  prewarmedWalks.delete(runKey);
56563
56665
  if (strictScope.getStore() !== true && args.forceRead !== true && Date.now() - prewarm.startedAt <= HISTORY_PREWARM_JOIN_MS) {
@@ -59768,9 +59870,9 @@ async function runDepositReclaimCommand(args, cli) {
59768
59870
  const to = cli.flag(args, "--to");
59769
59871
  const serviceUrl2 = cli.flag(args, "--service");
59770
59872
  const keyFile = cli.flag(args, "--fee-payer-key-file");
59771
- const rpcUrl = cli.flag(args, "--rpc") ?? process.env.UNDERMARKET_SOLANA_RPC_URL;
59873
+ const rpcUrl = cli.rpcUrl(args);
59772
59874
  if (!pool2 || !programId || !depositId || !to || !rpcUrl || Boolean(serviceUrl2) === Boolean(keyFile)) {
59773
- throw new Error("deposit-reclaim requires --pool, --program, --deposit-id, --to, exactly one of --service or --fee-payer-key-file, and --rpc or UNDERMARKET_SOLANA_RPC_URL");
59875
+ throw new Error("deposit-reclaim requires --pool, --program, --deposit-id, --to, exactly one of --service or --fee-payer-key-file, and --rpc-file <file> (or --rpc, or UNDERMARKET_SOLANA_RPC_URL)");
59774
59876
  }
59775
59877
  const store = cli.openStore(pool2);
59776
59878
  if (store.state.pool !== pool2) throw new Error("deposit pool does not match wallet");
@@ -59786,11 +59888,11 @@ async function runDepositReclaimCommand(args, cli) {
59786
59888
  }
59787
59889
  async function runDepositScanCommand(args, cli) {
59788
59890
  const pool2 = cli.flag(args, "--pool");
59789
- const rpcUrl = cli.flag(args, "--rpc") ?? process.env.UNDERMARKET_SOLANA_RPC_URL;
59891
+ const rpcUrl = cli.rpcUrl(args);
59790
59892
  const fromFlag = cli.flag(args, "--from") ?? "0";
59791
59893
  const countFlag = cli.flag(args, "--count") ?? String(RELAY_KEY_PROBES);
59792
59894
  if (!pool2 || !rpcUrl || !/^(0|[1-9][0-9]{0,9})$/.test(fromFlag) || !/^[1-9][0-9]{0,3}$/.test(countFlag)) {
59793
- throw new Error(`deposit-scan requires --pool, --rpc or UNDERMARKET_SOLANA_RPC_URL, and optional whole numbers --from (default 0) and --count (1 to ${MAX_SCAN_COUNT}, default ${RELAY_KEY_PROBES})`);
59895
+ throw new Error(`deposit-scan requires --pool, --rpc-file <file> (or --rpc, or UNDERMARKET_SOLANA_RPC_URL), and optional whole numbers --from (default 0) and --count (1 to ${MAX_SCAN_COUNT}, default ${RELAY_KEY_PROBES})`);
59794
59896
  }
59795
59897
  const from = Number(fromFlag);
59796
59898
  const count2 = Number(countFlag);
@@ -59829,9 +59931,9 @@ async function reclaimLandedDepositRent({ store, connection, programId, deps = {
59829
59931
  async function runDepositCompactCommand(args, cli) {
59830
59932
  const pool2 = cli.flag(args, "--pool");
59831
59933
  const programId = cli.flag(args, "--program");
59832
- const rpcUrl = cli.flag(args, "--rpc") ?? process.env.UNDERMARKET_SOLANA_RPC_URL;
59934
+ const rpcUrl = cli.rpcUrl(args);
59833
59935
  if (!pool2 || !programId || !rpcUrl) {
59834
- throw new Error("deposit-compact requires --pool, --program, and --rpc or UNDERMARKET_SOLANA_RPC_URL");
59936
+ throw new Error("deposit-compact requires --pool, --program, and --rpc-file <file> (or --rpc, or UNDERMARKET_SOLANA_RPC_URL)");
59835
59937
  }
59836
59938
  const path = cli.walletPath(pool2);
59837
59939
  let release;
@@ -60180,11 +60282,26 @@ var init_recoveryCache = __esm({
60180
60282
 
60181
60283
  // wallet/src/recovery/memoDecryptPool.mjs
60182
60284
  import { availableParallelism } from "node:os";
60285
+ import { performance as performance2 } from "node:perf_hooks";
60183
60286
  import { Worker as Worker2 } from "node:worker_threads";
60184
- function sharedXOnMainThread(xs, eta) {
60287
+ function sliceClock() {
60288
+ let started = performance2.now();
60289
+ return async () => {
60290
+ if (performance2.now() - started < SLICE_MS) return;
60291
+ await yieldTurn();
60292
+ started = performance2.now();
60293
+ };
60294
+ }
60295
+ async function sharedXOnMainThread(xs, eta) {
60185
60296
  mainThreadSharedX ??= new Function(`${CURVE_SOURCE}
60186
60297
  return memoSharedX;`)();
60187
- return xs.map((x) => mainThreadSharedX(x, eta));
60298
+ const turn = sliceClock();
60299
+ const out = [];
60300
+ for (const x of xs) {
60301
+ out.push(mainThreadSharedX(x, eta));
60302
+ await turn();
60303
+ }
60304
+ return out;
60188
60305
  }
60189
60306
  function workerCount() {
60190
60307
  let cores = 1;
@@ -60274,7 +60391,10 @@ async function trialDecryptBundles(jobs, eta) {
60274
60391
  const xs = records.flatMap((list) => list.map((record) => Buffer.from(record.subarray(0, 32)).toString("hex")));
60275
60392
  const shared2 = await memoSharedXs(xs, scalar);
60276
60393
  if (shared2.length !== xs.length) throw new Error("memo decrypt pool answered the wrong count");
60277
- return jobs.map((job, index) => {
60394
+ const turn = sliceClock();
60395
+ const results = [];
60396
+ for (let index = 0; index < jobs.length; index += 1) {
60397
+ const job = jobs[index];
60278
60398
  const context = {
60279
60399
  poolDomain: BigInt(job.poolDomain),
60280
60400
  outCommitment: BigInt(job.outCommitment),
@@ -60286,15 +60406,17 @@ async function trialDecryptBundles(jobs, eta) {
60286
60406
  if (record.length !== RECORD_BYTES2) return void 0;
60287
60407
  return openRecord(record, slot3, shared2[index * RECORD_COUNT2 + slot3], context);
60288
60408
  });
60289
- return acceptMemoPlaintexts({
60409
+ results.push(acceptMemoPlaintexts({
60290
60410
  plaintexts,
60291
60411
  outCommitment: job.outCommitment,
60292
60412
  zeroLeaf: job.zeroLeaf,
60293
60413
  subtreeStartIndex: job.subtreeStartIndex
60294
- });
60295
- });
60414
+ }));
60415
+ await turn();
60416
+ }
60417
+ return results;
60296
60418
  }
60297
- var CURVE_SOURCE, WORKER_SOURCE, mainThreadSharedX, MAX_WORKERS, pool, nextJob;
60419
+ var CURVE_SOURCE, WORKER_SOURCE, SLICE_MS, yieldTurn, mainThreadSharedX, MAX_WORKERS, pool, nextJob;
60298
60420
  var init_memoDecryptPool = __esm({
60299
60421
  "wallet/src/recovery/memoDecryptPool.mjs"() {
60300
60422
  "use strict";
@@ -60424,6 +60546,10 @@ parentPort.on("message", ({ id, eta, xs }) => {
60424
60546
  parentPort.postMessage({ id, out: xs.map((x) => memoSharedX(x, scalar)) });
60425
60547
  });
60426
60548
  `;
60549
+ SLICE_MS = 25;
60550
+ yieldTurn = () => new Promise((resolve38) => {
60551
+ setImmediate(resolve38);
60552
+ });
60427
60553
  MAX_WORKERS = 8;
60428
60554
  nextJob = 0;
60429
60555
  }
@@ -60492,6 +60618,7 @@ var init_successorHistories = __esm({
60492
60618
  // wallet/src/recovery/recover.mjs
60493
60619
  import { createHash as createHash15 } from "node:crypto";
60494
60620
  import { join as join10, resolve as resolve10 } from "node:path";
60621
+ import { performance as performance3 } from "node:perf_hooks";
60495
60622
  import { fileURLToPath as fileURLToPath5 } from "node:url";
60496
60623
  function pinnedGenerator2(profileVector) {
60497
60624
  if (profileVector?.payerKeys?.generator) return profileVector.payerKeys.generator;
@@ -60513,6 +60640,16 @@ function replayValueOf(kind, poolDomain, commitment, eta, leafIndex) {
60513
60640
  REPLAY_VALUES.set(key, value);
60514
60641
  return value;
60515
60642
  }
60643
+ function decryptedKey(eta, programId, pool2, digest2, context) {
60644
+ return createHash15("sha256").update(`${String(eta)}|${programId}|${pool2}|${digest2}|${[
60645
+ context.transitionKind,
60646
+ context.poolDomain,
60647
+ context.outCommitment,
60648
+ context.contextCommitment,
60649
+ context.epoch,
60650
+ context.subtreeStartIndex
60651
+ ].map((value) => String(value)).join(":")}`).digest("base64");
60652
+ }
60516
60653
  function assertFinalizedRecoveryContext(context, digest2, programId, pool2) {
60517
60654
  const kinds = Object.freeze({ deposit: 1, payment: 2, withdraw: 3, "note-exit": 4, move: 5 });
60518
60655
  const field4 = (value) => typeof value === "bigint" && value >= 0n && value < FIELD_MODULUS6;
@@ -60550,6 +60687,24 @@ function walMemoBundles(store) {
60550
60687
  }
60551
60688
  return bundles;
60552
60689
  }
60690
+ async function walMemoBundlesSliced(store) {
60691
+ const bundles = /* @__PURE__ */ new Map();
60692
+ let started = performance3.now();
60693
+ for (const name of store.listWal()) {
60694
+ const record = store.readWal(name);
60695
+ if (record?.memoBundleHex) {
60696
+ const bytes2 = Uint8Array.from(Buffer.from(hexOf2(record.memoBundleHex), "hex"));
60697
+ bundles.set(hexOf2(record.memoDigest ?? bundleDigest2(bytes2)), bytes2);
60698
+ }
60699
+ if (performance3.now() - started >= 25) {
60700
+ await new Promise((resolve38) => {
60701
+ setImmediate(resolve38);
60702
+ });
60703
+ started = performance3.now();
60704
+ }
60705
+ }
60706
+ return bundles;
60707
+ }
60553
60708
  function decryptJob(bytes2, context, eta) {
60554
60709
  return {
60555
60710
  bundleBytes: bytes2,
@@ -60568,6 +60723,30 @@ async function decryptAll(decrypt, items, eta) {
60568
60723
  const results = await trialDecryptBundles(items.map(({ bytes: bytes2, context }) => decryptJob(bytes2, context, eta)), eta);
60569
60724
  return results.map((result) => result.accepted);
60570
60725
  }
60726
+ async function decryptKept(decrypt, items, eta, chainProgramId, pool2) {
60727
+ if (decrypt !== void 0) return decryptAll(decrypt, items, eta);
60728
+ const program = chainProgramId.toBase58();
60729
+ const poolKey = pool2.toBase58();
60730
+ const keys3 = items.map(({ digest: digest2, context }) => decryptedKey(eta, program, poolKey, digest2, context));
60731
+ const outputs = keys3.map((key) => {
60732
+ const kept = DECRYPTED.get(key);
60733
+ return kept === void 0 ? void 0 : copyOutputs(kept);
60734
+ });
60735
+ const missing = items.filter((_, index) => outputs[index] === void 0);
60736
+ if (missing.length > 0) {
60737
+ const fresh = await decryptAll(void 0, missing, eta);
60738
+ let next = 0;
60739
+ outputs.forEach((value, index) => {
60740
+ if (value !== void 0) return;
60741
+ const result = fresh[next];
60742
+ next += 1;
60743
+ if (DECRYPTED.size >= DECRYPTED_MAX) DECRYPTED.clear();
60744
+ DECRYPTED.set(keys3[index], copyOutputs(result));
60745
+ outputs[index] = result;
60746
+ });
60747
+ }
60748
+ return outputs;
60749
+ }
60571
60750
  async function legacyOutputs({ store, digests, serviceUrl: serviceUrl2, fresh, deps, chainProgramId, pool: pool2, eta, decrypt }) {
60572
60751
  const bundles = [];
60573
60752
  const seen = /* @__PURE__ */ new Set();
@@ -60620,7 +60799,7 @@ async function syncedOutputs({ store, finalizedHistory: finalizedHistory2, diges
60620
60799
  }
60621
60800
  if (!fresh) store.listWal();
60622
60801
  let wal;
60623
- const walFor = () => wal ??= fresh ? /* @__PURE__ */ new Map() : walMemoBundles(store);
60802
+ const walFor = async () => wal ??= fresh ? /* @__PURE__ */ new Map() : await walMemoBundlesSliced(store);
60624
60803
  const reader = deps.memoReader ?? createMemoReader(serviceUrl2, { fetchImpl: deps.fetchImpl ?? fetch, ...deps.pause ? { pause: deps.pause } : {} });
60625
60804
  (deps.notice ?? ((text3) => process.stderr.write(text3)))(
60626
60805
  `reading ${pending.length} memos from the memo service, ${Math.ceil(pending.length / MEMO_BATCH_MAX)} request(s)
@@ -60637,10 +60816,17 @@ async function syncedOutputs({ store, finalizedHistory: finalizedHistory2, diges
60637
60816
  }
60638
60817
  }
60639
60818
  const items = [];
60819
+ let sliceStart = performance3.now();
60640
60820
  for (const context of chunk) {
60821
+ if (performance3.now() - sliceStart >= 25) {
60822
+ await new Promise((resolve38) => {
60823
+ setImmediate(resolve38);
60824
+ });
60825
+ sliceStart = performance3.now();
60826
+ }
60641
60827
  const digest2 = hexOf2(context.memoDigest);
60642
60828
  const fromService = served.get(digest2);
60643
- const bytes2 = fromService ?? walFor().get(digest2);
60829
+ const bytes2 = fromService ?? (await walFor()).get(digest2);
60644
60830
  if (!bytes2) {
60645
60831
  absent2 += 1;
60646
60832
  continue;
@@ -60648,7 +60834,7 @@ async function syncedOutputs({ store, finalizedHistory: finalizedHistory2, diges
60648
60834
  assertBundle(digest2, bytes2);
60649
60835
  items.push({ context, digest: digest2, bytes: bytes2, fromService });
60650
60836
  }
60651
- const outputs = await decryptAll(decrypt, items, eta);
60837
+ const outputs = await decryptKept(decrypt, items, eta, chainProgramId, pool2);
60652
60838
  items.forEach(({ context, digest: digest2, fromService }, index) => {
60653
60839
  decrypted += 1;
60654
60840
  found.set(context, outputs[index]);
@@ -60834,7 +61020,7 @@ async function runRecover({ store, connection, programId, digests, serviceUrl: s
60834
61020
  ...Array.isArray(digests) && digests.length > 0 ? { narrowedByDigests: true } : {}
60835
61021
  };
60836
61022
  }
60837
- var import_web3102, FIELD_MODULUS6, MODEL_DIR, seedBytes3, committedGenerator, REPLAY_VALUES, REPLAY_VALUES_MAX, hexOf2;
61023
+ var import_web3102, FIELD_MODULUS6, MODEL_DIR, seedBytes3, committedGenerator, REPLAY_VALUES, REPLAY_VALUES_MAX, DECRYPTED, DECRYPTED_MAX, copyOutputs, hexOf2;
60838
61024
  var init_recover = __esm({
60839
61025
  "wallet/src/recovery/recover.mjs"() {
60840
61026
  "use strict";
@@ -60856,6 +61042,13 @@ var init_recover = __esm({
60856
61042
  seedBytes3 = (state) => Uint8Array.from(Buffer.from(state.seedHex, "hex"));
60857
61043
  REPLAY_VALUES = /* @__PURE__ */ new Map();
60858
61044
  REPLAY_VALUES_MAX = 5e4;
61045
+ DECRYPTED = /* @__PURE__ */ new Map();
61046
+ DECRYPTED_MAX = 5e4;
61047
+ copyOutputs = (outputs) => outputs.map((output2) => ({
61048
+ ...output2,
61049
+ ...output2.account ? { account: { ...output2.account } } : {},
61050
+ ...output2.note ? { note: { ...output2.note } } : {}
61051
+ }));
60859
61052
  hexOf2 = (value) => String(value).replace(/^0x/, "").toLowerCase();
60860
61053
  }
60861
61054
  });
@@ -62284,8 +62477,11 @@ var init_errorCodes = __esm({
62284
62477
  "programdata_address_mismatch",
62285
62478
  "programdata_derivation_drift",
62286
62479
  "recovery_program_unexpectedly_deployed",
62480
+ "rpc_file_empty",
62287
62481
  "rpc_file_invalid",
62288
62482
  "rpc_file_unreadable",
62483
+ "rpc_flag_value_missing",
62484
+ "rpc_flags_conflict",
62289
62485
  "settler_is_retired_production_settler",
62290
62486
  "settler_is_staging_settler",
62291
62487
  "settler_is_upgrade_authority",
@@ -70161,13 +70357,13 @@ function readPoolDenominationFlag(args) {
70161
70357
  if (typeof value !== "string" || value === "" || value.startsWith("--")) return "";
70162
70358
  return value;
70163
70359
  }
70164
- function resolveExitDenominationPolicy({ pool: pool2, flagValue, envValue, allowOffDenomination = false }) {
70360
+ function resolveExitDenominationPolicy({ pool: pool2, flagValue: flagValue2, envValue, allowOffDenomination = false }) {
70165
70361
  let poolDenominationAtomic;
70166
70362
  let configError;
70167
70363
  const warnings = [];
70168
- if (flagValue !== void 0) {
70169
- if (isCanonicalDenominationAtomic(flagValue)) poolDenominationAtomic = flagValue;
70170
- else if (flagValue === "") configError = "--pool-denomination requires a value";
70364
+ if (flagValue2 !== void 0) {
70365
+ if (isCanonicalDenominationAtomic(flagValue2)) poolDenominationAtomic = flagValue2;
70366
+ else if (flagValue2 === "") configError = "--pool-denomination requires a value";
70171
70367
  else configError = "--pool-denomination must be a canonical positive u64 atomic string";
70172
70368
  } else if (envValue !== void 0 && envValue !== "") {
70173
70369
  let map;
@@ -70805,8 +71001,8 @@ async function runRecoverCommand(args) {
70805
71001
  }
70806
71002
  const pool2 = flag(args, "--pool") ?? requiredPoolFromHome();
70807
71003
  const store = openWalletStore(walletPath(pool2), passphrase());
70808
- const rpcUrl = flag(args, "--rpc") ?? process.env.UNDERMARKET_SOLANA_RPC_URL;
70809
- if (!rpcUrl) throw new Error("recover requires --rpc or UNDERMARKET_SOLANA_RPC_URL");
71004
+ const rpcUrl = resolveRpcUrl(args);
71005
+ if (!rpcUrl) throw new Error("rpc_required: recover requires --rpc-file <file> (or --rpc, or UNDERMARKET_SOLANA_RPC_URL)");
70810
71006
  const programId = flag(args, "--program");
70811
71007
  if (!programId) throw new Error("recover requires --program <program id>");
70812
71008
  const digestsFile = flag(args, "--digests");
@@ -70914,7 +71110,7 @@ async function main(argv = process.argv.slice(2)) {
70914
71110
  case "status": {
70915
71111
  const pool2 = flag(args, "--pool") ?? requiredPoolFromHome();
70916
71112
  const store = openWalletStore(walletPath(pool2), passphrase());
70917
- const rpcUrl = flag(args, "--rpc") ?? process.env.UNDERMARKET_SOLANA_RPC_URL;
71113
+ const rpcUrl = resolveRpcUrl(args);
70918
71114
  const solana = import_web3105.Keypair.fromSecretKey(Uint8Array.from(store.state.solanaSecretKey));
70919
71115
  const report = {
70920
71116
  pool: canonicalPoolKey(store.state.pool),
@@ -70960,11 +71156,11 @@ async function main(argv = process.argv.slice(2)) {
70960
71156
  await runValueMove(command, args);
70961
71157
  return;
70962
71158
  case "deposit-compact":
70963
- return runDepositCompactCommand(args, { flag, walletPath, acquireLease: acquireWalletProcessLease, openStore: (pool2) => openWalletStore(walletPath(pool2), passphrase()), connect });
71159
+ return runDepositCompactCommand(args, { flag, rpcUrl: (cliArgs) => resolveRpcUrl(cliArgs), walletPath, acquireLease: acquireWalletProcessLease, openStore: (pool2) => openWalletStore(walletPath(pool2), passphrase()), connect });
70964
71160
  case "deposit-reclaim":
70965
- return runDepositReclaimCommand(args, { flag, openStore: (pool2) => openWalletStore(walletPath(pool2), passphrase()), connect });
71161
+ return runDepositReclaimCommand(args, { flag, rpcUrl: (cliArgs) => resolveRpcUrl(cliArgs), openStore: (pool2) => openWalletStore(walletPath(pool2), passphrase()), connect });
70966
71162
  case "deposit-scan":
70967
- return runDepositScanCommand(args, { flag, openStore: (pool2) => openWalletStore(walletPath(pool2), passphrase()), connect });
71163
+ return runDepositScanCommand(args, { flag, rpcUrl: (cliArgs) => resolveRpcUrl(cliArgs), openStore: (pool2) => openWalletStore(walletPath(pool2), passphrase()), connect });
70968
71164
  case "withdraw": {
70969
71165
  await runValueMove(command, args);
70970
71166
  return;
@@ -70972,8 +71168,8 @@ async function main(argv = process.argv.slice(2)) {
70972
71168
  case "note-exit": {
70973
71169
  const pool2 = flag(args, "--pool") ?? requiredPoolFromHome();
70974
71170
  const store = openWalletStore(walletPath(pool2), passphrase());
70975
- const rpcUrl = flag(args, "--rpc") ?? process.env.UNDERMARKET_SOLANA_RPC_URL;
70976
- if (!rpcUrl) throw new Error("note-exit requires --rpc or UNDERMARKET_SOLANA_RPC_URL");
71171
+ const rpcUrl = resolveRpcUrl(args);
71172
+ if (!rpcUrl) throw new Error("rpc_required: note-exit requires --rpc-file <file> (or --rpc, or UNDERMARKET_SOLANA_RPC_URL)");
70977
71173
  const programId = flag(args, "--program");
70978
71174
  const recipientTokenAccount = flag(args, "--to");
70979
71175
  const serviceUrl2 = flag(args, "--service");
@@ -71094,18 +71290,18 @@ async function main(argv = process.argv.slice(2)) {
71094
71290
  "usage: shielded-wallet <init|restore|address|status|deposit|pay|withdraw|recover|note-exit|backup-export|backup-verify> [options]",
71095
71291
  " restore --pool <pubkey> --network <caip-2> --asset <mint> --recovery <init's recovery file>",
71096
71292
  " (a lost wallet from init's recovery text; then recover, deposit-scan, pocket scan)",
71097
- " deposit --pool <pubkey> --program <id> --proof-tools <file> --deposit-id <id> --amount <atomic> [--rpc <url>]",
71293
+ " deposit --pool <pubkey> --program <id> --proof-tools <file> --deposit-id <id> --amount <atomic> [--rpc-file <file>]",
71098
71294
  " (first run proves and records; back up; then add --submit --service <url>)",
71099
71295
  " [--relay --service <url> [--one-time-index <n>]] USDC from a one-time address; the facilitator pays every fee",
71100
- " deposit-reclaim --pool <pubkey> --program <id> --deposit-id <id> --to <token account> (--service <url> | --fee-payer-key-file <file>) [--rpc <url>]",
71296
+ " deposit-reclaim --pool <pubkey> --program <id> --deposit-id <id> --to <token account> (--service <url> | --fee-payer-key-file <file>) [--rpc-file <file>]",
71101
71297
  " (a relayed deposit that did not land: its USDC back out of the one-time address)",
71102
- " deposit-scan --pool <pubkey> [--from <index>] [--count <n>] [--rpc <url>]",
71298
+ " deposit-scan --pool <pubkey> [--from <index>] [--count <n>] [--rpc-file <file>]",
71103
71299
  " (after a restore from the recovery text: funded one-time addresses in one index range, default 0-63)",
71104
71300
  " backup-export --pool <pubkey> --to <new directory>",
71105
71301
  " backup-verify --pool <pubkey> --backup-dir <directory>",
71106
- " recover --pool <pubkey> --program <id> --proof-tools <file> [--rpc <url>] [--fresh] [--history-rpc <own|url>]",
71302
+ " recover --pool <pubkey> --program <id> --proof-tools <file> [--rpc-file <file>] [--fresh] [--history-rpc <own|url>]",
71107
71303
  " note-exit --pool <pubkey> --program <id> --to <token account> --service <url> --proof-tools <file> [--exit-amount <atomic>]",
71108
- " [--rpc <url>] [--digests <file>] [--note <commitment>]",
71304
+ " [--rpc-file <file>] [--digests <file>] [--note <commitment>]",
71109
71305
  " [--min-delay-ms <ms>] [--jitter-ms <ms>] [--exit-now]",
71110
71306
  " [--pool-denomination <atomic>] [--allow-off-denomination]",
71111
71307
  " [--recipient-owner <pubkey> --allow-foreign-recipient] pay a token account another key owns",
@@ -71122,9 +71318,9 @@ async function runSelfCustodyDeposit(args) {
71122
71318
  const programId = flag(args, "--program");
71123
71319
  const depositId = flag(args, "--deposit-id");
71124
71320
  const amount = flag(args, "--amount");
71125
- const rpcUrl = flag(args, "--rpc") ?? process.env.UNDERMARKET_SOLANA_RPC_URL;
71321
+ const rpcUrl = resolveRpcUrl(args);
71126
71322
  if (!programId || !depositId || !/^[1-9][0-9]*$/.test(amount ?? "") || !rpcUrl) {
71127
- throw new Error("deposit requires --program, --deposit-id, --amount <atomic>, --proof-tools, and --rpc or UNDERMARKET_SOLANA_RPC_URL");
71323
+ throw new Error("deposit requires --program, --deposit-id, --amount <atomic>, --proof-tools, and --rpc-file <file> (or --rpc, or UNDERMARKET_SOLANA_RPC_URL)");
71128
71324
  }
71129
71325
  const proofTools = loadP03PublicPaymentProofTools(flag(args, "--proof-tools"));
71130
71326
  const path = walletPath(pool2);
@@ -71209,7 +71405,7 @@ async function runValueMove(command, args) {
71209
71405
  const { profile, vector, artifactDir, production } = loadProfileDirectory(profileDir);
71210
71406
  const pool2 = new import_web3105.PublicKey(flag(args, "--pool") ?? profile.pool ?? poolFromProfile(profile));
71211
71407
  const store = openWalletStore(walletPath(pool2.toBase58()), passphrase());
71212
- const rpcUrl = flag(args, "--rpc") ?? process.env.UNDERMARKET_SOLANA_RPC_URL ?? profile.rpcUrl;
71408
+ const rpcUrl = resolveRpcUrl(args, { fallback: profile.rpcUrl });
71213
71409
  if (!rpcUrl) throw new Error("an RPC URL is required");
71214
71410
  const connection = connect(rpcUrl);
71215
71411
  await assertBroadcastAllowed(connection);
@@ -71502,6 +71698,7 @@ var init_shielded_wallet = __esm({
71502
71698
  init_notes();
71503
71699
  init_memo();
71504
71700
  init_pool_denomination();
71701
+ init_rpc_url();
71505
71702
  init_prove();
71506
71703
  init_chain();
71507
71704
  init_store2();
@@ -72614,6 +72811,7 @@ async function prepareRecoveredPayment({
72614
72811
  circuitSource,
72615
72812
  anchor
72616
72813
  });
72814
+ markPhase("account-path");
72617
72815
  }
72618
72816
  if (wantedNotes.length > MAX_RESPEND_NOTES) fail15("too_many_notes");
72619
72817
  if (wantedNotes.length > 0) await prepareFold(deps.buildRecoveryPath);
@@ -72623,11 +72821,13 @@ async function prepareRecoveredPayment({
72623
72821
  circuitSource,
72624
72822
  anchor
72625
72823
  }));
72824
+ markPhase("note-paths");
72626
72825
  if (reserved !== void 0 && [accountPath, ...notePaths].some((path) => path?.replayValue === void 0 || reserved.has(BigInt(path.replayValue).toString()))) {
72627
72826
  heldInputsWait();
72628
72827
  }
72629
72828
  await prepareFold(deps.buildFrontier);
72630
72829
  const frontier = (deps.buildFrontier ?? buildFinalizedP03Frontier)({ history, circuitSource });
72830
+ markPhase("frontier");
72631
72831
  const outputAccountI = anchor.index;
72632
72832
  const atomicOf = (entries, key) => entries.reduce((sum2, entry) => sum2 + BigInt(entry[key].b), 0n);
72633
72833
  if (accountFile === void 0 && accounts.length > 1 && BigInt(account2.account.b) + atomicOf(wantedNotes, "note") < BigInt(witnessQuote.amountAtomic) && atomicOf(accounts, "account") + atomicOf(notes, "note") >= BigInt(witnessQuote.amountAtomic)) {
@@ -92561,7 +92761,11 @@ async function finalizePendingReceipt({ store, connection, programId, name, time
92561
92761
  strict = true;
92562
92762
  const [tipClock, finalClock] = await Promise.all([finalizedClock(connection, "confirmed"), finalizedClock(connection)]);
92563
92763
  const tip = tipClock.slot;
92564
- const target = tip > finalClock.slot ? tip + FINALITY_GATE_MARGIN_SLOTS : tip;
92764
+ let target = tip > finalClock.slot ? tip + FINALITY_GATE_MARGIN_SLOTS : tip;
92765
+ const settleSlot = receiptSettleSlot(record.answeredSettleSlot);
92766
+ if (settleSlot !== void 0 && BigInt(settleSlot) >= tip - FINALITY_GATE_SETTLE_WINDOW_SLOTS && BigInt(settleSlot) + FINALITY_GATE_SETTLE_MARGIN_SLOTS < target) {
92767
+ target = BigInt(settleSlot) + FINALITY_GATE_SETTLE_MARGIN_SLOTS;
92768
+ }
92565
92769
  gateClock = finalClock;
92566
92770
  while (gateClock.slot < target && now() < deadline) {
92567
92771
  await sleep5(Math.min(pollMs, FINALITY_GATE_POLL_MS));
@@ -93231,12 +93435,21 @@ async function payResourceOnce({
93231
93435
  if (landed) recordLandedWithoutAnswer(store, walName2, record);
93232
93436
  throw error;
93233
93437
  }
93234
- store.writeWal(walName2, { ...record, state: "receipt", receipt, resultBase64, receiptFinality: "pending", answeredAtMs: Date.now() });
93438
+ const answeredSettleSlot = receiptSettleSlot(receipt.extra?.settleSlot);
93439
+ store.writeWal(walName2, {
93440
+ ...record,
93441
+ state: "receipt",
93442
+ receipt,
93443
+ resultBase64,
93444
+ receiptFinality: "pending",
93445
+ answeredAtMs: Date.now(),
93446
+ ...answeredSettleSlot !== void 0 ? { answeredSettleSlot } : {}
93447
+ });
93235
93448
  mark("receipt-pending");
93236
93449
  withPhaseScope(mark, "anchor-next", () => requestSubAnchorForNewLeaf());
93237
93450
  return { replayed: false, receipt, resultBase64, receiptFinality: "pending" };
93238
93451
  }
93239
- var import_web3111, fail19, failUnresolved, hash9, OWNER_UNSHIELD_REQUEST, OWNER_UNSHIELD_ORIGIN, POCKET_FILL_ID, POOL_MOVE_PATH, RECEIPT_FINALITY_TIMEOUT_MS, RECEIPT_FINALITY_POLL_MS, FINALITY_GATE_MARGIN_SLOTS, FINALITY_GATE_POLL_MS, FINALITY_GATE_SKIP_AFTER_MS, PENDING_FINALITY, isPending, REPLAY_INPUTS, PAYMENT_OUTPUT_INPUT2, QUOTE_MARGIN_SECONDS, SELLER_REFUSED_PAY_AGAIN, SELLER_REASON_FACILITATOR_UNDERFUNDED, ANCHOR_EPOCH_MS, ANCHOR_MARGIN_MS, ANCHOR_WAIT_CAP_MS, DEAD_PAYMENT_MARGIN_SECONDS, SYSVAR_OWNER, quoteLiveAt, replayValuesOf, finalPayments, FINAL_UNCHANGED;
93452
+ var import_web3111, fail19, failUnresolved, hash9, OWNER_UNSHIELD_REQUEST, OWNER_UNSHIELD_ORIGIN, POCKET_FILL_ID, POOL_MOVE_PATH, RECEIPT_FINALITY_TIMEOUT_MS, RECEIPT_FINALITY_POLL_MS, FINALITY_GATE_MARGIN_SLOTS, FINALITY_GATE_POLL_MS, FINALITY_GATE_SETTLE_MARGIN_SLOTS, FINALITY_GATE_SETTLE_WINDOW_SLOTS, receiptSettleSlot, FINALITY_GATE_SKIP_AFTER_MS, PENDING_FINALITY, isPending, REPLAY_INPUTS, PAYMENT_OUTPUT_INPUT2, QUOTE_MARGIN_SECONDS, SELLER_REFUSED_PAY_AGAIN, SELLER_REASON_FACILITATOR_UNDERFUNDED, ANCHOR_EPOCH_MS, ANCHOR_MARGIN_MS, ANCHOR_WAIT_CAP_MS, DEAD_PAYMENT_MARGIN_SECONDS, SYSVAR_OWNER, quoteLiveAt, replayValuesOf, finalPayments, FINAL_UNCHANGED;
93240
93453
  var init_payResource = __esm({
93241
93454
  "wallet/src/pay/payResource.mjs"() {
93242
93455
  "use strict";
@@ -93280,6 +93493,9 @@ var init_payResource = __esm({
93280
93493
  RECEIPT_FINALITY_POLL_MS = 2e3;
93281
93494
  FINALITY_GATE_MARGIN_SLOTS = 10n;
93282
93495
  FINALITY_GATE_POLL_MS = 1e3;
93496
+ FINALITY_GATE_SETTLE_MARGIN_SLOTS = 2n;
93497
+ FINALITY_GATE_SETTLE_WINDOW_SLOTS = 150n;
93498
+ receiptSettleSlot = (value) => typeof value === "string" && /^[1-9][0-9]{0,15}$/.test(value) && Number.isSafeInteger(Number(value)) ? value : void 0;
93283
93499
  FINALITY_GATE_SKIP_AFTER_MS = 6e4;
93284
93500
  PENDING_FINALITY = /* @__PURE__ */ new Set(["pending", "confirmed"]);
93285
93501
  isPending = (record) => record?.state === "receipt" && PENDING_FINALITY.has(record.receiptFinality);
@@ -93324,7 +93540,7 @@ async function main2(args = process.argv.slice(2)) {
93324
93540
  const needed = options["cancel-request"] ? ["wallet", "passphrase-file"] : REQUIRED;
93325
93541
  if (needed.some((key) => !options[key])) throw new Error("p03_resource_cli_arguments_missing");
93326
93542
  const historyUrl = historyRpcUrl(options["history-rpc"]);
93327
- const rpcUrl = () => resolveRpcUrl({
93543
+ const rpcUrl = () => resolveRpcUrl2({
93328
93544
  rpcFile: options["rpc-file"],
93329
93545
  origin: options["memo-service"],
93330
93546
  readFile: (path) => readFileSync(resolve16(path), "utf8").trim()
@@ -94673,9 +94889,16 @@ var init_EncryptedJsonFile = __esm({
94673
94889
  }
94674
94890
  /** write(), returning the exact text it put in the file. */
94675
94891
  async writeReturningPayload(value) {
94892
+ return this.writeText(this.encryptToText(value));
94893
+ }
94894
+ /**
94895
+ * write()'s file step for text made by encryptToText (this file's key, or
94896
+ * another file's with the same key): the same temp file, fsync, rename and
94897
+ * directory fsync. Returns the text. Lets a caller that keeps two copies of
94898
+ * one value under one key encrypt it once (the P03 settle journal).
94899
+ */
94900
+ async writeText(payload2) {
94676
94901
  await mkdir3(dirname18(this.filePath), { recursive: true });
94677
- const body = JSON.stringify(value);
94678
- const payload2 = this.keyMaterial ? JSON.stringify(this.encrypt(body)) : body;
94679
94902
  const temporaryPath = `${this.filePath}.${process.pid}.${randomBytes16(6).toString("hex")}.tmp`;
94680
94903
  await writeFile2(temporaryPath, payload2, { mode: 384 });
94681
94904
  if (this.options.durable) {
@@ -94723,7 +94946,11 @@ var init_EncryptedJsonFile = __esm({
94723
94946
  this.writeTextSync(payload2);
94724
94947
  return payload2;
94725
94948
  }
94726
- /** The exact text writeSync would put in the file for `value` (a caller can hash it before the write). */
94949
+ /**
94950
+ * The exact text writeSync would put in the file for `value` (a caller can hash it before the write).
94951
+ * Compact on purpose: the payload is encrypted (pretty-printing ciphertext buys nothing and inflates
94952
+ * every full-file rewrite), and the plaintext fallback only exists for local dev without an encryption key.
94953
+ */
94727
94954
  encryptToText(value) {
94728
94955
  return this.keyMaterial ? JSON.stringify(this.encrypt(JSON.stringify(value))) : JSON.stringify(value);
94729
94956
  }
@@ -96434,7 +96661,10 @@ function publicReceipt(receipt) {
96434
96661
  pool: extra.pool,
96435
96662
  programId: extra.programId,
96436
96663
  quoteCommitment: extra.quoteCommitment,
96437
- memoAvailabilityDigest: extra.memoAvailabilityDigest
96664
+ memoAvailabilityDigest: extra.memoAvailabilityDigest,
96665
+ // The settle's confirmed slot: the payer's next finality gate waits for it (payResource.mjs
96666
+ // FINALITY_GATE_SETTLE_MARGIN_SLOTS, 2026-10-16). A public chain fact of this payment.
96667
+ settleSlot: extra.settleSlot
96438
96668
  }
96439
96669
  };
96440
96670
  }
@@ -96445,6 +96675,30 @@ function sellerHttpReply(result) {
96445
96675
  const body = result.result ? Buffer.from(result.result.bodyBase64, "base64") : Buffer.from(JSON.stringify(result.paymentRequired ?? { code: result.code, ...result.reason === FACILITATOR_UNDERFUNDED ? { reason: result.reason } : {} }));
96446
96676
  return { status: result.status, headers, body };
96447
96677
  }
96678
+ function earlyConfirmedTransaction(connection, signature) {
96679
+ if (typeof connection.getTransaction !== "function" || typeof connection.getVerifiedTransaction === "function" || typeof connection.getVerifiedTransactions === "function") {
96680
+ return void 0;
96681
+ }
96682
+ return connection.getTransaction(signature, CONFIRMED_TRANSACTION_READ).then((raw) => raw ?? null, () => null);
96683
+ }
96684
+ function withPrefetchedTransaction(connection, signature, prefetched) {
96685
+ let pending = prefetched;
96686
+ const getTransaction = (requested, options) => {
96687
+ if (pending && requested === signature && options?.commitment === CONFIRMED_TRANSACTION_READ.commitment && options?.maxSupportedTransactionVersion === CONFIRMED_TRANSACTION_READ.maxSupportedTransactionVersion) {
96688
+ const answer = pending;
96689
+ pending = void 0;
96690
+ return Promise.resolve(answer);
96691
+ }
96692
+ return connection.getTransaction(requested, options);
96693
+ };
96694
+ return new Proxy(connection, {
96695
+ get(target, property) {
96696
+ if (property === "getTransaction") return getTransaction;
96697
+ const value = Reflect.get(target, property, target);
96698
+ return typeof value === "function" ? value.bind(target) : value;
96699
+ }
96700
+ });
96701
+ }
96448
96702
  async function createSellerAdapter({
96449
96703
  config,
96450
96704
  connection,
@@ -96760,11 +97014,21 @@ async function createSellerAdapter({
96760
97014
  mark("receipt-unverified");
96761
97015
  return completed2;
96762
97016
  }
96763
- const status = await visibleReceiptStatus(connection, receipt.transaction, visibility);
97017
+ const early = earlyConfirmedTransaction(connection, receipt.transaction);
97018
+ let polled = false;
97019
+ const status = await visibleReceiptStatus(
97020
+ connection,
97021
+ receipt.transaction,
97022
+ { ...visibility, sleep: (ms) => {
97023
+ polled = true;
97024
+ return visibility.sleep(ms);
97025
+ } }
97026
+ );
96764
97027
  const level = receiptStatusLevel(status);
96765
97028
  if (!level || require2 === "finalized" && level !== "finalized") fail26("receipt_pending");
97029
+ const prefetched = early && !polled && level === "confirmed" ? await early : null;
96766
97030
  await validateReceiptTransaction(
96767
- connection,
97031
+ prefetched ? withPrefetchedTransaction(connection, receipt.transaction, prefetched) : connection,
96768
97032
  receipt,
96769
97033
  { paymentHeader: offer.paymentHeader },
96770
97034
  { pool: profile.pool },
@@ -97096,7 +97360,7 @@ async function createSellerAdapter({
97096
97360
  }));
97097
97361
  return { server, handle, close, signerPublicKey, settledTransactions, offerSummaries };
97098
97362
  }
97099
- 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;
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;
97100
97364
  var init_service = __esm({
97101
97365
  "wallet/src/seller/service.mjs"() {
97102
97366
  "use strict";
@@ -97165,6 +97429,7 @@ var init_service = __esm({
97165
97429
  6e5
97166
97430
  ]);
97167
97431
  FOLLOW_UP_RESETTLE_FROM = 4;
97432
+ CONFIRMED_TRANSACTION_READ = Object.freeze({ commitment: "confirmed", maxSupportedTransactionVersion: 1 });
97168
97433
  }
97169
97434
  });
97170
97435
 
@@ -97340,20 +97605,19 @@ async function selfPay({
97340
97605
  ...record,
97341
97606
  state: "paid",
97342
97607
  transaction,
97343
- noteCommitment: (deps.findNote ?? selfPaidNoteCommitment)(store, unshieldId, eta).toString()
97608
+ noteCommitment: (await (deps.findNote ?? selfPaidNoteCommitment)(store, unshieldId, eta)).toString()
97344
97609
  };
97345
97610
  store.writeWal(name, record);
97346
97611
  return record;
97347
97612
  }
97348
- function selfPaidNoteCommitment(store, unshieldId, eta) {
97613
+ async function selfPaidNoteCommitment(store, unshieldId, eta) {
97349
97614
  const key = sha256Hex2(Buffer.from(selfPayRequestId(unshieldId), "utf8"));
97350
97615
  const payment = store.readWal(`resource-payment-${key}`);
97351
97616
  const proof = store.readWal(`resource-proof-${key}`)?.proved?.output;
97352
97617
  const accepted = payment?.paymentRequired?.accepts?.find((entry) => entry.scheme === "shielded-exact");
97353
97618
  if (!proof?.memoBundleHex || !accepted) fail27("payment_wal_missing");
97354
- const { accepted: outputs } = trialDecryptBundle({
97619
+ const [{ accepted: outputs }] = await trialDecryptBundles([{
97355
97620
  bundleBytes: Uint8Array.from(Buffer.from(proof.memoBundleHex.replace(/^0x/, ""), "hex")),
97356
- eta,
97357
97621
  poolDomain: BigInt(proof.poolDomain),
97358
97622
  outCommitment: BigInt(proof.outCommitment),
97359
97623
  contextCommitment: BigInt(proof.quoteCommitment),
@@ -97361,7 +97625,7 @@ function selfPaidNoteCommitment(store, unshieldId, eta) {
97361
97625
  transitionKind: 2,
97362
97626
  zeroLeaf: P03_PINNED_ZERO_LEAF,
97363
97627
  subtreeStartIndex: 0n
97364
- });
97628
+ }], eta);
97365
97629
  const notes = outputs.filter((entry) => entry.kind === "note");
97366
97630
  if (notes.length !== 1 || notes[0].note.b !== BigInt(proof.amountAtomic)) fail27("self_paid_note_not_found");
97367
97631
  return notes[0].commitment;
@@ -97373,7 +97637,7 @@ var init_selfPay = __esm({
97373
97637
  init_fs();
97374
97638
  init_service();
97375
97639
  init_notes2();
97376
- init_memo2();
97640
+ init_memoDecryptPool();
97377
97641
  init_store();
97378
97642
  init_recoveryPath();
97379
97643
  init_payResource();
@@ -97481,7 +97745,7 @@ async function main3(args = process.argv.slice(2)) {
97481
97745
  const feeKey = import_web3121.Keypair.fromSecretKey(Uint8Array.from(store.state.solanaSecretKey)).publicKey;
97482
97746
  const recipientOwner = new import_web3121.PublicKey(options["recipient-owner"]);
97483
97747
  if (recipientOwner.equals(feeKey)) fail28("recipient_owner_is_deposit_key");
97484
- const connection = connect2(resolveRpcUrl({
97748
+ const connection = connect2(resolveRpcUrl2({
97485
97749
  rpcFile: options["rpc-file"],
97486
97750
  origin: origin.origin,
97487
97751
  readFile: (path) => readFileSync(resolve19(path), "utf8").trim()
@@ -116425,7 +116689,7 @@ async function v0bMain(name, values, deps) {
116425
116689
  const rpc3 = values.has("--rpc-file") ? createV0bRpc(readSecretFile(values.get("--rpc-file"), "--rpc-file")) : void 0;
116426
116690
  return print(await v0bBalance({ store, rpc: rpc3 }));
116427
116691
  }
116428
- const rpc2 = createV0bRpc(resolveRpcUrl({
116692
+ const rpc2 = createV0bRpc(resolveRpcUrl2({
116429
116693
  rpcFile: values.get("--rpc-file"),
116430
116694
  env: deps.env ?? process.env,
116431
116695
  origin: values.get("--service"),
@@ -118165,7 +118429,7 @@ function playTokenOf(values) {
118165
118429
  }
118166
118430
  function rpcOf(name, values, { origin, env, err }) {
118167
118431
  if (!(READS_CHAIN.has(name) || name === "laneb receipts" && values.has("--verify") || values.has("--rpc-file"))) return void 0;
118168
- const { url } = resolveRpcUrl({
118432
+ const { url } = resolveRpcUrl2({
118169
118433
  rpcFile: values.get("--rpc-file"),
118170
118434
  env,
118171
118435
  origin,
@@ -121263,7 +121527,7 @@ async function main4(args = process.argv.slice(2), io = { out: (text3) => consol
121263
121527
  io.out(JSON.stringify({ success: true, ...releasePendingFill(store) }));
121264
121528
  return;
121265
121529
  }
121266
- const connection = connect2(resolveRpcUrl({
121530
+ const connection = connect2(resolveRpcUrl2({
121267
121531
  rpcFile: options["rpc-file"],
121268
121532
  origin: options.facilitator,
121269
121533
  readFile: (path) => readFileSync(resolve20(path), "utf8").trim()
@@ -124150,7 +124414,7 @@ function acknowledgeSuccessionNotice(store, fromPool, state, { now = Date.now()
124150
124414
  }
124151
124415
  var MODEL_VECTORS = fileURLToPath16(new URL("../../vendor/shielded-x402-model/vectors/rust-vectors.json", new URL("__bundled_source__/wallet/src/succession/scheduler.mjs", import.meta.url).href));
124152
124416
  var pinnedGenerator6 = () => JSON.parse(readFileSync(MODEL_VECTORS, "utf8")).payerKeys.generator;
124153
- var yieldTurn = () => new Promise((resolveTurn) => {
124417
+ var yieldTurn2 = () => new Promise((resolveTurn) => {
124154
124418
  setImmediate(resolveTurn);
124155
124419
  });
124156
124420
  var CIRCUIT_SOURCE = Object.freeze({ poseidonParametersSha256: P03_PINNED_POSEIDON_PARAMETERS_SHA256, zeroLeaf: P03_PINNED_ZERO_LEAF });
@@ -124174,9 +124438,9 @@ async function prepareSuccessionMove({ entry, verified, store, targetStore, conn
124174
124438
  deploymentIdentity: toolsB.deploymentIdentity
124175
124439
  });
124176
124440
  const recoverSource = async () => {
124177
- await yieldTurn();
124441
+ await yieldTurn2();
124178
124442
  const history2 = await loadHistory({ store, connection: split5, programId: entry.programId, pool: entry.from, deploymentIdentity: toolsA.deploymentIdentity });
124179
- await yieldTurn();
124443
+ await yieldTurn2();
124180
124444
  const successors = await loadSuccessorHistories({
124181
124445
  history: history2,
124182
124446
  store,
@@ -124185,7 +124449,7 @@ async function prepareSuccessionMove({ entry, verified, store, targetStore, conn
124185
124449
  deploymentIdentity: toolsB.deploymentIdentity,
124186
124450
  loadHistory
124187
124451
  });
124188
- await yieldTurn();
124452
+ await yieldTurn2();
124189
124453
  const walStates = captureWalStates(store);
124190
124454
  const recovery2 = await recover({
124191
124455
  store,
@@ -124200,7 +124464,7 @@ async function prepareSuccessionMove({ entry, verified, store, targetStore, conn
124200
124464
  return { recovery: recovery2, history: history2 };
124201
124465
  };
124202
124466
  const { recovery, history } = await recoverSource();
124203
- await yieldTurn();
124467
+ await yieldTurn2();
124204
124468
  const movable = recovery.recovered.filter((output2) => output2.spendStatus === "succession-movable");
124205
124469
  const eta = deriveWalletViewingAuthority2({ seedBytes: Uint8Array.from(Buffer.from(store.state.seedHex, "hex")), generator: pinnedGenerator6() }).eta;
124206
124470
  const anchor = { index: BigInt(verified.record.finalIndex), root: BigInt(`0x${verified.record.finalRoot}`) };
@@ -125832,6 +126096,8 @@ init_serviceUrl();
125832
126096
  init_anchorClock();
125833
126097
  var shownCaps = (caps) => Object.fromEntries(Object.entries(caps).map(([name, value]) => [name, String(value) === NO_LIMIT_ATOMIC2 ? "none" : value]));
125834
126098
  var RESOURCE_PAYMENT_WAL_PREFIX2 = "resource-payment-";
126099
+ var POOL_LAYOUT_CODE = /pool_state_length_or_magic_invalid|pool_layout_table_invalid|pool_state_anchor_layout_invalid/;
126100
+ var POOL_LAYOUT_UPDATE = "This wallet release cannot read the pool account: the pool program most likely changed its layout in an upgrade. Tell the user to restart this MCP server in the host (or restart the host app): each start loads the latest shinjuku-shielded";
125835
126101
  var SELF_PAY_ORIGIN3 = "https://self-pay.invalid/";
125836
126102
  var REQUEST_ID2 = /^[A-Za-z0-9._:-]{1,128}$/;
125837
126103
  var POSITIVE_ATOMIC4 = /^[1-9][0-9]{0,30}$/;
@@ -126205,16 +126471,17 @@ async function runShieldFromElsewhere({ session, depositId, amountAtomic, onStag
126205
126471
  const result = await (steps.submit ?? submitRelayedDeposit)(common);
126206
126472
  return { state: result.state, transaction: typeof result.receipt?.signature === "string" ? result.receipt.signature : null };
126207
126473
  }
126208
- var walkWalletHistory = (session, store) => loadWalletP03History({
126474
+ var walkWalletHistory = (session, store, share) => loadWalletP03History({
126209
126475
  store,
126210
126476
  connection: connectSplit(session.connection, session.historyUrl),
126211
126477
  programId: session.programId,
126212
126478
  pool: store.state.pool,
126213
- deploymentIdentity: session.proofTools.deploymentIdentity
126479
+ deploymentIdentity: session.proofTools.deploymentIdentity,
126480
+ ...share ? { share } : {}
126214
126481
  });
126215
- async function refreshWallet({ session, store, history: walked }) {
126482
+ async function refreshWallet({ session, store, history: walked, share }) {
126216
126483
  const walStates = captureWalStates(store);
126217
- const history = walked ?? await walkWalletHistory(session, store);
126484
+ const history = walked ?? await walkWalletHistory(session, store, share);
126218
126485
  const result = await runRecover({
126219
126486
  store,
126220
126487
  connection: session.connection,
@@ -126271,6 +126538,14 @@ function selfPayMayHaveSigned(store, unshieldId) {
126271
126538
  if (record && record.state !== "started") return true;
126272
126539
  return Boolean(store.readWal(`${RESOURCE_PAYMENT_WAL_PREFIX2}${sha256Hex2(Buffer.from(selfPayRequestId(unshieldId), "utf8"))}`));
126273
126540
  }
126541
+ function leg1SettleSlot(store, float, requestId) {
126542
+ try {
126543
+ const inner = typeof float?.innerRequestId === "string" && float.innerRequestId ? float.innerRequestId : floatInnerRequestId(requestId);
126544
+ return store?.readWal(`${RESOURCE_PAYMENT_WAL_PREFIX2}${sha256Hex2(Buffer.from(inner, "utf8"))}`)?.answeredSettleSlot;
126545
+ } catch {
126546
+ return void 0;
126547
+ }
126548
+ }
126274
126549
  function bindUnshieldRecipient(store, unshieldId, amountAtomic, to) {
126275
126550
  const name = `self-pay-${unshieldId}`;
126276
126551
  const record = store.readWal(name);
@@ -126421,6 +126696,8 @@ var CAP_BALANCE_RETRY_MS = 5e3;
126421
126696
  var REFRESH_RETRY_MS = 6e4;
126422
126697
  var FINALITY_POLL_MS = 2e3;
126423
126698
  var FINALITY_WAIT_MS = 6e4;
126699
+ var FINALITY_REFRESH_LIMIT_MS = 18e4;
126700
+ var FINALITY_SETTLE_WINDOW_SLOTS = 150;
126424
126701
  var PUBLIC_READ_ANSWER_MS = 3e3;
126425
126702
  var SWITCH_WAIT_MS = 4e4;
126426
126703
  var ELICIT_TIMEOUT_MS = 5 * 6e4;
@@ -126461,7 +126738,13 @@ function createWalletTools({
126461
126738
  let refreshFailedAt = 0;
126462
126739
  let refreshFailedCode;
126463
126740
  let refreshInBackground = false;
126464
- const refreshStep = (store, history) => (ops.refresh ?? refreshWallet)({ session, store, ...history ? { history } : {} });
126741
+ const refreshStep = (store, history, share) => (ops.refresh ?? refreshWallet)({
126742
+ session,
126743
+ store,
126744
+ ...history ? { history } : {},
126745
+ ...share ? { share } : {}
126746
+ });
126747
+ let lastWriterEndedAt = 0;
126465
126748
  const refreshWalk = ops.refreshWalk ?? (ops.refresh ? void 0 : (store) => walkWalletHistory(session, store));
126466
126749
  const errorCode2 = (error, fallback) => describeError(error, { fallback, safeText: SAFE_TEXT }).split(/[\s:;(]/, 1)[0];
126467
126750
  const underLease = (task) => session.serial(async () => {
@@ -126475,6 +126758,7 @@ function createWalletTools({
126475
126758
  return await task(session.openStore());
126476
126759
  } finally {
126477
126760
  await release();
126761
+ lastWriterEndedAt = Date.now();
126478
126762
  }
126479
126763
  });
126480
126764
  const autoPockets = createAutoPockets({
@@ -126488,7 +126772,7 @@ function createWalletTools({
126488
126772
  deps: ops.autoPockets ?? {}
126489
126773
  });
126490
126774
  if (money.autoPockets === true) autoPockets.start();
126491
- const mover = createMoveScheduler({ session, underLease, log, onSettled: refreshAtFinality, deps: ops.move && typeof ops.move === "object" ? ops.move : {} });
126775
+ const mover = createMoveScheduler({ session, underLease, log, onSettled: () => refreshAtFinality(), deps: ops.move && typeof ops.move === "object" ? ops.move : {} });
126492
126776
  if (ops.move !== false) mover.start();
126493
126777
  const succession = ops.succession === false ? void 0 : {
126494
126778
  successionStatus,
@@ -126559,7 +126843,12 @@ function createWalletTools({
126559
126843
  }
126560
126844
  };
126561
126845
  const withPoolSwitch = (tool) => ({ ...tool, run: async (...args) => {
126562
- const output2 = await tool.run(...args);
126846
+ let output2;
126847
+ try {
126848
+ output2 = await tool.run(...args);
126849
+ } finally {
126850
+ if (tool.annotations?.readOnlyHint !== true) lastWriterEndedAt = Date.now();
126851
+ }
126563
126852
  if (!succession || !output2 || output2.isError || typeof output2.text !== "string") return output2;
126564
126853
  const read = readSwitch();
126565
126854
  const field4 = switchField(read);
@@ -126609,6 +126898,7 @@ function createWalletTools({
126609
126898
  function refreshShared({ background = false } = {}) {
126610
126899
  if (!refreshRun) {
126611
126900
  refreshInBackground = background;
126901
+ const calledAt = Date.now();
126612
126902
  refreshRun = (async () => {
126613
126903
  let early;
126614
126904
  if (background && refreshWalk) {
@@ -126624,7 +126914,8 @@ function createWalletTools({
126624
126914
  return underLease((store) => {
126625
126915
  const same2 = early !== void 0 && early.pool === store.state.pool && early.records === JSON.stringify(captureWalStates(store));
126626
126916
  if (early !== void 0 && !same2) log("MCP_REFRESH_WALK_AGAIN");
126627
- return refreshStep(store, same2 ? early.history : void 0);
126917
+ const share = early !== void 0 && !same2 ? { notBeforeMs: Math.max(calledAt, lastWriterEndedAt) } : void 0;
126918
+ return refreshStep(store, same2 ? early.history : void 0, share);
126628
126919
  });
126629
126920
  })().then(() => {
126630
126921
  refreshFailedAt = 0;
@@ -126647,31 +126938,52 @@ function createWalletTools({
126647
126938
  let finalityClosed = false;
126648
126939
  const finalityPollMs = ops.finalityPollMs ?? FINALITY_POLL_MS;
126649
126940
  const finalityWaitMs = ops.finalityWaitMs ?? FINALITY_WAIT_MS;
126650
- function refreshAtFinality(signature) {
126651
- if (typeof signature !== "string" || !signature || finalityClosed) return;
126652
- finalityTarget = signature;
126941
+ const finalityRefreshMs = ops.finalityRefreshMs ?? FINALITY_REFRESH_LIMIT_MS;
126942
+ const readSlot = (commitment) => withTimeout(
126943
+ Promise.resolve().then(() => (ops.slot ?? ((c) => session.connection.getSlot(c)))(commitment)),
126944
+ ops.slotReadMs ?? PUBLIC_READ_TIMEOUT_MS
126945
+ );
126946
+ function refreshAtFinality(settleSlot) {
126947
+ if (finalityClosed) return;
126948
+ const given = typeof settleSlot === "number" && Number.isSafeInteger(settleSlot) && settleSlot >= 0 ? settleSlot : typeof settleSlot === "string" && /^(0|[1-9][0-9]{0,15})$/.test(settleSlot) && Number.isSafeInteger(Number(settleSlot)) ? Number(settleSlot) : void 0;
126949
+ const next = readSlot("confirmed").catch(() => void 0).then((tip) => {
126950
+ if (!Number.isSafeInteger(tip)) return given;
126951
+ if (given === void 0 || given > tip || given < tip - FINALITY_SETTLE_WINDOW_SLOTS) return tip;
126952
+ return given;
126953
+ });
126954
+ const prior = finalityTarget;
126955
+ finalityTarget = Promise.all([prior, next]).then(([a, b]) => {
126956
+ const known = [a, b].filter((slot3) => Number.isSafeInteger(slot3));
126957
+ return known.length ? Math.max(...known) : void 0;
126958
+ });
126653
126959
  finalityDeadline = Date.now() + finalityWaitMs;
126654
- if (finalityWait) return;
126655
- const statuses = ops.signatureStatuses ?? ((signatures) => session.connection.getSignatureStatuses(signatures));
126960
+ if (!finalityWait) startFinalityWait();
126961
+ }
126962
+ function startFinalityWait() {
126963
+ let watched;
126656
126964
  finalityWait = (async () => {
126657
126965
  for (; ; ) {
126658
- const watched = finalityTarget;
126659
- let status;
126966
+ watched = finalityTarget;
126967
+ const target = await watched;
126968
+ if (finalityClosed) return;
126969
+ if (target === void 0) {
126970
+ log("MCP_FINALITY_WAIT_FAILED no settle slot");
126971
+ return;
126972
+ }
126973
+ let finalized;
126660
126974
  try {
126661
- status = (await statuses([watched]))?.value?.[0] ?? void 0;
126975
+ finalized = await readSlot("finalized");
126662
126976
  } catch {
126663
- status = void 0;
126977
+ finalized = void 0;
126664
126978
  }
126665
126979
  if (finalityClosed) return;
126666
- if (watched === finalityTarget && status?.err) {
126667
- log("MCP_FINALITY_WAIT_FAILED_TRANSACTION");
126668
- return;
126669
- }
126670
- if (watched === finalityTarget && status?.confirmationStatus === "finalized") {
126980
+ if (watched === finalityTarget && Number.isSafeInteger(finalized) && finalized >= target) {
126671
126981
  log("MCP_FINALITY_REFRESH");
126672
- await refreshShared({ background: true }).catch(() => {
126982
+ await withTimeout(refreshShared({ background: true }), finalityRefreshMs).catch((error) => {
126983
+ if (error?.timedOut) log("MCP_FINALITY_REFRESH_SLOW");
126673
126984
  });
126674
- return;
126985
+ if (watched === finalityTarget || finalityClosed) return;
126986
+ continue;
126675
126987
  }
126676
126988
  if (Date.now() + finalityPollMs > finalityDeadline) {
126677
126989
  log("MCP_FINALITY_WAIT_EXPIRED");
@@ -126684,6 +126996,8 @@ function createWalletTools({
126684
126996
  }
126685
126997
  })().finally(() => {
126686
126998
  finalityWait = void 0;
126999
+ if (finalityClosed || finalityTarget === watched) finalityTarget = void 0;
127000
+ else startFinalityWait();
126687
127001
  });
126688
127002
  }
126689
127003
  async function refreshIfStale() {
@@ -126969,7 +127283,7 @@ function createWalletTools({
126969
127283
  const floatPaid = float?.state === "receipt";
126970
127284
  const amounts = floatPaid ? { priceAtomic: float.priceAtomic, feeAtomic: float.feeAtomic, amountAtomic: float.paidAtomic } : { priceAtomic: receipt.amountAtomic ?? null, feeAtomic: "0", amountAtomic: receipt.amountAtomic ?? null };
126971
127285
  const one = paidStore ? localBalanceOf(paidStore) : null;
126972
- if (receipt.replayed !== true) refreshAtFinality(floatPaid ? float.floatSettleTransaction : transaction);
127286
+ if (receipt.replayed !== true) refreshAtFinality(floatPaid ? leg1SettleSlot(paidStore, float, requestId) : receipt.extra?.settleSlot);
126973
127287
  mark("mcp-result");
126974
127288
  return answerResult(paidAnswerContent({
126975
127289
  status: response.status,
@@ -127034,6 +127348,8 @@ function createWalletTools({
127034
127348
  next = `PAID: the chain shows the payment landed${error?.transaction ? ` (transaction ${error.transaction})` : ""}, but the seller did not return its answer and refuses the same payment. Do not call x402_pay again with this request_id. Tell the user it was paid without an answer; pay again only with a NEW request_id if the user wants a second payment`;
127035
127349
  } else if (code === "wallet_locked") {
127036
127350
  next = `another shinjuku-wallet command is using this wallet; wait for it, then call x402_pay again with request_id "${requestId}"`;
127351
+ } else if (POOL_LAYOUT_CODE.test(code)) {
127352
+ next = `${POOL_LAYOUT_UPDATE}. Then call x402_pay again with request_id "${requestId}" and the same url, method, headers, and body: it pays once. If the latest release says the same, tell your owner: ${code}`;
127037
127353
  } else if (OWNER_CAP_CODES.has(code)) {
127038
127354
  next = ownerOnlyNext(`${code}: ${rest || "a wallet-file cap refused this payment"}`.replace(/[.;\s]+$/, ""), code);
127039
127355
  } else if (code === "fetch_offer_confidential" || /confidential_(retired|use_laneb_pay_url)$/.test(code)) {
@@ -127557,6 +127873,7 @@ function createWalletTools({
127557
127873
  else if (/insufficient_token_balance|above_balance/.test(code)) next = "Nothing was moved. The wallet's own key holds less public USDC than amount_atomic (wallet_balance unshieldedAtomic). Tell the user, or call wallet_shield with a NEW request_id and a smaller amount_atomic";
127558
127874
  else if (/insufficient_sol/.test(code)) next = `Nothing new was moved. This request_id is a deposit an older version started that pays its own network fee, and the wallet's own key${ownAddress ? ` ${ownAddress}` : " (init's solanaFeePayer)"} holds too little SOL for it. Tell your owner: ${code}. A NEW request_id needs no SOL: the facilitator pays its fees`;
127559
127875
  else if (/pool_at_deposit_cap|pool_value_cap|above_pool_deposit_cap|above_cap|pool_terminal|id_reused/.test(code)) next = `Nothing new was moved. Do not retry this request. Tell your owner: ${code}.${atAddress}`;
127876
+ else if (POOL_LAYOUT_CODE.test(code)) next = `${POOL_LAYOUT_UPDATE}. Then ${again(kind, job)}: it continues where it stopped and never moves the money twice. If the latest release says the same, tell your owner: ${code}`;
127560
127877
  else if (code === "mcp_unshield_to_not_allowed") next = unboundNext(job.requestId, job.amountAtomic);
127561
127878
  else if (code === "mcp_unshield_to_invalid") next = "Nothing was paid. Tell the user: this address has no USDC token account of the pool mint; it must have one first";
127562
127879
  else if (code === "wallet_locked") next = `another shinjuku-wallet command is using this wallet; ${again(kind, job)} in ${POLL_SECONDS2} s: it continues where it stopped and never moves the money twice`;
@@ -127853,7 +128170,7 @@ function createWalletTools({
127853
128170
  job.result = result;
127854
128171
  log(`MCP_${kind.toUpperCase()}_DONE requestId=${job.requestId}`);
127855
128172
  if (kind !== "unshield") afterShield(job);
127856
- else refreshAtFinality(result?.exitTransaction ?? result?.selfPayTransaction);
128173
+ else refreshAtFinality();
127857
128174
  }, (error) => {
127858
128175
  job.failedStage = job.stage;
127859
128176
  job.state = "failed";
@@ -128132,7 +128449,7 @@ function createWalletTools({
128132
128449
  session,
128133
128450
  underLease,
128134
128451
  log,
128135
- deps: { onSettled: refreshAtFinality }
128452
+ deps: { onSettled: () => refreshAtFinality() }
128136
128453
  });
128137
128454
  } catch (error) {
128138
128455
  log(`MCP_POOL_SWITCH_SCHEDULER_NOT_STARTED: ${error?.name ?? "Error"}`);
@@ -129467,7 +129784,7 @@ async function main5(args = process.argv.slice(2)) {
129467
129784
  let steps;
129468
129785
  const runOne = async ({ id, amountAtomic, accountCommitment: accountCommitment3 }) => {
129469
129786
  if (!steps) {
129470
- const connection = connect2(resolveRpcUrl({
129787
+ const connection = connect2(resolveRpcUrl2({
129471
129788
  rpcFile: options["rpc-file"],
129472
129789
  origin: origin.origin,
129473
129790
  readFile: (path) => readFileSync(resolve27(path), "utf8").trim()
@@ -130090,7 +130407,7 @@ async function main6(args = process.argv.slice(2)) {
130090
130407
  try {
130091
130408
  const store = openWalletStore(walletPath2, passphrase2);
130092
130409
  timing("store-open");
130093
- const connection = connect2(resolveRpcUrl({
130410
+ const connection = connect2(resolveRpcUrl2({
130094
130411
  rpcFile: options["rpc-file"],
130095
130412
  origin: options["memo-service"],
130096
130413
  readFile: (path) => readFileSync(resolve28(path), "utf8").trim()
@@ -130168,7 +130485,7 @@ async function keepWarmOnce(values, { io, env = process.env }) {
130168
130485
  const walletPath2 = resolve29(values.get("--wallet"));
130169
130486
  const passphrase2 = assertPassphrase(values.has("--passphrase-file") ? readFileSync(resolve29(values.get("--passphrase-file")), "utf8").trim() : String(env.SHIELDED_WALLET_PASSPHRASE ?? ""));
130170
130487
  const store = openWalletStore(walletPath2, passphrase2);
130171
- const connection = connect2(resolveRpcUrl({
130488
+ const connection = connect2(resolveRpcUrl2({
130172
130489
  rpcFile: values.get("--rpc-file"),
130173
130490
  origin: values.get("--memo-service"),
130174
130491
  readFile: (path) => readFileSync(resolve29(path), "utf8").trim()
@@ -133261,7 +133578,7 @@ async function addFunds(values, deps) {
133261
133578
  const { io } = deps;
133262
133579
  const run5 = runValues(values, deps);
133263
133580
  const passphrase2 = readPrivateLine(run5.passphraseFile, "--passphrase-file");
133264
- const rpcUrl = run5.rpcFile !== void 0 ? readPrivateLine(run5.rpcFile, "--rpc-file") : resolveRpcUrl({ env: deps.env, origin: run5.service, notice: (text3) => io.err(text3) }).url;
133581
+ const rpcUrl = run5.rpcFile !== void 0 ? readPrivateLine(run5.rpcFile, "--rpc-file") : resolveRpcUrl2({ env: deps.env, origin: run5.service, notice: (text3) => io.err(text3) }).url;
133265
133582
  if (values.get("--route") === "privacy-cash") {
133266
133583
  try {
133267
133584
  run5.source = readSourceKeyFile(run5.fromKeyFile).publicKey.toBase58();
@@ -133672,7 +133989,7 @@ var sessionLease = (session) => (task) => {
133672
133989
  };
133673
133990
  return typeof session.serial === "function" ? session.serial(run5) : run5();
133674
133991
  };
133675
- var spendRpcUrl = (values, deps) => deps.rpcUrlFromFile ?? values.get("--rpc") ?? deps.env[RPC_ENV3] ?? resolveRpcUrl({
133992
+ var spendRpcUrl = (values, deps) => deps.rpcUrlFromFile ?? values.get("--rpc") ?? deps.env[RPC_ENV3] ?? resolveRpcUrl2({
133676
133993
  env: {},
133677
133994
  origin: facilitatorOriginOf(values.get("--service"), values.get("--facilitator"), values.get("--memo-service")),
133678
133995
  notice: () => {
@@ -135386,7 +135703,7 @@ async function runMoveCommand(values, deps, io) {
135386
135703
  `);
135387
135704
  return;
135388
135705
  }
135389
- const rpcUrl = values.has("--rpc-file") ? deps.readRpcFile(values.get("--rpc-file")) : deps.env[RPC_ENV3] || resolveRpcUrl({ env: {}, origin: facilitator, notice: io.err }).url;
135706
+ const rpcUrl = values.has("--rpc-file") ? deps.readRpcFile(values.get("--rpc-file")) : deps.env[RPC_ENV3] || resolveRpcUrl2({ env: {}, origin: facilitator, notice: io.err }).url;
135390
135707
  const lease = (walletFile) => async (task) => {
135391
135708
  let release;
135392
135709
  try {
@@ -135740,7 +136057,7 @@ function rpcFileArgs(command, args, values, deps, io) {
135740
136057
  const index = args.indexOf("--rpc-file");
135741
136058
  return { args: [...args.slice(0, index), "--rpc", url2, ...args.slice(index + 2)], url: url2 };
135742
136059
  }
135743
- const { url } = resolveRpcUrl({ env: {}, origin: facilitatorOriginOf(values.get("--service"), values.get("--facilitator"), values.get("--memo-service")), notice: io.err });
136060
+ const { url } = resolveRpcUrl2({ env: {}, origin: facilitatorOriginOf(values.get("--service"), values.get("--facilitator"), values.get("--memo-service")), notice: io.err });
135744
136061
  return { args: [...args, "--rpc", url], url };
135745
136062
  }
135746
136063
  function runSellerIdentity(values, deps, io) {
@@ -136110,7 +136427,7 @@ async function adoptOneTimeForBalance({ store, values, deps, io }) {
136110
136427
  const pools = [...config ? knownPools(config) : [], ...Object.keys(RETIRED_POOLS)];
136111
136428
  const wallets = openEarlierWallets({ home, store, passphrase: deps.env[PASSPHRASE_ENV2], pools });
136112
136429
  if (!wallets.some((wallet) => wallet.state === "open")) return [];
136113
- const rpcUrl = values.get("--rpc") ?? deps.rpcUrlFromFile ?? deps.env[RPC_ENV3] ?? resolveRpcUrl({ env: {}, origin: new URL(values.get("--service")).origin, notice: io.err }).url;
136430
+ const rpcUrl = values.get("--rpc") ?? deps.rpcUrlFromFile ?? deps.env[RPC_ENV3] ?? resolveRpcUrl2({ env: {}, origin: new URL(values.get("--service")).origin, notice: io.err }).url;
136114
136431
  return await adoptOneTimeDeposits({ wallets, run: {
136115
136432
  store,
136116
136433
  connection: (deps.connectRpc ?? connect2)(rpcUrl, "finalized"),
@@ -136811,7 +137128,7 @@ async function runShinjukuWallet(argv, overrides = {}) {
136811
137128
  applyStoredChannel({ home: setupHome(runDeps.env) });
136812
137129
  } catch {
136813
137130
  }
136814
- const familyRpcUrl = () => rpc2.url ?? values.get("--rpc") ?? runDeps.env[RPC_ENV3] ?? resolveRpcUrl({
137131
+ const familyRpcUrl = () => rpc2.url ?? values.get("--rpc") ?? runDeps.env[RPC_ENV3] ?? resolveRpcUrl2({
136815
137132
  env: {},
136816
137133
  origin: facilitatorOriginOf(values.get("--service"), values.get("--facilitator"), values.get("--memo-service")),
136817
137134
  notice: io.err