shinjuku-shielded 0.3.15 → 0.3.16

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": "6a87b0a15ec5979313adc3e9b7f19e239c2f14af1c46565e958054dbc60e8af7",
5
- "bytes": 11122024,
6
- "sourceCommit": "b5ba33a6fa8251d6c6226defa8fbfd235efe5817",
4
+ "sha256": "b32bffc727e1e7e33fb134a3918638006259033511e1a2aaf455d037da1f70b3",
5
+ "bytes": 11124201,
6
+ "sourceCommit": "4b3c69bd892409a11b931e3a01f254a8b2f4af0f",
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.15",
3
+ "version": "0.3.16",
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": "6a87b0a1",
7
- "shinjukuSha256": "6a87b0a15ec5979313adc3e9b7f19e239c2f14af1c46565e958054dbc60e8af7",
6
+ "shinjukuRelease": "b32bffc7",
7
+ "shinjukuSha256": "b32bffc727e1e7e33fb134a3918638006259033511e1a2aaf455d037da1f70b3",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -118735,6 +118735,8 @@ function snapshotFromRecovery({ state, programId, recovery, history, walStates,
118735
118735
  });
118736
118736
  const slot3 = history?.finalizedPoolSnapshot?.slot;
118737
118737
  if (slot3 === void 0 || !canonicalAtomic(String(slot3))) throw new Error("balance_snapshot_slot_invalid");
