shinjuku-shielded 0.3.24 → 0.3.25

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": "5e07378e3416f54223cbb56fc04d28bed3106dfbdef58b62570ad0469f27aade",
5
- "bytes": 11135322,
6
- "sourceCommit": "6427ce901fd81417ad60ea6364079db32785626c",
4
+ "sha256": "1505ebf0afdb67fbbe6050d5065335f3b13e94cefd356fb422ce946f4fba3253",
5
+ "bytes": 11138284,
6
+ "sourceCommit": "d34c7e8fbb9d384d53557aeeac05386f35ce2fb9",
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.24",
3
+ "version": "0.3.25",
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": "5e07378e",
7
- "shinjukuSha256": "5e07378e3416f54223cbb56fc04d28bed3106dfbdef58b62570ad0469f27aade",
6
+ "shinjukuRelease": "1505ebf0",
7
+ "shinjukuSha256": "1505ebf0afdb67fbbe6050d5065335f3b13e94cefd356fb422ce946f4fba3253",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -59758,6 +59758,16 @@ var init_recoveryCache = __esm({
59758
59758
  });
59759
59759
 
59760
59760
  // wallet/src/succession/successorHistories.mjs
59761
+ function withoutProgramHistory(identity) {
59762
+ if (!identity || typeof identity !== "object" || identity.programHistory === void 0) return void 0;
59763
+ const { programHistory: _history, ...rest } = identity;
59764
+ return rest;
59765
+ }
59766
+ function errorChainText(error) {
59767
+ const parts = [];
59768
+ for (let current = error, depth = 0; current && depth < 8; current = current.cause, depth += 1) parts.push(String(current?.message ?? current));
59769
+ return parts.join(" ");
59770
+ }
59761
59771
  async function loadSuccessorHistories({
59762
59772
  history,
59763
59773
  store,
@@ -59775,24 +59785,34 @@ async function loadSuccessorHistories({
59775
59785
  if (typeof target !== "string") return walks;
59776
59786
  if (seen.has(target) || walks.length >= MAX_SUCCESSOR_HOPS) throw new Error("wallet_succession_chain_invalid");
59777
59787
  seen.add(target);
59778
- const walk = await loadHistory({
59788
+ const identity = deploymentIdentityFor?.(target) ?? deploymentIdentity;
59789
+ const read = (given) => loadHistory({
59779
59790
  store,
59780
59791
  connection,
59781
59792
  programId,
59782
59793
  pool: target,
59783
- deploymentIdentity: deploymentIdentityFor?.(target) ?? deploymentIdentity,
59794
+ deploymentIdentity: given,
59784
59795
  cache: walletSuccessorHistoryCache(store, target)
59785
59796
  });
59797
+ let walk;
59798
+ try {
59799
+ walk = await read(identity);
59800
+ } catch (error) {
59801
+ const historyFree = withoutProgramHistory(identity);
59802
+ if (!historyFree || !HISTORY_FREE_NEEDED.test(errorChainText(error))) throw error;
59803
+ walk = await read(historyFree);
59804
+ }
59786
59805
  if (walk?.pool !== target) throw new Error("wallet_succession_chain_invalid");
59787
59806
  walks.push(walk);
59788
59807
  current = walk;
59789
59808
  }
59790
59809
  }
59791
- var MAX_SUCCESSOR_HOPS;
59810
+ var HISTORY_FREE_NEEDED, MAX_SUCCESSOR_HOPS;
59792
59811
  var init_successorHistories = __esm({
59793
59812
  "wallet/src/succession/successorHistories.mjs"() {
59794
59813
  "use strict";
59795
59814
  init_walletHistory();
59815
+ HISTORY_FREE_NEEDED = /p03_program_history_coverage_interval_invalid_pool_initialized_after_last_upgrade_use_history_free_identity/;
59796
59816
  MAX_SUCCESSOR_HOPS = 8;
59797
59817
  }
59798
59818
  });
@@ -90683,14 +90703,27 @@ function floatRecordOf(store, requestId) {
90683
90703
  function floatInnerRequestId(requestId) {
90684
90704
  return requestId.length <= 122 ? `${requestId}:float` : `float:${hash8(requestId)}`;
90685
90705
  }
90686
- function floatRefundsInFlightAtomic(store, refreshedAtMs) {
90706
+ function floatRefundsInFlightAtomic(store, snapshot) {
90707
+ const refreshedAtMs = Date.parse(snapshot?.refreshedAt ?? "");
90708
+ const outputs = Array.isArray(snapshot?.outputs) ? snapshot.outputs : [];
90709
+ const knowsRecipients = outputs.some((output2) => output2?.kind === "note" && output2.diversifier !== void 0);
90710
+ const arrived = (refundTo) => {
90711
+ let address5;
90712
+ try {
90713
+ address5 = decodeShieldedSolanaP03Address(String(refundTo));
90714
+ } catch {
90715
+ return false;
90716
+ }
90717
+ return outputs.some((output2) => output2?.kind === "note" && output2.diversifier === address5.diversifier.toString() && output2.recipientX === address5.recipientX.toString());
90718
+ };
90687
90719
  let total = 0n;
90688
90720
  try {
90689
90721
  for (const name of store.listWal()) {
90690
90722
  if (!name.startsWith(FLOAT_PAYMENT_WAL_PREFIX)) continue;
90691
90723
  const record = store.readWal(name);
90692
90724
  if (record?.schema !== FLOAT_PAYMENT_SCHEMA || record.state !== "refund_pending" || !POSITIVE.test(String(record.paidAtomic))) continue;
90693
- if (Number.isFinite(refreshedAtMs) && refreshedAtMs >= record.refundPendingAtMs + REFUND_SETTLED_AFTER_MS) continue;
90725
+ if (arrived(record.refundTo)) continue;
90726
+ if (!knowsRecipients && Number.isFinite(refreshedAtMs) && refreshedAtMs >= record.refundPendingAtMs + REFUND_SETTLED_AFTER_MS) continue;
90694
90727
  total += BigInt(record.paidAtomic);
90695
90728
  }
90696
90729
  } catch {
@@ -118928,7 +118961,10 @@ function snapshotFromRecovery({ state, programId, recovery, history, walStates,
118928
118961
  leafIndex: String(entry?.leafIndex),
118929
118962
  valueAtomic: value === void 0 ? void 0 : String(value),
118930
118963
  spendStatus: entry?.spendStatus,
118931
- digest: digestKey(entry?.digest)
118964
+ digest: digestKey(entry?.digest),
118965
+ // A note's one-time recipient (diversifier d and recipient x): a float refund is matched to its
118966
+ // own refund address by these (floatRefundsInFlightAtomic). Public to this wallet only (sealed cache).
118967
+ ...entry?.kind === "note" && /^[0-9]+$/.test(String(entry.note?.d)) && /^[0-9]+$/.test(String(entry.note?.pDX)) ? { diversifier: String(entry.note.d), recipientX: String(entry.note.pDX) } : {}
118932
118968
  };
118933
118969
  if (!["account", "note"].includes(output2.kind) || !canonicalAtomic(output2.valueAtomic) || !canonicalAtomic(output2.commitment) || !canonicalAtomic(output2.leafIndex) || !SPEND_STATUSES.has(output2.spendStatus) || !/^[0-9a-f]{64}$/.test(output2.digest)) {
118934
118970
  throw new Error("balance_snapshot_output_invalid");
@@ -119050,7 +119086,8 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
119050
119086
  ...entry.record.relay?.approvedOnChain ? { approvalStanding: true } : {}
119051
119087
  }));
119052
119088
  const receipts = records.filter((entry) => entry.type === "payment" && entry.phase === "done");
119053
- const paid = receipts.filter((entry) => !String(entry.record.url ?? "").startsWith(SELF_PAY_ORIGIN2) && !String(entry.record.url ?? "").includes(POOL_MOVE_PATH2));
119089
+ const refunded = new Set(records.filter((entry) => entry.type === "float-payment" && entry.state === "refund_pending").map((entry) => entry.record.innerRequestId).filter((id) => typeof id === "string"));
119090
+ const paid = receipts.filter((entry) => !String(entry.record.url ?? "").startsWith(SELF_PAY_ORIGIN2) && !String(entry.record.url ?? "").includes(POOL_MOVE_PATH2) && !refunded.has(entry.record.requestId));
119054
119091
  const sum2 = (values) => values.reduce((total, value) => total + BigInt(value ?? "0"), 0n);
119055
119092
  const config = state.config ?? {};
119056
119093
  const capOrNone = (value) => String(value) === NO_LIMIT_ATOMIC2 ? "none" : value;
@@ -124966,13 +125003,13 @@ function oneBalance(report, store) {
124966
125003
  const shieldedAtomic = report?.split?.shieldedAtomic ?? null;
124967
125004
  const pocketsAtomic = report?.split?.pocketsAtomic ?? null;
124968
125005
  const moving = movingAtomic(store);
124969
- let refreshedAtMs;
125006
+ let snapshot;
124970
125007
  try {
124971
- refreshedAtMs = Date.parse(readBalanceSnapshot(store).snapshot?.refreshedAt ?? "");
125008
+ snapshot = readBalanceSnapshot(store).snapshot;
124972
125009
  } catch {
124973
- refreshedAtMs = void 0;
125010
+ snapshot = void 0;
124974
125011
  }
124975
- const refunds = floatRefundsInFlightAtomic(store, refreshedAtMs);
125012
+ const refunds = floatRefundsInFlightAtomic(store, snapshot);
124976
125013
  const known = shieldedAtomic !== null && pocketsAtomic !== null;
124977
125014
  const setAside = known ? BigInt(pocketsAtomic) + moving : null;
124978
125015
  const total = known ? BigInt(shieldedAtomic) + setAside + refunds : null;
@@ -125574,6 +125611,13 @@ function createWalletTools({
125574
125611
  next = typeof blocking === "string" && /^[A-Za-z0-9._:-]{1,128}$/.test(blocking) ? `Nothing was paid: an earlier payment (request_id "${blocking}") is not finished, and the wallet pays one at a time. Call x402_pay again with request_id "${blocking}" and that payment's url, method, headers, and body (it finishes it and never pays twice), or close it with wallet_cancel request_id "${blocking}"; then call x402_pay again with request_id "${requestId}"` : `Nothing was paid: an earlier payment is not finished, and the wallet pays one at a time. wallet_balance lists it in detail.inFlight; finish it with x402_pay and its request_id, or close it with wallet_cancel; then call x402_pay again with request_id "${requestId}"`;
125575
125612
  } else if (/accounts_split_merge_first/.test(line)) {
125576
125613
  next = `Nothing was paid: the shielded balance covers this price in total, but it sits in several accounts and one payment draws only one account and at most 3 notes. Tell your owner to join them: shinjuku-wallet merge --wallet <wallet.json> --passphrase-file passphrase.txt --program <program> --proof-tools proof-tools.json --profile public-profile.json --facilitator <facilitator origin> (--dry-run shows the plan; shinjuku-wallet help merge). After it finished and the next pool anchor (up to 5 minutes; about 30 s on a pool with sub-anchors) passed, call x402_pay again with request_id "${requestId}" and the same url, method, headers, and body`;
125614
+ } else if (/^p03_merchant_respend_(insufficient_recovered_value|no_spendable_account)$|^(p03_payer_)?insufficient(_recovered)?_value$/.test(code)) {
125615
+ return refuse9(
125616
+ code,
125617
+ "the shielded balance is less than this payment needs (price + fee); nothing was paid",
125618
+ `Nothing was paid. Tell the user the balance is too low for this price: wallet_balance shows breakdown.shieldedAtomic (a refund still on its way shows as refundsInFlightAtomic and becomes spendable when it arrives). To pay, add USDC with wallet_shield (or wallet_shield_from_elsewhere), wait until wallet_balance counts it, then call x402_pay again with request_id "${requestId}" and the same url, method, headers, and body (it never pays twice)`,
125619
+ { requestId }
125620
+ );
125577
125621
  } else if (code.startsWith("wallet_spend_refused_")) {
125578
125622
  next = "the wallet file's own budget refused it (init --max-payment / --max-cumulative); nothing was paid. Read wallet_balance detail.budget";
125579
125623
  } else if (code.startsWith("wallet_quote_refused_") || code.startsWith("fetch_")) {