118738
+ const anchorIndexes = Array.isArray(history?.finalizedPoolSnapshot?.anchors) ? history.finalizedPoolSnapshot.anchors.map((anchor) => BigInt(anchor.index)) : [];
118739
+ const anchorIndex = anchorIndexes.length ? anchorIndexes.reduce((a, b) => b > a ? b : a).toString() : void 0;
118738
118740
  return {
118739
118741
  schema: BALANCE_SNAPSHOT_SCHEMA,
118740
118742
  pool: state.pool,
@@ -118742,6 +118744,7 @@ function snapshotFromRecovery({ state, programId, recovery, history, walStates,
118742
118744
  refreshedAt: new Date(now).toISOString(),
118743
118745
  slot: String(slot3),
118744
118746
  poolIndex: String(recovery.poolIndex),
118747
+ ...anchorIndex === void 0 ? {} : { anchorIndex },
118745
118748
  fresh: fresh === true,
118746
118749
  walStates: { ...walStates },
118747
118750
  outputs
@@ -119021,7 +119024,8 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
119021
119024
  const reservedAtomic = unknownInputs ? unspentAtomic : sum2([...reserved].filter((commitment) => unspent.has(commitment)).map((commitment) => unspent.get(commitment).valueAtomic));
119022
119025
  detail.reservedAtomic = reservedAtomic.toString();
119023
119026
  const spendable = unspentAtomic - reservedAtomic;
119024
- detail.fundedAccounts = unknownInputs ? 0 : [...unspent.values()].filter((output2) => output2.kind === "account" && BigInt(output2.valueAtomic) > 0n && !reserved.has(output2.commitment)).length;
119027
+ const anchorIndex = canonicalAtomic(snapshot.anchorIndex) ? BigInt(snapshot.anchorIndex) : void 0;
119028
+ detail.fundedAccounts = unknownInputs ? 0 : [...unspent.values()].filter((output2) => output2.kind === "account" && BigInt(output2.valueAtomic) > 0n && !reserved.has(output2.commitment) && (anchorIndex === void 0 || BigInt(output2.leafIndex) < anchorIndex)).length;
119025
119029
  const next = detail.inFlight.length ? "finish or cancel the work in detail.inFlight; the wallet runs one payment at a time" : pendingDeposits.length ? `deposit ${pendingDeposits[0].depositId} is not finished: rerun the same deposit command with --submit --service <facilitator URL>` : pocketsUnscanned ? scanPockets2 : unspentAtomic === 0n && successionMovableAtomic > 0n ? "this pool was succeeded by a new pool: its value moves there (a move per account, the facilitator pays the fee) and counts in this balance until then" : unspentAtomic === 0n ? "nothing to spend: deposit (shinjuku-wallet help deposit), or run balance --refresh if a payment to this wallet settled" : void 0;
119026
119030
  return report("current", spendable + successionMovableAtomic, next);
119027
119031
  }
@@ -124142,6 +124146,7 @@ function writeCache(key, record, ctx) {
124142
124146
  // wallet/src/mcp/tools.mjs
124143
124147
  init_pool_denomination();
124144
124148
  init_serviceUrl();
124149
+ init_anchorClock();
124145
124150
  var shownCaps = (caps) => Object.fromEntries(Object.entries(caps).map(([name, value]) => [name, String(value) === NO_LIMIT_ATOMIC2 ? "none" : value]));
124146
124151
  var RESOURCE_PAYMENT_WAL_PREFIX2 = "resource-payment-";
124147
124152
  var SELF_PAY_ORIGIN3 = "https://self-pay.invalid/";
@@ -124714,6 +124719,8 @@ function localBalance(session) {
124714
124719
  return null;
124715
124720
  }
124716
124721
  }
124722
+ var CAP_BALANCE_WAIT_MS = 35e3;
124723
+ var CAP_BALANCE_RETRY_MS = 5e3;
124717
124724
  var REFRESH_RETRY_MS = 6e4;
124718
124725
  var SWITCH_WAIT_MS = 4e4;
124719
124726
  var ELICIT_TIMEOUT_MS = 5 * 6e4;
@@ -124730,7 +124737,9 @@ function createWalletTools({
124730
124737
  client,
124731
124738
  exportBackup,
124732
124739
  balanceRefreshWaitMs = BALANCE_REFRESH_WAIT_MS,
124733
- elicitTimeoutMs = ELICIT_TIMEOUT_MS
124740
+ elicitTimeoutMs = ELICIT_TIMEOUT_MS,
124741
+ capBalanceWaitMs = CAP_BALANCE_WAIT_MS,
124742
+ capBalanceRetryMs = CAP_BALANCE_RETRY_MS
124734
124743
  }) {
124735
124744
  const outDir = join26(dirname23(session.walletPath), "mcp-out");
124736
124745
  const knownIds = /* @__PURE__ */ new Map();
@@ -125564,6 +125573,19 @@ function createWalletTools({
125564
125573
  const again = (kind, job) => `call ${KINDS[kind].tool} again with request_id "${job.requestId}", amount_atomic "${job.amountAtomic}"${job.to ? `, and to "${job.to}"` : ""}`;
125565
125574
  const unboundNext = (requestId, amountAtomic) => `Nothing was signed. Do not retry this request_id. Tell your owner: the self-payment of this unshield has no recipient bound to it, so this server will not choose one. The owner finishes it with the CLI: shinjuku-wallet unshield (the same --wallet, proof tools, --profile, --program, --facilitator) --unshield-id ${moneyId(requestId)} --amount ${amountAtomic} --to <recipient USDC account> --recipient-owner <recipient owner>`;
125566
125575
  const preApproved = () => [...new Set(unshieldTo)];
125576
+ async function anchorWaitAfterDeposit() {
125577
+ try {
125578
+ const read = await underLease((store) => readBalanceSnapshot(store));
125579
+ const anchorIndex = read.snapshot?.anchorIndex;
125580
+ if (anchorIndex === void 0) return {};
125581
+ const waiting = read.snapshot.outputs.some((output2) => output2.kind === "account" && output2.spendStatus === "unspent" && BigInt(output2.leafIndex) >= BigInt(anchorIndex));
125582
+ if (!waiting) return {};
125583
+ noteAnchorOrigin(session.memoServiceUrl);
125584
+ return { spendableAtMs: Date.now() + nextAnchorWaitMs(Date.now(), 0) };
125585
+ } catch {
125586
+ return {};
125587
+ }
125588
+ }
125567
125589
  function doneNext(kind, job) {
125568
125590
  if (kind === "unshield") {
125569
125591
  return `the USDC was sent to ${job.to}; tell the user (exitTransaction). wallet_balance shows the new shielded balance${job.result?.backupPath ? `. An encrypted backup of the wallet was saved at ${job.result.backupPath}` : ""}`;
@@ -125573,6 +125595,9 @@ function createWalletTools({
125573
125595
  return `the deposit landed. The wallet now reads it from the chain so it becomes spendable: call wallet_balance in ${POLL_SECONDS2} s (x402_pay and wallet_unshield also wait for it by themselves)`;
125574
125596
  }
125575
125597
  if (!post.refreshed) return `the deposit landed; reading it from the chain failed (${post.refreshError}): call wallet_balance in a minute, it retries by itself`;
125598
+ if (post.spendableAtMs !== void 0 && post.spendableAtMs > Date.now()) {
125599
+ return `the deposit landed and is in the shielded balance. It is spendable after the next pool anchor, in about ${Math.ceil((post.spendableAtMs - Date.now()) / 1e3)} s: x402_pay and wallet_unshield wait for it by themselves, so call them now if the user wants to${post.backupPath ? `. An encrypted backup of the wallet was saved at ${post.backupPath}` : ""}`;
125600
+ }
125576
125601
  return `the deposit landed and is in the shielded balance: x402_pay and wallet_unshield can spend it now${post.backupPath ? `. An encrypted backup of the wallet was saved at ${post.backupPath}` : ""}`;
125577
125602
  }
125578
125603
  const ELSEWHERE_PRIVACY = "the chain shows the other wallet paying this new one-time address, and that address depositing this amount into the pool (both public; the facilitator also sees the amount and time). Later payments from the shielded balance do not show which deposit paid them. With few depositors in the pool, an amount few other deposits share, or a payment soon after the deposit, is easier to pair";
@@ -125899,13 +125924,19 @@ function createWalletTools({
125899
125924
  } catch {
125900
125925
  stale = true;
125901
125926
  }
125902
- if (stale) await refreshShared().catch(() => {
125903
- });
125904
- const one = localBalance(session);
125905
- if (one?.balanceAtomic === null || one?.balanceAtomic === void 0) {
125927
+ let one = stale ? null : localBalance(session);
125928
+ const unknown = () => one?.balanceAtomic === null || one?.balanceAtomic === void 0;
125929
+ for (const deadline = Date.now() + capBalanceWaitMs; unknown(); ) {
125930
+ await refreshShared().catch(() => {
125931
+ });
125932
+ one = localBalance(session);
125933
+ if (!unknown() || Date.now() + capBalanceRetryMs > deadline) break;
125934
+ await new Promise((resolve36) => setTimeout(resolve36, capBalanceRetryMs));
125935
+ }
125936
+ if (unknown()) {
125906
125937
  return refuse9(
125907
125938
  "mcp_balance_unknown",
125908
- `the shielded balance could not be read just now (the chain read failed, or another wallet command holds this wallet), so the wallet cannot check the ${usdcText(MAX_SHIELDED_BALANCE_ATOMIC)} USDC balance cap; nothing was moved`,
125939
+ `the shielded balance could not be read just now (a payment from moments ago is not final yet, the chain read failed, or another wallet command holds this wallet), so the wallet cannot check the ${usdcText(MAX_SHIELDED_BALANCE_ATOMIC)} USDC balance cap; nothing was moved`,
125909
125940
  `call ${spec.tool} again with the same request_id and amount_atomic in ${POLL_SECONDS2} s`,
125910
125941
  { requestId: request.requestId }
125911
125942
  );
@@ -126041,7 +126072,10 @@ function createWalletTools({
126041
126072
  }
126042
126073
  function afterShield(job) {
126043
126074
  job.post = { state: "running" };
126044
- job.postPromise = refreshShared().then(() => ({ refreshed: true }), (error) => ({ refreshed: false, refreshError: errorCode2(error, "mcp_refresh_failed") })).then((refresh) => underLease((store) => backupNow(store)).catch((error) => typeof exportBackup === "function" ? { backupPath: null, backupError: errorCode2(error, "wallet_locked") } : {}).then((backup) => {
126075
+ job.postPromise = refreshShared().then(
126076
+ () => anchorWaitAfterDeposit().then((wait2) => ({ refreshed: true, ...wait2 })),
126077
+ (error) => ({ refreshed: false, refreshError: errorCode2(error, "mcp_refresh_failed") })
126078
+ ).then((refresh) => underLease((store) => backupNow(store)).catch((error) => typeof exportBackup === "function" ? { backupPath: null, backupError: errorCode2(error, "wallet_locked") } : {}).then((backup) => {
126045
126079
  job.post = { state: "done", ...refresh, ...backup };
126046
126080
  })).finally(() => {
126047
126081
  if (money.autoPockets === true) autoPockets.onShielded();