shinjuku-shield 0.3.90 → 0.3.91

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/README.md CHANGED
@@ -6,13 +6,13 @@ answers privately, in a terminal chat or for your agent app. Its `mcp`
6
6
  command runs a local MCP server (stdio) for your agent. Your keys never
7
7
  leave your machine.
8
8
 
9
- This is version 0.3.90. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.91. The npm packages `shinjuku-shield` and
10
10
  `shinjuku-shielded` hold the same files; `shinjuku` is the short command.
11
11
 
12
12
  ## Set up
13
13
 
14
14
  ```sh
15
- npx -y --no-audit --no-fund shinjuku-shield@0.3.90 setup
15
+ npx -y --no-audit --no-fund shinjuku-shield@0.3.91 setup
16
16
  ```
17
17
 
18
18
  Setup makes the wallet on this machine, a private passphrase file, and the
@@ -24,7 +24,7 @@ server to that host's config.
24
24
  ## The `shinjuku` command
25
25
 
26
26
  ```sh
27
- npm install -g shinjuku@0.3.90
27
+ npm install -g shinjuku@0.3.91
28
28
  shinjuku help
29
29
  ```
30
30
 
@@ -36,13 +36,13 @@ Every wallet command works after `shinjuku`, for example `shinjuku balance`,
36
36
  Claude Code:
37
37
 
38
38
  ```sh
39
- claude mcp add --scope user shinjuku-shielded -- npx -y --no-audit --no-fund shinjuku-shield@0.3.90 mcp
39
+ claude mcp add --scope user shinjuku-shielded -- npx -y --no-audit --no-fund shinjuku-shield@0.3.91 mcp
40
40
  ```
41
41
 
42
42
  Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`):
43
43
 
44
44
  ```json
45
- { "mcpServers": { "shinjuku-shielded": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.90", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku-shielded": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.91", "mcp"] } } }
46
46
  ```
47
47
 
48
48
  On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
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": "4cda1792a58d926b79cdd34975b53b9177940b1f3e266318695cb32d7f70b295",
5
- "bytes": 12682087,
6
- "sourceCommit": "eb43ca199e7be979f8bb406d387eec337a2ca50b",
4
+ "sha256": "1d7842469a3a56390eae2b339649cb42551a19ef63c2f9b1d5716f875ce11b52",
5
+ "bytes": 12694423,
6
+ "sourceCommit": "49f574ac0d496bfe5fda2fff09ed900810a54eed",
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",
@@ -1297,5 +1297,5 @@
1297
1297
  "version": "1.8.0"
1298
1298
  }
1299
1299
  ],
1300
- "publishedAt": "2026-10-24T00:59:36.270Z"
1300
+ "publishedAt": "2026-10-24T01:44:56.272Z"
1301
1301
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shield",
3
- "version": "0.3.90",
3
+ "version": "0.3.91",
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": "4cda1792",
7
- "shinjukuSha256": "4cda1792a58d926b79cdd34975b53b9177940b1f3e266318695cb32d7f70b295",
6
+ "shinjukuRelease": "1d784246",
7
+ "shinjukuSha256": "1d7842469a3a56390eae2b339649cb42551a19ef63c2f9b1d5716f875ce11b52",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -30309,7 +30309,7 @@ Message: ${transactionMessage2}.
30309
30309
  await this._unsubscribeClientSubscription(clientSubscriptionId, "root change");
30310
30310
  }
30311
30311
  };
30312
- var Keypair16 = class _Keypair {
30312
+ var Keypair17 = class _Keypair {
30313
30313
  /**
30314
30314
  * Create a new keypair instance.
30315
30315
  * Generate random keypair if no {@link Ed25519Keypair} is provided.
@@ -30853,7 +30853,7 @@ Message: ${transactionMessage2}.
30853
30853
  } = params;
30854
30854
  assert(privateKey.length === PRIVATE_KEY_BYTES$1, `Private key must be ${PRIVATE_KEY_BYTES$1} bytes but received ${privateKey.length} bytes`);
30855
30855
  try {
30856
- const keypair = Keypair16.fromSecretKey(privateKey);
30856
+ const keypair = Keypair17.fromSecretKey(privateKey);
30857
30857
  const publicKey4 = keypair.publicKey.toBytes();
30858
30858
  const signature3 = sign3(message, keypair.secretKey);
30859
30859
  return this.createInstructionWithPublicKey({
@@ -32293,7 +32293,7 @@ Message: ${transactionMessage2}.
32293
32293
  exports.Enum = Enum;
32294
32294
  exports.EpochSchedule = EpochSchedule;
32295
32295
  exports.FeeCalculatorLayout = FeeCalculatorLayout;
32296
- exports.Keypair = Keypair16;
32296
+ exports.Keypair = Keypair17;
32297
32297
  exports.LAMPORTS_PER_SOL = LAMPORTS_PER_SOL;
32298
32298
  exports.LOOKUP_TABLE_INSTRUCTION_LAYOUTS = LOOKUP_TABLE_INSTRUCTION_LAYOUTS;
32299
32299
  exports.Loader = Loader;
@@ -104690,6 +104690,9 @@ function checkConfig(value) {
104690
104690
  }
104691
104691
  if (BigInt(caps.maxSession) < BigInt(caps.maxPayment)) throw invalid3("caps.maxSession is below caps.maxPayment");
104692
104692
  if (value.tor !== void 0 && typeof value.tor !== "boolean") throw invalid3("tor is not true or false");
104693
+ if (value.unshieldTo !== void 0 && (!Array.isArray(value.unshieldTo) || value.unshieldTo.length < 1 || value.unshieldTo.length > MAX_OWN_ADDRESSES || !value.unshieldTo.every(isPubkey))) {
104694
+ throw invalid3(`unshieldTo is not a list of 1 to ${MAX_OWN_ADDRESSES} base58 Solana addresses`);
104695
+ }
104693
104696
  if (value.torTunnel !== void 0) {
104694
104697
  let tunnel;
104695
104698
  try {
@@ -104714,9 +104717,17 @@ function checkConfig(value) {
104714
104717
  return Object.freeze({
104715
104718
  ...value,
104716
104719
  caps: Object.freeze({ ...caps }),
104720
+ ...value.unshieldTo ? { unshieldTo: Object.freeze([...value.unshieldTo]) } : {},
104717
104721
  ...value.previousPools ? { previousPools: Object.freeze(value.previousPools.map((entry) => Object.freeze({ ...entry }))) } : {}
104718
104722
  });
104719
104723
  }
104724
+ function ownAddressesNow(home) {
104725
+ try {
104726
+ return [...readSetupConfig(home)?.unshieldTo ?? []];
104727
+ } catch {
104728
+ return [];
104729
+ }
104730
+ }
104720
104731
  function readSetupConfig(home) {
104721
104732
  const path = configPath(home);
104722
104733
  if (!existsSync(path)) return null;
@@ -104760,7 +104771,8 @@ function mcpFlagsFromConfig(home, config) {
104760
104771
  ["--max-payment", config.caps.maxPayment],
104761
104772
  ["--max-session", config.caps.maxSession],
104762
104773
  ["--max-shield", config.caps.maxShield],
104763
- ["--max-unshield", config.caps.maxUnshield]
104774
+ ["--max-unshield", config.caps.maxUnshield],
104775
+ ["--unshield-to", config.unshieldTo ? [...config.unshieldTo] : void 0]
104764
104776
  ].filter(([, value]) => value !== void 0);
104765
104777
  }
104766
104778
  function npxPackageWord(npmVersion) {
@@ -104931,7 +104943,7 @@ function addToJsonHost(file, entry, { now = /* @__PURE__ */ new Date(), force =
104931
104943
  }
104932
104944
  return { file, changed: true, ...backup ? { backup } : {} };
104933
104945
  }
104934
- var import_web3118, CONFIG_SCHEMA, CONFIG_FILE, NPM_PACKAGE_NAMES, NPX_PACKAGE, COMMAND_PACKAGE, NPX_INSTALL_FLAGS, MCP_SERVER_NAME, P03_BINDING2, PRODUCTION, RETIRED_POOLS, NO_LIMIT_ATOMIC2, DEFAULT_CAPS, OLD_DEFAULT_SETUP_CAPS, OLD_DEFAULT_WALLET_BUDGETS, capText, POSITIVE_ATOMIC2, configPath, homeWalletPath, invalid3, isPubkey, HOST_TARGETS, PINNED_PACKAGE, NPM_VERSION, knownNpmVersion, WINDOWS_SHELL_CODE, isPlainObject3, sameJson2;
104946
+ var import_web3118, CONFIG_SCHEMA, CONFIG_FILE, NPM_PACKAGE_NAMES, NPX_PACKAGE, COMMAND_PACKAGE, NPX_INSTALL_FLAGS, MCP_SERVER_NAME, P03_BINDING2, PRODUCTION, RETIRED_POOLS, NO_LIMIT_ATOMIC2, DEFAULT_CAPS, OLD_DEFAULT_SETUP_CAPS, OLD_DEFAULT_WALLET_BUDGETS, capText, POSITIVE_ATOMIC2, configPath, homeWalletPath, invalid3, isPubkey, MAX_OWN_ADDRESSES, isOwnAddressText, HOST_TARGETS, PINNED_PACKAGE, NPM_VERSION, knownNpmVersion, WINDOWS_SHELL_CODE, isPlainObject3, sameJson2;
104935
104947
  var init_config2 = __esm({
104936
104948
  "wallet/src/setup/config.mjs"() {
104937
104949
  "use strict";
@@ -104981,6 +104993,8 @@ var init_config2 = __esm({
104981
104993
  return false;
104982
104994
  }
104983
104995
  };
104996
+ MAX_OWN_ADDRESSES = 8;
104997
+ isOwnAddressText = (value) => isPubkey(value);
104984
104998
  HOST_TARGETS = Object.freeze(["claude-code", "claude-desktop", "cursor"]);
104985
104999
  PINNED_PACKAGE = /^shinjuku-shield(?:ed)?@(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/;
104986
105000
  NPM_VERSION = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/;
@@ -110043,9 +110057,12 @@ function refusalSay(a, tool, again, npmVersion) {
110043
110057
  return /\bover Tor\b/.test(detail) ? "Nothing was paid. The seller gave no answer over Tor; some block Tor exits. Try later, or ask the user whether to pay without --tor." : "Nothing was paid. The seller did not answer.";
110044
110058
  }
110045
110059
  const unshown = /without showing it|could not show the confirmation/.test(detail);
110046
- if (code2 === "mcp_unshield_declined") {
110047
- return unshown ? "Not sent. This app answered the send question before you could see it. Use an app that shows the question." : "Not sent. The send was not confirmed.";
110060
+ if (code2 === "mcp_unshield_declined" && unshown || code2 === "mcp_unshield_to_not_allowed") {
110061
+ const head = "Not sent: this app could not show you the address to confirm.";
110062
+ const own = `${head} If it is your wallet, run the setup command below in a terminal.`;
110063
+ return str(a.ownAddressCommand) && words(own) <= SAY_MAX_WORDS ? own : head;
110048
110064
  }
110065
+ if (code2 === "mcp_unshield_declined") return "Not sent. The send was not confirmed.";
110049
110066
  if (code2 === "mcp_setup_declined" && unshown) {
110050
110067
  return `Not set up. This app cannot show the setup question. Run ${oneTimeCommandLine("setup", npmVersion)} in a terminal, then restart this app.`;
110051
110068
  }
@@ -110248,15 +110265,16 @@ function balanceLine(a, room = SAY_MAX_WORDS) {
110248
110265
  if (a.status === "reading") return "The first balance read of this wallet is not done yet. Ask again in 30 seconds.";
110249
110266
  if (a.refreshing === true) {
110250
110267
  const last = a.lastRead && usdcText(a.lastRead.privateAtomic) && Number.isSafeInteger(a.lastRead.ageMinutes) && a.lastRead.ageMinutes > 0 ? ` The last full read, ${ageWords(a.lastRead.ageMinutes)} ago, showed ${usdcText(a.lastRead.privateAtomic)} USDC private.` : "";
110251
- const lines = Number(a.refreshingSeconds) >= 60 ? [`Still reading the chain; it takes longer than usual.${last} Ask again in a minute.`, "The wallet is still reading the chain; this takes longer than usual. Ask again in a minute."] : [`Still reading the chain.${last} Ask again in about 30 seconds.`, "The wallet is still reading the chain. Ask again in about 30 seconds."];
110252
- return last ? lines.find((text3) => words(text3) <= SAY_MAX_WORDS) ?? lines[1] : lines[1];
110268
+ const known = a.lastRead && usdcText(a.lastRead.privateAtomic) && Number.isSafeInteger(a.lastRead.ageMinutes) ? `Your last known balance is ${usdcText(a.lastRead.privateAtomic)} USDC private (${a.lastRead.ageMinutes > 0 ? `${ageWords(a.lastRead.ageMinutes)} ago` : "under a minute ago"}); the wallet is checking it now.` : "";
110269
+ const lines = Number(a.refreshingSeconds) >= 60 ? [known && `${known} Ask again in a minute.`, last && `Still reading the chain; it takes longer than usual.${last} Ask again in a minute.`, "The wallet is still reading the chain; this takes longer than usual. Ask again in a minute."] : [known && `${known} Ask again in about 30 seconds.`, last && `Still reading the chain.${last} Ask again in about 30 seconds.`, "The wallet is still reading the chain. Ask again in about 30 seconds."];
110270
+ return lines.find((text3) => text3 && words(text3) <= SAY_MAX_WORDS) ?? lines.at(-1);
110253
110271
  }
110254
110272
  const why = refreshErrorWords(str(a.refreshError), a.lockedBy);
110255
110273
  return why ? `Your private balance is not known yet: ${why}. Ask again in a minute.` : "Your private balance is not known yet. Ask again in a minute.";
110256
110274
  }
110257
110275
  const refund = usdcText(a.breakdown?.refundsInFlightAtomic);
110258
110276
  const have = (rest, shorter = rest) => {
110259
- const named = positive(refund) ? [`(${refund} of it is a refund on its way)`, `(${refund} refund on its way)`, `(${refund} refunding)`] : [];
110277
+ const named = !positive(refund) ? [] : a.refreshReason === "refund_check" ? [`(${refund} of it is a refund; the wallet is checking that it arrived)`, `(${refund} of it is a refund being checked)`, `(${refund} refund being checked)`] : [`(${refund} of it is a refund on its way)`, `(${refund} refund on its way)`, `(${refund} refunding)`];
110260
110278
  const line = [rest, shorter].flatMap((tail) => named.map((part) => `You have ${balance} USDC private ${part}${tail}`)).find((text3) => words(text3) <= SAY_MAX_WORDS);
110261
110279
  return line ?? `You have ${balance} USDC private${rest}`;
110262
110280
  };
@@ -125682,6 +125700,14 @@ function oneBalance(report, store, walked) {
125682
125700
  }
125683
125701
  };
125684
125702
  }
125703
+ function refundFromOldWalk(store, report) {
125704
+ try {
125705
+ if ((report?.detail?.lastRefresh?.ageSeconds ?? 0) * 1e3 < INBOUND_REFRESH_AGE_MS) return false;
125706
+ return floatRefundsInFlightAtomic(store, readBalanceSnapshot(store).snapshot) > 0n;
125707
+ } catch {
125708
+ return false;
125709
+ }
125710
+ }
125685
125711
  function localBalance(session) {
125686
125712
  try {
125687
125713
  return localBalanceOf(session.openStore());
@@ -127565,7 +127591,7 @@ ${canonical5}`;
127565
127591
  const balance = {
127566
127592
  name: "wallet_balance",
127567
127593
  title: "Balance",
127568
- description: `Use this when the user asks "what is my balance" or "how much USDC do I have": it answers ONE balance (balanceAtomic, what x402_pay can spend) and the public (unshielded) USDC. Pays nothing and signs nothing. When the user asks to see the wallet ("show my wallet"), print \`card\` as it is: at most 8 lines with the private balance, the public USDC, Tor on or off, the last 3 payments (time UTC, seller host, USDC), and example requests. balanceParts (only above 1): a send of all of it first gets the balance ready to send (next says the minutes; never say parts or join to the user). balanceAtomic is one number; breakdown names its parts (breakdown.shieldedAtomic is what wallet_unshield can send; privateSendPendingAtomic is the part of a running private send (wallet_unshield with parts or max_delay_minutes) not sent yet, already taken off shieldedAtomic; refundsInFlightAtomic is a Shinjuku refund on its way back; setAsideAtomic (only when not 0) is value an older version of this wallet set aside to pay sellers; during a pool move, pools lists each pool's part and movingAtomic is value on its way from the old pool to the new one; the move field shows the move, poolSwitches each switch). UNSHIELDED (unshieldedAtomic) is the plain public USDC on the wallet's own Solana key (ownAddress), the USDC wallet_shield makes private; unshieldedSolLamports is that key's SOL, information only: wallet_shield needs none (the facilitator's relayer pays every fee and rent). When the local balance is stale (a new deposit, a received payment), it reads the chain by itself first; when that takes longer than about 20 s it answers the last balance with refreshing: true and finishes in the background (call wallet_balance again in 30 s). A balance with refreshing: true and refreshReason is exact (payment_settling: a payment settles and the number counts it; account_join: the wallet gets its own balance ready to send and the number counts all of it; pool_move: value from the old pool arrives and the number counts it; first_check: a new wallet's first history check, the number is its own landed deposits). status reading (balanceAtomic null): the first balance read of a wallet that may hold money is not done; call wallet_balance again in 30 s. If the public read fails, unshieldedAtomic is null with unshieldedReason; the rest still answers. ownKeyTokens (only when there are any): the other tokens on ownAddress (not its USDC), each { mint, amountAtomic, decimals, amount }, at most 10 (ownKeyTokensMore counts the rest); a token is its mint, never a name or symbol; wallet_transfer_token with that mint as token_mint sells one and sends the USDC privately. earlierDeposits (only when there are any): USDC found at a one-time deposit address of an earlier pool, each with a notice line to tell the user; the wallet moves it into the current pool by itself. addFunds (only when there are any): each wallet_add_funds address still open, with its state and the USDC known to wait there (heldAtomic; null with heldNote when the address was not checked yet). startsAtNextRequest true (old name): its deposit is not sent yet; once the wallet has seen USDC there, it sends it by itself in about startsInMinutes + 1 minutes, in every host, with no ask. landingAtomic (only when not 0): USDC you added whose deposit landed on chain and that balanceAtomic does not count yet; this call finishes it and waits about 20 s for it, past that it is being counted: call wallet_balance again as next says (at once in the same turn when this call waited its 20 s, else in 30 s). session shows the spending limits (null or "none": no limit), what is left, where the limits come from, and how to change them (session.summary). verbose: true adds \`internal\` (accounts, notes, and other engineering numbers); the default answer leaves them out. To add money, use wallet_add_funds. Amounts are atomic units (USDC: 1000000 = 1 USDC); say and usdc give them in USDC.`,
127594
+ description: `Use this when the user asks "what is my balance" or "how much USDC do I have": it answers ONE balance (balanceAtomic, what x402_pay can spend) and the public (unshielded) USDC. Pays nothing and signs nothing. When the user asks to see the wallet ("show my wallet"), print \`card\` as it is: at most 8 lines with the private balance, the public USDC, Tor on or off, the last 3 payments (time UTC, seller host, USDC), and example requests. balanceParts (only above 1): a send of all of it first gets the balance ready to send (next says the minutes; never say parts or join to the user). balanceAtomic is one number; breakdown names its parts (breakdown.shieldedAtomic is what wallet_unshield can send; privateSendPendingAtomic is the part of a running private send (wallet_unshield with parts or max_delay_minutes) not sent yet, already taken off shieldedAtomic; refundsInFlightAtomic is a Shinjuku refund on its way back; setAsideAtomic (only when not 0) is value an older version of this wallet set aside to pay sellers; during a pool move, pools lists each pool's part and movingAtomic is value on its way from the old pool to the new one; the move field shows the move, poolSwitches each switch). UNSHIELDED (unshieldedAtomic) is the plain public USDC on the wallet's own Solana key (ownAddress), the USDC wallet_shield makes private; unshieldedSolLamports is that key's SOL, information only: wallet_shield needs none (the facilitator's relayer pays every fee and rent). When the local balance is stale (a new deposit, a received payment), it reads the chain by itself first; when that takes longer than about 20 s it answers the last balance with refreshing: true and finishes in the background (call wallet_balance again in 30 s). A balance with refreshing: true and refreshReason is exact (payment_settling: a payment settles and the number counts it; account_join: the wallet gets its own balance ready to send and the number counts all of it; pool_move: value from the old pool arrives and the number counts it; refund_check: a refund counted from an old read, which the wallet checks now (never tell the user it is still on its way); first_check: a new wallet's first history check, the number is its own landed deposits). status reading (balanceAtomic null): the first balance read of a wallet that may hold money is not done; call wallet_balance again in 30 s. If the public read fails, unshieldedAtomic is null with unshieldedReason; the rest still answers. ownKeyTokens (only when there are any): the other tokens on ownAddress (not its USDC), each { mint, amountAtomic, decimals, amount }, at most 10 (ownKeyTokensMore counts the rest); a token is its mint, never a name or symbol; wallet_transfer_token with that mint as token_mint sells one and sends the USDC privately. earlierDeposits (only when there are any): USDC found at a one-time deposit address of an earlier pool, each with a notice line to tell the user; the wallet moves it into the current pool by itself. addFunds (only when there are any): each wallet_add_funds address still open, with its state and the USDC known to wait there (heldAtomic; null with heldNote when the address was not checked yet). startsAtNextRequest true (old name): its deposit is not sent yet; once the wallet has seen USDC there, it sends it by itself in about startsInMinutes + 1 minutes, in every host, with no ask. landingAtomic (only when not 0): USDC you added whose deposit landed on chain and that balanceAtomic does not count yet; this call finishes it and waits about 20 s for it, past that it is being counted: call wallet_balance again as next says (at once in the same turn when this call waited its 20 s, else in 30 s). session shows the spending limits (null or "none": no limit), what is left, where the limits come from, and how to change them (session.summary, only when a limit is set; with none, say nothing about limits, and never tell the user the session counters (sessionSpentAtomic, sessionMovedAtomic): they are limit bookkeeping and may count earlier conversations). verbose: true adds \`internal\` (accounts, notes, and other engineering numbers); the default answer leaves them out. To add money, use wallet_add_funds. Amounts are atomic units (USDC: 1000000 = 1 USDC); say and usdc give them in USDC.`,
127569
127595
  inputSchema: { type: "object", properties: {
127570
127596
  verbose: { type: "boolean", description: "Optional. true adds `internal`: the engineering numbers (funded accounts, notes, payments without waiting, a 0 set-aside value). Default false." }
127571
127597
  }, additionalProperties: false },
@@ -127640,6 +127666,17 @@ ${canonical5}`;
127640
127666
  });
127641
127667
  const cause = staleCauseOf(report);
127642
127668
  refresh = { refreshing: true, ...cause === void 0 ? {} : { refreshReason: cause } };
127669
+ } else if (report.split?.shieldedAtomic != null && !recentlyFailed && !finalityWait && refundFromOldWalk(store, report)) {
127670
+ const outcome = await Promise.race([refreshShared().then(() => "done", () => "failed"), sleep2(balanceRefreshWaitMs).then(() => "waiting")]);
127671
+ if (outcome === "done") {
127672
+ try {
127673
+ store = session.openStore();
127674
+ } catch {
127675
+ }
127676
+ report = computeBalance({ store, snapshotRead: readBalanceSnapshot(store), source: "refresh" });
127677
+ walkedOne = void 0;
127678
+ refresh = { refreshed: true };
127679
+ } else refresh = { refreshing: true, refreshReason: "refund_check" };
127643
127680
  } else if (report.split?.shieldedAtomic != null && !recentlyFailed && !finalityWait && !refreshRun && (report.detail?.lastRefresh?.ageSeconds ?? 0) * 1e3 >= INBOUND_REFRESH_AGE_MS) {
127644
127681
  refreshShared({ background: true }).catch(() => {
127645
127682
  });
@@ -127977,7 +128014,8 @@ ${canonical5}`;
127977
128014
  const { setAsideAtomic, ...breakdownRest } = one.breakdown;
127978
128015
  const breakdown = setAsideAtomic === "0" ? breakdownRest : one.breakdown;
127979
128016
  if (setAsideAtomic === "0") internal.setAsideAtomic = "0";
127980
- const capWords = (atomic4) => atomic4 === void 0 || atomic4 === null || String(atomic4) === NO_LIMIT_ATOMIC2 ? "no limit" : `${usdcText2(atomic4)} USDC`;
128017
+ const noLimit = (atomic4) => atomic4 === void 0 || atomic4 === null || String(atomic4) === NO_LIMIT_ATOMIC2;
128018
+ const capWords = (atomic4) => noLimit(atomic4) ? "no limit" : `${usdcText2(atomic4)} USDC`;
127981
128019
  let countStore = store;
127982
128020
  try {
127983
128021
  countStore = session.openStore();
@@ -127988,7 +128026,7 @@ ${canonical5}`;
127988
128026
  } catch {
127989
128027
  }
127990
128028
  const sessionLeft = sessionLeftShown(policy, countStore);
127991
- const sessionSummary = `Spending limits: ${capWords(policy.limits.maxPaymentAtomic)} per payment and ${capWords(policy.limits.maxSessionAtomic)} in any 24 hours, for this wallet (every MCP process counts; a pool upgrade starts a new 24-hour count)${sessionLeft === null ? "" : ` (${usdcText2(sessionLeft)} USDC left now)`}; ${usdcText2(policy.sessionSpentAtomic) ?? "0"} USDC paid in the last 24 hours. --max-payment comes from ${capPlaceWords("--max-payment")}, and --max-session from ${capPlaceWords("--max-session")}. To change one from the host's entry, edit that flag (in USDC, like 0.05) in the host's entry for shinjuku-shielded; to change one from setup, run ${terminalCommand("setup --yes --max-payment <usdc> --max-session <usdc>")} in a terminal; then restart the host. The wallet file also holds its own limits (--max-payment, and --max-cumulative for all payments together): ${terminalCommand("limits")} shows and changes them`;
128029
+ const sessionSummary = noLimit(policy.limits.maxPaymentAtomic) && noLimit(policy.limits.maxSessionAtomic) ? void 0 : `Spending limits: ${capWords(policy.limits.maxPaymentAtomic)} per payment and ${capWords(policy.limits.maxSessionAtomic)} in any 24 hours, for this wallet (every MCP process counts; a pool upgrade starts a new 24-hour count)${sessionLeft === null ? "" : ` (${usdcText2(sessionLeft)} USDC left now)`}; ${usdcText2(policy.sessionSpentAtomic) ?? "0"} USDC paid in the last 24 hours. --max-payment comes from ${capPlaceWords("--max-payment")}, and --max-session from ${capPlaceWords("--max-session")}. To change one from the host's entry, edit that flag (in USDC, like 0.05) in the host's entry for shinjuku-shielded; to change one from setup, run ${terminalCommand("setup --yes --max-payment <usdc> --max-session <usdc>")} in a terminal; then restart the host. The wallet file also holds its own limits (--max-payment, and --max-cumulative for all payments together): ${terminalCommand("limits")} shows and changes them`;
127992
128030
  return json3({
127993
128031
  ok: true,
127994
128032
  ...reportPublic,
@@ -128016,7 +128054,7 @@ ${canonical5}`;
128016
128054
  session: {
128017
128055
  ...shownCaps({ ...policy.limits, sessionLeftAtomic: sessionLeft }),
128018
128056
  sessionSpentAtomic: policy.sessionSpentAtomic,
128019
- summary: sessionSummary,
128057
+ ...sessionSummary ? { summary: sessionSummary } : {},
128020
128058
  shield: shieldEnabled ? { maxShieldAtomic: money.maxShieldAtomic ?? null, ...movedFields("shield", capOf2(money.maxShieldAtomic)) } : null,
128021
128059
  unshield: unshieldEnabled ? {
128022
128060
  maxUnshieldAtomic: money.maxUnshieldAtomic ?? null,
@@ -128903,7 +128941,13 @@ ${canonical5}`;
128903
128941
  return Boolean(plan) && plan.parts.some((part) => selfPayMayHaveSigned(store, part.unshieldId));
128904
128942
  }
128905
128943
  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 unshield (the same --wallet, proof tools, --profile, --program, --facilitator) --unshield-id ${moneyId(requestId)} --amount ${usdcFlagText(amountAtomic)} --to <recipient USDC account> --recipient-owner <recipient owner>`;
128906
- const preApproved = () => [...new Set(unshieldTo)];
128944
+ const preApproved = () => [.../* @__PURE__ */ new Set([...unshieldTo, ...typeof money.unshieldToNow === "function" ? money.unshieldToNow() : []])];
128945
+ const ownAddressCommand = () => {
128946
+ const line = terminalCommand("setup");
128947
+ const home = process.env.SHIELDED_WALLET_HOME;
128948
+ return home ? `${line} (with SHIELDED_WALLET_HOME set to ${home}, as in this app's entry)` : line;
128949
+ };
128950
+ const ownAddressNext = () => `Nothing was sent. Do not retry with another address, and do not run setup yourself. Tell the user: this app could not show you the address to confirm, so nothing was sent. If it is your own wallet, open a terminal and run ${ownAddressCommand()}; it asks for your wallet address (type or paste it there), then ask again here`;
128907
128951
  async function anchorWaitAfterDeposit() {
128908
128952
  try {
128909
128953
  const read = await underLease((store) => readBalanceSnapshot(store));
@@ -128917,13 +128961,14 @@ ${canonical5}`;
128917
128961
  return {};
128918
128962
  }
128919
128963
  }
128964
+ const BACKUP_SAVED_NEXT = ". Tell the user a backup of the wallet was saved (say only that; its folder is in backupPath if the user asks)";
128920
128965
  function doneNext(kind, job) {
128921
128966
  if (kind === "unshield" && job.result?.privateSend) {
128922
- return sendDoneNext(job.to, job.result.privateSend) + `${job.result?.backupPath ? `. An encrypted backup of the wallet was saved at ${job.result.backupPath}` : ""}`;
128967
+ return sendDoneNext(job.to, job.result.privateSend) + `${job.result?.backupPath ? BACKUP_SAVED_NEXT : ""}`;
128923
128968
  }
128924
128969
  if (kind === "unshield") {
128925
128970
  const tx = job.result?.exitTransaction;
128926
- return `Sent ${usdcText2(job.amountAtomic)} USDC to ${job.to}${typeof tx === "string" ? ` (transaction ${tx})` : ""}. Tell the user; nothing to do: the private balance shows it in a few seconds (wallet_balance)${job.result?.backupPath ? `. An encrypted backup of the wallet was saved at ${job.result.backupPath}` : ""}`;
128971
+ return `Sent ${usdcText2(job.amountAtomic)} USDC to ${job.to}${typeof tx === "string" ? ` (transaction ${tx})` : ""}. Tell the user; nothing to do: the private balance shows it in a few seconds (wallet_balance)${job.result?.backupPath ? BACKUP_SAVED_NEXT : ""}`;
128927
128972
  }
128928
128973
  const post = job.post;
128929
128974
  if (!post || post.state === "running") {
@@ -128931,9 +128976,9 @@ ${canonical5}`;
128931
128976
  }
128932
128977
  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`;
128933
128978
  if (post.spendableAtMs !== void 0 && post.spendableAtMs > Date.now()) {
128934
- return `the deposit landed and is in the shielded balance. It is spendable 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}` : ""}`;
128979
+ return `the deposit landed and is in the shielded balance. It is spendable 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 ? BACKUP_SAVED_NEXT : ""}`;
128935
128980
  }
128936
- 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}` : ""}`;
128981
+ return `the deposit landed and is in the shielded balance: x402_pay and wallet_unshield can spend it now${post.backupPath ? BACKUP_SAVED_NEXT : ""}`;
128937
128982
  }
128938
128983
  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";
128939
128984
  const reclaimHow = (requestId) => `only the owner can take it back, with the CLI: shinjuku deposit-reclaim --deposit-id ${elsewhereId(requestId)} --to <a USDC token account> --fee-payer-key-file <a key with a little SOL> (with --pool, --program, --rpc); the chain then links that key and --to to the address`;
@@ -129068,6 +129113,7 @@ ${canonical5}`;
129068
129113
  ...refused ? { stage: "refused" } : { stage: "failed", failedStage: job.failedStage },
129069
129114
  ...job.error.largestReceivedAtomic ? { largestReceivedAtomic: job.error.largestReceivedAtomic } : {},
129070
129115
  ...job.error.libraryCode ? { libraryCode: job.error.libraryCode } : {},
129116
+ ...job.error.ownAddressCommand ? { ownAddressCommand: job.error.ownAddressCommand } : {},
129071
129117
  ...job.error.heldAll ? { heldAll: true } : {},
129072
129118
  ...job.error.balanceAtomic === "0" ? {
129073
129119
  balanceAtomic: "0",
@@ -130075,8 +130121,8 @@ ${canonical5}`;
130075
130121
  "mcp_unshield_to_not_allowed",
130076
130122
  "this address is not pre-approved, and this MCP client cannot ask the user to confirm a send (it did not declare MCP elicitation); nothing was sent",
130077
130123
  // No launch-flag how-to here (fault 8b; mcp5 2026-10-21): an agent or a page it read could use it to have the user allow its own address.
130078
- `Nothing was sent. Do not retry with another address. Tell the user: use an MCP app that shows the send question (it then asks the user to confirm each new address); the owner's README says how to pre-approve an address. Addresses allowed now: ${preApproved().join(", ") || "none"}`,
130079
- { requestId: request.requestId }
130124
+ `${ownAddressNext()}. Addresses allowed now: ${preApproved().join(", ") || "none"}`,
130125
+ { requestId: request.requestId, ownAddressCommand: ownAddressCommand() }
130080
130126
  );
130081
130127
  }
130082
130128
  if (kind === "unshield" && !record) {
@@ -130236,7 +130282,8 @@ ${canonical5}`;
130236
130282
  job.error = {
130237
130283
  code: "mcp_unshield_declined",
130238
130284
  detail: `${confirmed.why}; nothing was signed or sent`,
130239
- next: confirmed.unshown ? "Nothing was sent. This MCP client answered the send question at once, before the user could see it, so asking again here fails the same way. Tell the user it was not sent, and that the send works in an MCP app that shows the question to the user" : "Nothing was sent. Tell the user it was not sent. Send it only if the user asks again: then call wallet_unshield again (it asks the user again)"
130285
+ ...confirmed.unshown ? { ownAddressCommand: ownAddressCommand() } : {},
130286
+ next: confirmed.unshown ? `${ownAddressNext()}. Asking again in this app without it fails the same way` : "Nothing was sent. Tell the user it was not sent. Send it only if the user asks again: then call wallet_unshield again (it asks the user again)"
130240
130287
  };
130241
130288
  uncount(kind, key);
130242
130289
  log(`MCP_UNSHIELD_DECLINED requestId=${job.requestId}${confirmed.unshown ? " unshown=true" : ""}`);
@@ -132119,6 +132166,7 @@ ${canonical5}`;
132119
132166
  const usdcMin = usdcText2(BigInt(quote.minOutAtomic));
132120
132167
  const amount = formatAtomic(BigInt(request.amountAtomic), decimals);
132121
132168
  const message = `Sell ${amount} of token ${request.tokenMint} for about ${usdcOut} USDC (at least ${usdcMin}; price impact ${(Number(quote.priceImpactPct) * 100).toFixed(2)} %), shield it, and send it to ${request.to} as ${request.parts} parts, each after a random wait of up to ${request.maxDelayMinutes} min? The address receives USDC, not the token.`;
132169
+ const askedAt = Date.now();
132122
132170
  try {
132123
132171
  const answer = await client.elicit({ message, requestedSchema: {
132124
132172
  type: "object",
@@ -132126,8 +132174,10 @@ ${canonical5}`;
132126
132174
  required: ["confirm"]
132127
132175
  } }, elicitTimeoutMs);
132128
132176
  if (answer?.action === "accept" && answer?.content?.confirm !== false) return { ok: true };
132177
+ if (elicitUnshown(askedAt, answer)) return { ok: false, unshown: true, why: `this MCP client answered the confirmation in ${Date.now() - askedAt} ms without showing it to the user` };
132129
132178
  return { ok: false, why: answer?.action === "accept" ? "the user did not tick Sell and send" : answer?.action === "cancel" ? "the user closed the confirmation" : "the user declined" };
132130
132179
  } catch (error) {
132180
+ if (error?.timedOut !== true && elicitUnshown(askedAt)) return { ok: false, unshown: true, why: "this MCP client could not show the confirmation to the user" };
132131
132181
  return { ok: false, why: error?.timedOut === true ? `no answer in ${Math.round(elicitTimeoutMs / 1e3)} s` : "the MCP client did not answer the confirmation" };
132132
132182
  }
132133
132183
  }
@@ -132188,8 +132238,8 @@ ${canonical5}`;
132188
132238
  return refuse12(
132189
132239
  "mcp_unshield_to_not_allowed",
132190
132240
  "this address is not pre-approved, and this MCP client cannot ask the user to confirm (it did not declare MCP elicitation); nothing was signed",
132191
- "Nothing was signed. Tell the user: use an MCP app that shows the send question; the owner's README says how to pre-approve an address",
132192
- { requestId: id }
132241
+ ownAddressNext(),
132242
+ { requestId: id, ownAddressCommand: ownAddressCommand() }
132193
132243
  );
132194
132244
  }
132195
132245
  let blocker;
@@ -132339,8 +132389,8 @@ ${canonical5}`;
132339
132389
  return refuse12(
132340
132390
  "mcp_unshield_declined",
132341
132391
  `${confirmed.why}; nothing was signed`,
132342
- "Nothing was sold or sent. Tell the user it was not done. Do it only if the user asks again: then call wallet_transfer_token again (it asks the user again)",
132343
- { requestId: id }
132392
+ confirmed.unshown ? ownAddressNext() : "Nothing was sold or sent. Tell the user it was not done. Do it only if the user asks again: then call wallet_transfer_token again (it asks the user again)",
132393
+ { requestId: id, ...confirmed.unshown ? { ownAddressCommand: ownAddressCommand() } : {} }
132344
132394
  );
132345
132395
  }
132346
132396
  approval = { by: "elicitation", at: (/* @__PURE__ */ new Date()).toISOString() };
@@ -134638,6 +134688,7 @@ async function openWalletServer({
134638
134688
  noShield = false,
134639
134689
  maxShieldAtomic,
134640
134690
  unshieldTo,
134691
+ unshieldToNow,
134641
134692
  maxUnshieldAtomic,
134642
134693
  exitProofToolsPath,
134643
134694
  profilePath,
@@ -134680,7 +134731,8 @@ async function openWalletServer({
134680
134731
  {
134681
134732
  ...fixedMoney,
134682
134733
  autoPockets: autoPockets !== false,
134683
- readyPockets: readyText ? Number(readyText) : DEFAULT_READY_POCKETS
134734
+ readyPockets: readyText ? Number(readyText) : DEFAULT_READY_POCKETS,
134735
+ ...typeof unshieldToNow === "function" ? { unshieldToNow } : {}
134684
134736
  },
134685
134737
  Object.fromEntries(Object.keys(live).map((key) => [key, { get: () => live[key], enumerable: true }]))
134686
134738
  ));
@@ -135416,7 +135468,7 @@ async function runChat({ io, values, deps = {} }) {
135416
135468
  if (code2 === "inference_tor_required" || code2 === "ECONNREFUSED" || proxyRefusedConnection(error)) {
135417
135469
  say6(`Your private connection (Tor) stopped answering, so the question could not go out. ${paidWords} Start Tor again, then ${again}.`);
135418
135470
  } else if (INSUFFICIENT2.test(code2 ?? "")) {
135419
- say6(`Your private balance is too low for this answer. ${paidWords} Add funds: ${FUND_COMMAND} (whole USDC from an exchange), or ask your agent to add funds (from 0.01 USDC). Then ${again}.`);
135471
+ say6(`Your private balance is too low for this answer. ${paidWords} Add funds: ${FUND_COMMAND} (any amount from 0.01 USDC: change 2 to the amount you want), or ask your agent to add funds. Then ${again}.`);
135420
135472
  } else if (CAP_CODES.test(code2 ?? "")) {
135421
135473
  say6(`This answer would pass the chat's cap. ${paidWords} ${capsLine2()} End the chat (/exit) and start it again, with a higher --max-payment <usdc> or --max-session <usdc> when you want more.`);
135422
135474
  } else if (code2 === "inference_no_provider") {
@@ -155910,7 +155962,7 @@ import { resolve as resolve40 } from "node:path";
155910
155962
 
155911
155963
  // wallet/src/cli/shinjukuWallet.mjs
155912
155964
  init_fs();
155913
- var import_web3138 = __toESM(require_index_cjs(), 1);
155965
+ var import_web3139 = __toESM(require_index_cjs(), 1);
155914
155966
  init_esm2();
155915
155967
  init_shielded_wallet();
155916
155968
  init_walk();
@@ -155936,6 +155988,7 @@ init_config2();
155936
155988
  // wallet/src/setup/setup.mjs
155937
155989
  init_processLease();
155938
155990
  init_fs();
155991
+ var import_web3135 = __toESM(require_index_cjs(), 1);
155939
155992
  init_secretFile();
155940
155993
  init_recoveryFile();
155941
155994
  init_config2();
@@ -156076,6 +156129,27 @@ function readPassphrase(path, access) {
156076
156129
  if (text3.length < 8) throw fail40("setup_passphrase_too_short", "the passphrase in the passphrase file is shorter than 8 characters; nothing was created");
156077
156130
  return text3;
156078
156131
  }
156132
+ function addOwnAddress({ home, address: address5 }, deps) {
156133
+ const config = readSetupConfig(home);
156134
+ if (!config?.pool || !config.passphraseFile) throw fail40("setup_own_address_not_set_up", "this wallet home is not set up yet; config.json was not changed. Run setup first");
156135
+ const old = [...config.unshieldTo ?? []];
156136
+ if (old.includes(address5)) return { ownAddresses: old, dropped: [] };
156137
+ let own;
156138
+ try {
156139
+ const passphrase2 = readSecretText(config.passphraseFile, "the passphrase file");
156140
+ own = import_web3135.Keypair.fromSecretKey(Uint8Array.from(deps.openWalletStore(homeWalletPath(home, config.pool), passphrase2).state.solanaSecretKey)).publicKey.toBase58();
156141
+ } catch {
156142
+ own = void 0;
156143
+ }
156144
+ if (own === void 0) throw fail40("setup_own_address_unchecked", "setup could not open the wallet to check that the address is not this wallet's own; config.json was not changed. Run setup again");
156145
+ if (own === address5) {
156146
+ throw fail40("setup_own_address_is_wallet", `${own} is this wallet's own address. A send to it would link your sends to your deposits, so it is never approved. Give the address of another wallet you own (Phantom, Solflare, or an exchange's deposit address); config.json was not changed`);
156147
+ }
156148
+ const all = [...old, address5];
156149
+ const kept = all.slice(-MAX_OWN_ADDRESSES);
156150
+ writeSetupConfig(home, { ...config, unshieldTo: kept });
156151
+ return { ownAddresses: kept, dropped: all.slice(0, all.length - kept.length) };
156152
+ }
156079
156153
  async function runSetup(options, deps = {}) {
156080
156154
  const home = resolve28(options.home);
156081
156155
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
@@ -156267,6 +156341,7 @@ async function runSetup(options, deps = {}) {
156267
156341
  const drainingHere = (releasePools?.migration?.from ?? []).filter((entry) => entry.pool !== identity.pool && existsSync(homeWalletPath(home, entry.pool)) && !(previousPools ?? []).some((known) => known.pool === entry.pool));
156268
156342
  if (drainingHere.length) previousPools = [...previousPools ?? [], ...drainingHere.map((entry) => ({ pool: entry.pool, program: entry.programId }))].slice(-8);
156269
156343
  const torTunnel = tor ? options.torTunnel ?? old?.torTunnel : void 0;
156344
+ const unshieldTo = old?.unshieldTo ? [...old.unshieldTo] : [];
156270
156345
  const base = {
156271
156346
  schema: CONFIG_SCHEMA,
156272
156347
  ...identity,
@@ -156275,6 +156350,7 @@ async function runSetup(options, deps = {}) {
156275
156350
  caps,
156276
156351
  tor,
156277
156352
  ...torTunnel ? { torTunnel } : {},
156353
+ ...unshieldTo.length ? { unshieldTo } : {},
156278
156354
  ...wallet.recoveryFile ? { recoveryFile: wallet.recoveryFile } : {},
156279
156355
  ...previousPools?.length ? { previousPools } : {}
156280
156356
  };
@@ -156362,6 +156438,7 @@ async function runSetup(options, deps = {}) {
156362
156438
  wallet,
156363
156439
  passphraseFile,
156364
156440
  passphraseGenerated,
156441
+ ownAddresses: unshieldTo,
156365
156442
  proofTools: { dir: tools.dir, downloaded: tools.downloaded, folder: tools.folder },
156366
156443
  platform: platform.wsl ? "windows+wsl" : "linux",
156367
156444
  warnings,
@@ -157333,7 +157410,7 @@ init_cli3();
157333
157410
 
157334
157411
  // wallet/src/deposit/privacyCash.mjs
157335
157412
  init_fs();
157336
- var import_web3137 = __toESM(require_index_cjs(), 1);
157413
+ var import_web3138 = __toESM(require_index_cjs(), 1);
157337
157414
  init_esm2();
157338
157415
  init_balance();
157339
157416
  init_chain2();
@@ -157347,7 +157424,7 @@ import { dirname as dirname30, join as join36, resolve as resolve32 } from "node
157347
157424
  import { fileURLToPath as fileURLToPath20, pathToFileURL as pathToFileURL2 } from "node:url";
157348
157425
 
157349
157426
  // wallet/src/deposit/privacyCashLocal.mjs
157350
- var import_web3135 = __toESM(require_index_cjs(), 1);
157427
+ var import_web3136 = __toESM(require_index_cjs(), 1);
157351
157428
  init_esm2();
157352
157429
  init_chain2();
157353
157430
  import { randomInt as randomInt8 } from "node:crypto";
@@ -157425,7 +157502,7 @@ async function withoutPrivacyCashServer(operation) {
157425
157502
  }
157426
157503
  }
157427
157504
  async function currentTreeState(connection, ids) {
157428
- const info = await retryRpc2(() => connection.getAccountInfo(new import_web3135.PublicKey(ids.tree), "confirmed"));
157505
+ const info = await retryRpc2(() => connection.getAccountInfo(new import_web3136.PublicKey(ids.tree), "confirmed"));
157429
157506
  if (!info || info.owner.toBase58() !== ids.program || info.data.length < TREE_ROOT_OFFSET + 32) fail43("pool_unreadable");
157430
157507
  const data = Buffer.from(info.data);
157431
157508
  return {
@@ -157434,7 +157511,7 @@ async function currentTreeState(connection, ids) {
157434
157511
  };
157435
157512
  }
157436
157513
  async function programFeeRule(connection, ids) {
157437
- const info = await retryRpc2(() => connection.getAccountInfo(new import_web3135.PublicKey(ids.globalConfig), "finalized"));
157514
+ const info = await retryRpc2(() => connection.getAccountInfo(new import_web3136.PublicKey(ids.globalConfig), "finalized"));
157438
157515
  if (!info || info.owner.toBase58() !== ids.program || info.data.length < FEE_ERROR_MARGIN_OFFSET + 2) fail43("pool_unreadable");
157439
157516
  const data = Buffer.from(info.data);
157440
157517
  return { withdrawalFeeRate: BigInt(data.readUInt16LE(WITHDRAWAL_FEE_RATE_OFFSET)), feeErrorMargin: BigInt(data.readUInt16LE(FEE_ERROR_MARGIN_OFFSET)) };
@@ -157444,25 +157521,25 @@ function programMinimumFee(net2, rule) {
157444
157521
  return expected === 0n ? 0n : expected * (10000n - rule.feeErrorMargin) / 10000n;
157445
157522
  }
157446
157523
  function transactAccounts({ ids, nullifiers, signer, signerTokenAccount, recipient, recipientTokenAccount, feeRecipientTokenAccount }) {
157447
- const program = new import_web3135.PublicKey(ids.program);
157448
- const pda = (seed, value) => import_web3135.PublicKey.findProgramAddressSync([Buffer.from(seed), value], program)[0];
157524
+ const program = new import_web3136.PublicKey(ids.program);
157525
+ const pda = (seed, value) => import_web3136.PublicKey.findProgramAddressSync([Buffer.from(seed), value], program)[0];
157449
157526
  return [
157450
- { pubkey: new import_web3135.PublicKey(ids.tree), isSigner: false, isWritable: true },
157527
+ { pubkey: new import_web3136.PublicKey(ids.tree), isSigner: false, isWritable: true },
157451
157528
  { pubkey: pda("nullifier0", nullifiers[0]), isSigner: false, isWritable: true },
157452
157529
  { pubkey: pda("nullifier1", nullifiers[1]), isSigner: false, isWritable: true },
157453
157530
  { pubkey: pda("nullifier0", nullifiers[1]), isSigner: false, isWritable: false },
157454
157531
  { pubkey: pda("nullifier1", nullifiers[0]), isSigner: false, isWritable: false },
157455
- { pubkey: new import_web3135.PublicKey(ids.globalConfig), isSigner: false, isWritable: false },
157532
+ { pubkey: new import_web3136.PublicKey(ids.globalConfig), isSigner: false, isWritable: false },
157456
157533
  { pubkey: signer, isSigner: true, isWritable: true },
157457
- { pubkey: new import_web3135.PublicKey(ids.mint), isSigner: false, isWritable: false },
157534
+ { pubkey: new import_web3136.PublicKey(ids.mint), isSigner: false, isWritable: false },
157458
157535
  { pubkey: signerTokenAccount, isSigner: false, isWritable: true },
157459
157536
  { pubkey: recipient, isSigner: false, isWritable: true },
157460
157537
  { pubkey: recipientTokenAccount, isSigner: false, isWritable: true },
157461
- { pubkey: new import_web3135.PublicKey(ids.vault), isSigner: false, isWritable: true },
157538
+ { pubkey: new import_web3136.PublicKey(ids.vault), isSigner: false, isWritable: true },
157462
157539
  { pubkey: feeRecipientTokenAccount, isSigner: false, isWritable: true },
157463
157540
  { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
157464
157541
  { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
157465
- { pubkey: import_web3135.SystemProgram.programId, isSigner: false, isWritable: false }
157542
+ { pubkey: import_web3136.SystemProgram.programId, isSigner: false, isWritable: false }
157466
157543
  ];
157467
157544
  }
157468
157545
  async function proveTransact({ internals, sdk, encryptionService, inputs, paths, root, nextIndex, outputAmount, extAmount, fee, recipientTokenAccount, feeRecipientTokenAccount, mint, padChange = false }) {
@@ -157495,7 +157572,7 @@ async function proveTransact({ internals, sdk, encryptionService, inputs, paths,
157495
157572
  const extDataHashDecimal = BigInt(`0x${Buffer.from(extDataHash).reverse().toString("hex")}`).toString();
157496
157573
  const input = {
157497
157574
  root,
157498
- mintAddress: internals.getMintAddressField(new import_web3135.PublicKey(mint)),
157575
+ mintAddress: internals.getMintAddressField(new import_web3136.PublicKey(mint)),
157499
157576
  publicAmount: publicAmount.toString(),
157500
157577
  extDataHash,
157501
157578
  inAmount: inputs.map((utxo) => utxo.amount.toString(10)),
@@ -157529,7 +157606,7 @@ async function proveTransact({ internals, sdk, encryptionService, inputs, paths,
157529
157606
  return { data, nullifiers: inputNullifiers.map(toBeBytes), outputs };
157530
157607
  }
157531
157608
  function compile({ payer, blockhash, instructions: instructions2, lookupTable, priceInstruction }) {
157532
- const build2 = (list) => new import_web3135.VersionedTransaction(new import_web3135.TransactionMessage({ payerKey: payer, recentBlockhash: blockhash, instructions: list }).compileToV0Message([lookupTable]));
157609
+ const build2 = (list) => new import_web3136.VersionedTransaction(new import_web3136.TransactionMessage({ payerKey: payer, recentBlockhash: blockhash, instructions: list }).compileToV0Message([lookupTable]));
157533
157610
  const transaction = build2(priceInstruction ? [instructions2[0], priceInstruction, ...instructions2.slice(1)] : instructions2);
157534
157611
  let bytes2;
157535
157612
  try {
@@ -157561,7 +157638,7 @@ async function buildLocalDeposit({ internals, sdk, connection, ids, source, amou
157561
157638
  paths = [zeroPath(), zeroPath()];
157562
157639
  }
157563
157640
  const total = inputs.reduce((sum2, utxo) => sum2 + BigInt(utxo.amount.toString()), 0n) + BigInt(amount);
157564
- const placeholder = getAssociatedTokenAddressSync(new import_web3135.PublicKey(mint), new import_web3135.PublicKey(ids.feeRecipient), true, TOKEN_PROGRAM_ID);
157641
+ const placeholder = getAssociatedTokenAddressSync(new import_web3136.PublicKey(mint), new import_web3136.PublicKey(ids.feeRecipient), true, TOKEN_PROGRAM_ID);
157565
157642
  const proved = await proveTransact({
157566
157643
  internals,
157567
157644
  sdk,
@@ -157577,28 +157654,28 @@ async function buildLocalDeposit({ internals, sdk, connection, ids, source, amou
157577
157654
  feeRecipientTokenAccount: placeholder,
157578
157655
  mint
157579
157656
  });
157580
- const sourceTokenAccount = getAssociatedTokenAddressSync(new import_web3135.PublicKey(mint), source, false, TOKEN_PROGRAM_ID);
157581
- const deposit = new import_web3135.TransactionInstruction({
157582
- programId: new import_web3135.PublicKey(ids.program),
157657
+ const sourceTokenAccount = getAssociatedTokenAddressSync(new import_web3136.PublicKey(mint), source, false, TOKEN_PROGRAM_ID);
157658
+ const deposit = new import_web3136.TransactionInstruction({
157659
+ programId: new import_web3136.PublicKey(ids.program),
157583
157660
  keys: transactAccounts({
157584
157661
  ids,
157585
157662
  nullifiers: proved.nullifiers,
157586
157663
  signer: source,
157587
157664
  signerTokenAccount: sourceTokenAccount,
157588
- recipient: new import_web3135.PublicKey(ids.feeRecipient),
157665
+ recipient: new import_web3136.PublicKey(ids.feeRecipient),
157589
157666
  recipientTokenAccount: placeholder,
157590
157667
  feeRecipientTokenAccount: placeholder
157591
157668
  }),
157592
157669
  data: proved.data
157593
157670
  });
157594
- const table = (await retryRpc2(() => connection.getAddressLookupTable(new import_web3135.PublicKey(ids.lookupTable)))).value;
157671
+ const table = (await retryRpc2(() => connection.getAddressLookupTable(new import_web3136.PublicKey(ids.lookupTable)))).value;
157595
157672
  if (!table) fail43("pool_unreadable");
157596
157673
  const latest = await retryRpc2(() => connection.getLatestBlockhash("confirmed"));
157597
157674
  const transaction = compile({
157598
157675
  payer: source,
157599
157676
  blockhash: latest.blockhash,
157600
- instructions: [import_web3135.ComputeBudgetProgram.setComputeUnitLimit({ units: SDK_DEPOSIT_COMPUTE_UNITS }), deposit],
157601
- priceInstruction: import_web3135.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: SDK_DEPOSIT_MICRO_LAMPORTS }),
157677
+ instructions: [import_web3136.ComputeBudgetProgram.setComputeUnitLimit({ units: SDK_DEPOSIT_COMPUTE_UNITS }), deposit],
157678
+ priceInstruction: import_web3136.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: SDK_DEPOSIT_MICRO_LAMPORTS }),
157602
157679
  lookupTable: table
157603
157680
  });
157604
157681
  return {
@@ -157625,13 +157702,13 @@ async function readRelayConfig2({ serviceUrl: serviceUrl2, ids, fetchImpl = fetc
157625
157702
  let feeTokenAccount;
157626
157703
  let lookupTable;
157627
157704
  try {
157628
- feePayer = new import_web3135.PublicKey(config?.feePayer);
157629
- feeTokenAccount = new import_web3135.PublicKey(config?.feeTokenAccount);
157630
- lookupTable = new import_web3135.PublicKey(config?.lookupTable);
157705
+ feePayer = new import_web3136.PublicKey(config?.feePayer);
157706
+ feeTokenAccount = new import_web3136.PublicKey(config?.feeTokenAccount);
157707
+ lookupTable = new import_web3136.PublicKey(config?.lookupTable);
157631
157708
  } catch {
157632
157709
  fail43("relay_config_invalid");
157633
157710
  }
157634
- if (config?.schema !== PRIVACY_CASH_RELAY_CONFIG_SCHEMA || config.programId !== ids.program || config.mint !== ids.mint || feePayer.toBase58() !== config.feePayer || lookupTable.toBase58() !== config.lookupTable || !feeTokenAccount.equals(getAssociatedTokenAddressSync(new import_web3135.PublicKey(ids.mint), feePayer, false, TOKEN_PROGRAM_ID)) || !atomic4(config.feeFloorAtomic) || !atomic4(config.minimumAtomic) || !atomic4(config.maximumAtomic) || !atomic4(config.computeUnitPriceMicroLamports) || !Number.isSafeInteger(config.computeUnitLimit) || config.computeUnitLimit < 3e5 || config.computeUnitLimit > 14e5) {
157711
+ if (config?.schema !== PRIVACY_CASH_RELAY_CONFIG_SCHEMA || config.programId !== ids.program || config.mint !== ids.mint || feePayer.toBase58() !== config.feePayer || lookupTable.toBase58() !== config.lookupTable || !feeTokenAccount.equals(getAssociatedTokenAddressSync(new import_web3136.PublicKey(ids.mint), feePayer, false, TOKEN_PROGRAM_ID)) || !atomic4(config.feeFloorAtomic) || !atomic4(config.minimumAtomic) || !atomic4(config.maximumAtomic) || !atomic4(config.computeUnitPriceMicroLamports) || !Number.isSafeInteger(config.computeUnitLimit) || config.computeUnitLimit < 3e5 || config.computeUnitLimit > 14e5) {
157635
157712
  fail43("relay_config_mismatch");
157636
157713
  }
157637
157714
  return Object.freeze({
@@ -157654,8 +157731,8 @@ async function buildLocalWithdrawal({ internals, sdk, connection, ids, encryptio
157654
157731
  const total = inputs.reduce((sum2, utxo) => sum2 + BigInt(utxo.amount.toString()), 0n);
157655
157732
  const change = total - net2 - fee;
157656
157733
  if (change < 0n) fail43("withdraw_balance_insufficient");
157657
- const owner = new import_web3135.PublicKey(recipient);
157658
- const recipientTokenAccount = getAssociatedTokenAddressSync(new import_web3135.PublicKey(mint), owner, false, TOKEN_PROGRAM_ID);
157734
+ const owner = new import_web3136.PublicKey(recipient);
157735
+ const recipientTokenAccount = getAssociatedTokenAddressSync(new import_web3136.PublicKey(mint), owner, false, TOKEN_PROGRAM_ID);
157659
157736
  const proved = await proveTransact({
157660
157737
  internals,
157661
157738
  sdk,
@@ -157672,8 +157749,8 @@ async function buildLocalWithdrawal({ internals, sdk, connection, ids, encryptio
157672
157749
  mint,
157673
157750
  padChange: true
157674
157751
  });
157675
- const withdrawal = new import_web3135.TransactionInstruction({
157676
- programId: new import_web3135.PublicKey(ids.program),
157752
+ const withdrawal = new import_web3136.TransactionInstruction({
157753
+ programId: new import_web3136.PublicKey(ids.program),
157677
157754
  keys: transactAccounts({
157678
157755
  ids,
157679
157756
  nullifiers: proved.nullifiers,
@@ -157692,12 +157769,12 @@ async function buildLocalWithdrawal({ internals, sdk, connection, ids, encryptio
157692
157769
  payer: relay.feePayer,
157693
157770
  blockhash: latest.blockhash,
157694
157771
  instructions: [
157695
- import_web3135.ComputeBudgetProgram.setComputeUnitLimit({ units: relay.computeUnitLimit }),
157772
+ import_web3136.ComputeBudgetProgram.setComputeUnitLimit({ units: relay.computeUnitLimit }),
157696
157773
  // Create with empty data, as Privacy Cash's relayer sends it (30 of 30 new recipients); the address is fresh.
157697
- createAssociatedTokenAccountInstruction(relay.feePayer, recipientTokenAccount, owner, new import_web3135.PublicKey(mint), TOKEN_PROGRAM_ID),
157774
+ createAssociatedTokenAccountInstruction(relay.feePayer, recipientTokenAccount, owner, new import_web3136.PublicKey(mint), TOKEN_PROGRAM_ID),
157698
157775
  withdrawal
157699
157776
  ],
157700
- priceInstruction: relay.computeUnitPrice > 0n ? import_web3135.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: relay.computeUnitPrice }) : void 0,
157777
+ priceInstruction: relay.computeUnitPrice > 0n ? import_web3136.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: relay.computeUnitPrice }) : void 0,
157701
157778
  lookupTable: table
157702
157779
  });
157703
157780
  return {
@@ -157745,15 +157822,15 @@ async function relayWithdrawal({ serviceUrl: serviceUrl2, transactionBase64, fet
157745
157822
  }
157746
157823
 
157747
157824
  // wallet/src/deposit/privacyCashTree.mjs
157748
- var import_web3136 = __toESM(require_index_cjs(), 1);
157825
+ var import_web3137 = __toESM(require_index_cjs(), 1);
157749
157826
  init_chain2();
157750
157827
  init_proxy();
157751
157828
  init_sealedCache();
157752
157829
  init_reference2();
157753
157830
  import { createHash as createHash32, createPublicKey as createPublicKey6, verify as verify3 } from "node:crypto";
157754
- var PROGRAM_ID = new import_web3136.PublicKey("9fhQBbumKEFuXtMBDw8AaQyAjCorLGJQiS3skWZdQyQD");
157755
- var USDC_MINT2 = new import_web3136.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
157756
- var TREE_ACCOUNT = import_web3136.PublicKey.findProgramAddressSync([Buffer.from("merkle_tree"), USDC_MINT2.toBuffer()], PROGRAM_ID)[0];
157831
+ var PROGRAM_ID = new import_web3137.PublicKey("9fhQBbumKEFuXtMBDw8AaQyAjCorLGJQiS3skWZdQyQD");
157832
+ var USDC_MINT2 = new import_web3137.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
157833
+ var TREE_ACCOUNT = import_web3137.PublicKey.findProgramAddressSync([Buffer.from("merkle_tree"), USDC_MINT2.toBuffer()], PROGRAM_ID)[0];
157757
157834
  var TREE_DEPTH3 = 26;
157758
157835
  var TREE_ACCOUNT_BYTES = 4136;
157759
157836
  var NEXT_INDEX_OFFSET = 40;
@@ -157966,7 +158043,7 @@ function transactionMessage(signature2, result) {
157966
158043
  decoded = decodeVersion1(raw);
157967
158044
  } else {
157968
158045
  if (raw[0] === 0 || raw[0] > 127) throw new Error("unknown transaction format");
157969
- const { message } = import_web3136.VersionedTransaction.deserialize(raw);
158046
+ const { message } = import_web3137.VersionedTransaction.deserialize(raw);
157970
158047
  decoded = {
157971
158048
  message: raw.subarray(1 + 64 * raw[0]),
157972
158049
  first: raw.subarray(1, 65),
@@ -158311,7 +158388,7 @@ function countCoverDeposits(crowd, { sinceLeafIndex, sinceSlot, grossAtomic }) {
158311
158388
  return Object.freeze({ count: count2, from: crowd.from, lowerBound });
158312
158389
  }
158313
158390
  async function findUnspentNotes({ leaves, encryptionService, lightWasm, mint = USDC_MINT2, spent = [], knownSpent = /* @__PURE__ */ new Set() }) {
158314
- const mintAddress = new import_web3136.PublicKey(mint).toBase58();
158391
+ const mintAddress = new import_web3137.PublicKey(mint).toBase58();
158315
158392
  const notes = [];
158316
158393
  for (const [index, leaf] of leaves.entries()) {
158317
158394
  let utxo;
@@ -158535,7 +158612,7 @@ function readSourceKeyFile(path) {
158535
158612
  fail45("key_file_invalid");
158536
158613
  }
158537
158614
  try {
158538
- return import_web3137.Keypair.fromSecretKey(Uint8Array.from(bytes2));
158615
+ return import_web3138.Keypair.fromSecretKey(Uint8Array.from(bytes2));
158539
158616
  } catch {
158540
158617
  return fail45("key_file_invalid");
158541
158618
  }
@@ -158562,7 +158639,7 @@ async function tokenBalance(connection, tokenAccount, commitment = "finalized")
158562
158639
  try {
158563
158640
  account2 = await retryRpc2(async () => {
158564
158641
  try {
158565
- return await getAccount(connection, new import_web3137.PublicKey(tokenAccount), commitment, TOKEN_PROGRAM_ID);
158642
+ return await getAccount(connection, new import_web3138.PublicKey(tokenAccount), commitment, TOKEN_PROGRAM_ID);
158566
158643
  } catch (error) {
158567
158644
  if (error instanceof TokenAccountNotFoundError) return null;
158568
158645
  throw error;
@@ -158585,7 +158662,7 @@ function assertDepositTransaction(transaction, { source, sourceTokenAccount, amo
158585
158662
  }
158586
158663
  const key = (index) => keys3.get(index)?.toBase58();
158587
158664
  if (key(0) !== source) fail45("deposit_transaction_unexpected");
158588
- const budget = import_web3137.ComputeBudgetProgram.programId.toBase58();
158665
+ const budget = import_web3138.ComputeBudgetProgram.programId.toBase58();
158589
158666
  let unitLimit;
158590
158667
  let unitPrice;
158591
158668
  for (const instruction of message.compiledInstructions.filter((candidate) => key(candidate.programIdIndex) === budget)) {
@@ -158702,12 +158779,12 @@ async function privacyCashDeposit({ store, rpcUrl, treeRpcUrl, keyFile, amountAt
158702
158779
  }));
158703
158780
  return Object.freeze({ command: "privacy-cash deposit", settleOnly: true, source, deposits: deposits2 });
158704
158781
  }
158705
- const mint = new import_web3137.PublicKey(USDC_MINT3);
158782
+ const mint = new import_web3138.PublicKey(USDC_MINT3);
158706
158783
  const sourceTokenAccount = getAssociatedTokenAddressSync(mint, keypair.publicKey, false, TOKEN_PROGRAM_ID).toBase58();
158707
158784
  const held = await tokenBalance(connection, sourceTokenAccount);
158708
158785
  if (held < amount) fail45("source_usdc_insufficient", { heldAtomic: held.toString() });
158709
158786
  if (await retryRpc2(() => connection.getBalance(keypair.publicKey, "finalized")) < MIN_SOURCE_LAMPORTS) fail45("source_sol_insufficient");
158710
- const [tree] = import_web3137.PublicKey.findProgramAddressSync([Buffer.from("merkle_tree"), mint.toBuffer()], new import_web3137.PublicKey(PRIVACY_CASH.program));
158787
+ const [tree] = import_web3138.PublicKey.findProgramAddressSync([Buffer.from("merkle_tree"), mint.toBuffer()], new import_web3138.PublicKey(PRIVACY_CASH.program));
158711
158788
  const treeAccount = await retryRpc2(() => connection.getAccountInfo(tree, "finalized"));
158712
158789
  if (!treeAccount || treeAccount.owner.toBase58() !== PRIVACY_CASH.program || treeAccount.data.length < 4128) fail45("pool_unreadable");
158713
158790
  if (amount > Buffer.from(treeAccount.data).readBigUInt64LE(4120)) fail45("deposit_above_limit");
@@ -158866,8 +158943,8 @@ function relayedDeposit(store, depositId) {
158866
158943
  const record = store.readWal(`deposit-${depositId}`);
158867
158944
  if (!record) fail45("withdraw_no_relayed_deposit");
158868
158945
  if (record.schema !== DEPOSIT_WAL_SCHEMA || !record.relay) fail45("withdraw_not_a_relayed_deposit");
158869
- const owner = new import_web3137.PublicKey(record.relay.owner);
158870
- if (record.asset !== USDC_MINT3 || getAssociatedTokenAddressSync(new import_web3137.PublicKey(USDC_MINT3), owner, true, TOKEN_PROGRAM_ID).toBase58() !== record.relay.tokenAccount) {
158946
+ const owner = new import_web3138.PublicKey(record.relay.owner);
158947
+ if (record.asset !== USDC_MINT3 || getAssociatedTokenAddressSync(new import_web3138.PublicKey(USDC_MINT3), owner, true, TOKEN_PROGRAM_ID).toBase58() !== record.relay.tokenAccount) {
158871
158948
  fail45("withdraw_one_time_address_mismatch");
158872
158949
  }
158873
158950
  return { record, owner: record.relay.owner, tokenAccount: record.relay.tokenAccount, amount: BigInt(record.amountAtomic) };
@@ -159031,8 +159108,8 @@ function proofFacts(data) {
159031
159108
  }
159032
159109
  const first = data.subarray(NULLIFIERS_OFFSET2, NULLIFIERS_OFFSET2 + 32);
159033
159110
  const second = data.subarray(NULLIFIERS_OFFSET2 + 32, NULLIFIERS_OFFSET2 + 64);
159034
- const program = new import_web3137.PublicKey(PRIVACY_CASH.program);
159035
- const account2 = (seed, nullifier) => import_web3137.PublicKey.findProgramAddressSync([Buffer.from(seed), nullifier], program)[0].toBase58();
159111
+ const program = new import_web3138.PublicKey(PRIVACY_CASH.program);
159112
+ const account2 = (seed, nullifier) => import_web3138.PublicKey.findProgramAddressSync([Buffer.from(seed), nullifier], program)[0].toBase58();
159036
159113
  return {
159037
159114
  rootHex: data.subarray(ROOT_OFFSET, ROOT_OFFSET + 32).toString("hex"),
159038
159115
  nullifiersHex: [first.toString("hex"), second.toString("hex")],
@@ -159042,7 +159119,7 @@ function proofFacts(data) {
159042
159119
  function transactSplData(transactionBase64) {
159043
159120
  let transaction;
159044
159121
  try {
159045
- transaction = import_web3137.VersionedTransaction.deserialize(Buffer.from(transactionBase64, "base64"));
159122
+ transaction = import_web3138.VersionedTransaction.deserialize(Buffer.from(transactionBase64, "base64"));
159046
159123
  } catch {
159047
159124
  fail45("withdraw_proof_unreadable");
159048
159125
  }
@@ -159091,7 +159168,7 @@ async function oneTimeAccountHistory(connection, tokenAccount, sinceMs) {
159091
159168
  const signatures = [];
159092
159169
  let before;
159093
159170
  for (let page = 0; page < 10; page += 1) {
159094
- const entries = await retryRpc2(() => connection.getSignaturesForAddress(new import_web3137.PublicKey(tokenAccount), { limit: 100, ...before ? { before } : {} }, "finalized"));
159171
+ const entries = await retryRpc2(() => connection.getSignaturesForAddress(new import_web3138.PublicKey(tokenAccount), { limit: 100, ...before ? { before } : {} }, "finalized"));
159095
159172
  for (const entry of entries) {
159096
159173
  if (entry.blockTime && entry.blockTime * 1e3 < sinceMs) return signatures;
159097
159174
  if (entry.err === null) signatures.push(entry.signature);
@@ -159161,7 +159238,7 @@ async function earlierProofs(connection, record, walk = void 0) {
159161
159238
  const roots = /* @__PURE__ */ new Set([...finalized.roots, ...confirmed.roots]);
159162
159239
  const walked = walk ? (await walk()).synced : void 0;
159163
159240
  for (const proof of record.proofs) {
159164
- const keys3 = proof.nullifierAccounts.map((key) => new import_web3137.PublicKey(key));
159241
+ const keys3 = proof.nullifierAccounts.map((key) => new import_web3138.PublicKey(key));
159165
159242
  if (walked) {
159166
159243
  const paid = proof.nullifiersHex.filter((nullifier) => walked.spentNullifiers.has(nullifier)).map((nullifier) => walked.spentNullifiers.get(nullifier));
159167
159244
  if (paid.length && paid.every((account2) => account2 !== null && account2 !== record.oneTimeTokenAccount)) continue;
@@ -159309,7 +159386,7 @@ async function privacyCashWithdraw({ store, rpcUrl, treeRpcUrl, keyFile, deposit
159309
159386
  if (uncounted && !holdOverride) fail45("withdraw_crowd_uncounted", { via, reason: uncounted });
159310
159387
  const recent = via === "sdk" ? await recentCrowd({ connection, seconds: CROWD_WINDOW_DAYS * 86400, now: Math.floor(chainNow / 1e3) }) : void 0;
159311
159388
  if (place && !uncounted && recent) countFrom(recent, { sinceLeafIndex: place.leafIndex, sinceSlot: place.slot }, "deposit");
159312
- const mint = new import_web3137.PublicKey(USDC_MINT3);
159389
+ const mint = new import_web3138.PublicKey(USDC_MINT3);
159313
159390
  const encryptionService = noteKeys(sdk.utils, keypair);
159314
159391
  const storage = memoryStorage();
159315
159392
  const sdkConnection = connect2(rpcUrl, "confirmed");
@@ -159474,7 +159551,7 @@ async function privacyCashWithdraw({ store, rpcUrl, treeRpcUrl, keyFile, deposit
159474
159551
  let answer;
159475
159552
  try {
159476
159553
  answer = await watchedSdkWithdraw(() => quietly(() => sdk.utils.withdrawSPL({
159477
- recipient: new import_web3137.PublicKey(deposit.owner),
159554
+ recipient: new import_web3138.PublicKey(deposit.owner),
159478
159555
  lightWasm: sdk.lightWasm,
159479
159556
  storage,
159480
159557
  publicKey: keypair.publicKey,
@@ -159978,8 +160055,8 @@ function fundingWait(ctx, held) {
159978
160055
  const { plan, store } = ctx;
159979
160056
  const decimals = Number(store.state.decimals ?? 6);
159980
160057
  const part = /^[0-9]{1,20}$/.test(String(held ?? "")) && BigInt(held) > 0n ? BigInt(held) : 0n;
159981
- const whole = part - part % 1000000n;
159982
- const holds = part > 0n ? `; it holds ${usdc5(part, decimals)} so far: send the rest${whole > 0n ? `, or stop add-funds (Ctrl-C) and rerun it with --amount ${usdcFlag(whole)} (take the rest back later with ${CLI} deposit-reclaim)` : ""}` : "";
160058
+ const usable = plan.route === "privacy-cash" ? part - part % ONE_USDC : part >= PAY_ME_MIN_ATOMIC ? part : 0n;
160059
+ const holds = part > 0n ? `; it holds ${usdc5(part, decimals)} so far: send the rest${usable > 0n ? `, or stop add-funds (Ctrl-C) and rerun it with --amount ${usdcFlag(usable)}${usable < part ? ` (take the rest back later with ${CLI} deposit-reclaim)` : ""}` : ""}` : "";
159983
160060
  return `${usdc5(plan.amountAtomic, decimals)} USDC (Solana SPL) at ${relayedDeposit2(store, plan)?.relay?.owner ?? "the one-time address"}${holds}`;
159984
160061
  }
159985
160062
  function splitFundingWait(ctx, waiting) {
@@ -161969,7 +162046,7 @@ function validateMcp(values, env) {
161969
162046
  for (const address5 of unshieldTo) {
161970
162047
  let canonical5 = false;
161971
162048
  try {
161972
- canonical5 = new import_web3138.PublicKey(address5).toBase58() === address5;
162049
+ canonical5 = new import_web3139.PublicKey(address5).toBase58() === address5;
161973
162050
  } catch {
161974
162051
  canonical5 = false;
161975
162052
  }
@@ -162055,6 +162132,8 @@ function mcpServerOptions(values, deps) {
162055
162132
  ...values.has("--no-shield") ? { noShield: true } : {},
162056
162133
  ...values.has("--max-shield") ? { maxShieldAtomic: values.get("--max-shield") } : {},
162057
162134
  ...values.has("--unshield-to") ? { unshieldTo: values.get("--unshield-to") } : {},
162135
+ // A mcp run from config.json reads the user's own addresses again on each send (setup --own-address after the start).
162136
+ ...values.get(MCP_FROM_CONFIG) ? { unshieldToNow: () => ownAddressesNow(values.get(MCP_FROM_CONFIG).home) } : {},
162058
162137
  ...values.has("--max-unshield") ? { maxUnshieldAtomic: values.get("--max-unshield") } : {},
162059
162138
  ...values.has("--exit-proof-tools") ? { exitProofToolsPath: values.get("--exit-proof-tools") } : {},
162060
162139
  ...values.has("--profile") ? { profilePath: values.get("--profile") } : {},
@@ -162369,10 +162448,10 @@ var fundArgvOf = (amount) => SETUP_FUND_ARGV.map((word, index) => SETUP_FUND_ARG
162369
162448
  var fundCommandText = (tor, amount = "2") => `${CLI2} ${fundArgvOf(amount).map(shellWord).join(" ")}${tor ? " --tor" : ""}`;
162370
162449
  function fundingStepText(tor) {
162371
162450
  return [
162372
- "Add money: the wallet starts empty. Two private ways:",
162373
- ' Any amount from 0.01 USDC (cents work): add the server to your agent host (above), then ask your agent "Add 0.50 USDC to my shielded balance". It calls wallet_add_funds and gives a one-time address: send that USDC on Solana from any wallet or exchange, and it becomes private a few minutes after it arrives, at the next wallet call (no SOL needed; our relayer pays every fee).',
162374
- ` From an exchange, in this terminal, whole USDC only: ${fundCommandText(tor)}`,
162375
- " It prints a one-time address: withdraw exactly that amount of USDC (Solana SPL) from the exchange to it, and it shields the USDC by itself (our relayer pays every fee). --amount is in USDC (2 = 2 USDC): change it to the amount you want.",
162451
+ "Add money: the wallet starts empty. Two private ways, each for any amount from 0.01 USDC:",
162452
+ ` In this terminal: ${fundCommandText(tor)}`,
162453
+ " It prints a one-time address and its QR code: send exactly that amount of USDC (Solana SPL) to it from an exchange or a phone wallet, and it makes the USDC private by itself (our relayer pays every fee). --amount is in USDC (2 = 2 USDC, 0.05 = 5 cents): change it to the amount you want.",
162454
+ ' Or ask your agent: add the server to your agent host (above), then ask "Add 0.50 USDC to my private balance". It calls wallet_add_funds and gives a one-time address: send that USDC on Solana from any wallet or exchange, and it becomes private a few minutes after it arrives, at the next wallet call (no SOL needed; our relayer pays every fee).',
162376
162455
  ""
162377
162456
  ].join("\n");
162378
162457
  }
@@ -162387,8 +162466,8 @@ function privateInferencePath(config, { apiKey } = {}) {
162387
162466
  text: [
162388
162467
  "Private inference: your agent's first private AI answer in five steps.",
162389
162468
  ` 1. Fund: ${fundCommandText(true)}`,
162390
- " It prints a one-time address: withdraw exactly that amount of USDC (Solana SPL) from an exchange to it. --amount is in USDC and add-funds takes whole USDC only (2 = 2 USDC): change it to the amount you want. For less than 1 USDC (from 0.01 USDC), use the MCP way below.",
162391
- " Or, for any amount from 0.01 USDC, let the agent fund it over MCP: ask it to add funds; the tool wallet_add_funds gives a one-time address for USDC in another wallet or an exchange.",
162469
+ " It prints a one-time address and its QR code: send exactly that amount of USDC (Solana SPL) to it from an exchange or a phone wallet. --amount is in USDC, any amount from 0.01 (2 = 2 USDC, 0.05 = 5 cents): change it to the amount you want.",
162470
+ " Or let the agent fund it over MCP: ask it to add funds; the tool wallet_add_funds gives a one-time address for USDC in another wallet or an exchange.",
162392
162471
  " 2. Shield: add-funds does it. It moves the USDC into the shielded pool (our relayer pays every fee) and says ready with your balance; all of it stays there, where infer pays from.",
162393
162472
  ` 3. First private call (a short qwen3-8-flash answer costs about 0.0013 USDC: the AI provider's price plus the 0.001 USDC Shinjuku fee; setup never pays one). It needs no flags: config.json gives the wallet, the caps, and Tor:`,
162394
162473
  ` echo "Say hello" | ${CLI2} infer -`,
@@ -162405,14 +162484,39 @@ function privateInferencePath(config, { apiKey } = {}) {
162405
162484
  ].join("\n")
162406
162485
  };
162407
162486
  }
162487
+ async function askOwnAddress(deps, recorded = []) {
162488
+ const question = recorded.length ? `Your own wallet addresses now: ${recorded.join(", ")}. Add another one (Phantom, Solflare, or an exchange)? Paste it, or press Enter to skip: ` : "Your own wallet address (Phantom, Solflare, or an exchange), for sending money back to yourself? Paste it, or press Enter to skip: ";
162489
+ for (let attempt = 0; attempt < 3; attempt += 1) {
162490
+ const answer = String(await deps.ask(question)).trim();
162491
+ if (!answer || /^(n|no|skip)$/i.test(answer)) return void 0;
162492
+ if (isOwnAddressText(answer)) return answer;
162493
+ deps.io.err("That is not a Solana address (32 to 44 letters and digits, as your wallet app shows it). Paste it again, or press Enter to skip.\n");
162494
+ }
162495
+ deps.io.err(`No address recorded. To add one later, run ${CLI2} setup again in a terminal and paste it at this question.
162496
+ `);
162497
+ return void 0;
162498
+ }
162499
+ var ownAddressLine = (list) => ` own address ${list.join(", ")}: your agent sends money back to you there without asking each time`;
162500
+ function recordOwnAddress(home, address5, deps) {
162501
+ try {
162502
+ const { ownAddresses, dropped } = addOwnAddress({ home, address: address5 }, setupDependencies(deps));
162503
+ return [
162504
+ ownAddressLine(ownAddresses),
162505
+ ...dropped.length ? [` note config.json keeps ${MAX_OWN_ADDRESSES} own addresses: the oldest (${dropped.join(", ")}) now ask before each send`] : []
162506
+ ];
162507
+ } catch (error) {
162508
+ const why = /^setup_own_address_[a-z_]+: /.test(String(error?.message)) ? error.message : "setup_own_address_unsaved: config.json could not be written";
162509
+ return [` note your own address was not saved: ${why}`];
162510
+ }
162511
+ }
162408
162512
  async function offerFundNow(deps, { tor }) {
162409
162513
  const io = deps.io;
162410
162514
  for (let attempt = 0; attempt < 3; attempt += 1) {
162411
162515
  const answer = String(await deps.ask("Fund and shield now? Amount in USDC (Enter to skip): ")).trim();
162412
162516
  if (!answer || /^(n|no|skip)$/i.test(answer)) break;
162413
162517
  const atomic4 = usdcToAtomic(answer);
162414
- if (!atomic4 || BigInt(atomic4) % 1000000n !== 0n) {
162415
- io.err(atomic4 ? "This step takes whole USDC only, like 2 (a relayed deposit is whole USDC, so its amount does not single you out). For less, or cents (from 0.01 USDC), skip here, add the server to your agent host, and ask your agent to add funds (wallet_add_funds). Type a whole number, or press Enter (or n) to skip.\n" : "Type a whole number of USDC, like 2, or press Enter (or n) to skip.\n");
162518
+ if (!atomic4 || BigInt(atomic4) < PAY_ME_MIN_ATOMIC) {
162519
+ io.err(atomic4 ? "The smallest amount is 0.01 USDC. Type an amount like 2 or 0.05, or press Enter (or n) to skip.\n" : "Type an amount of USDC, like 2 or 0.05, or press Enter (or n) to skip.\n");
162416
162520
  continue;
162417
162521
  }
162418
162522
  const argv2 = fundArgvOf(usdcFlagText(atomic4));
@@ -162423,7 +162527,7 @@ async function offerFundNow(deps, { tor }) {
162423
162527
  }
162424
162528
  return;
162425
162529
  }
162426
- io.out(`Not funded now. When you are ready, ask your agent to add funds (wallet_add_funds, from 0.01 USDC), or for whole USDC run: ${fundCommandText(tor)}
162530
+ io.out(`Not funded now. When you are ready, run ${fundCommandText(tor)} (any amount from 0.01 USDC), or ask your agent to add funds.
162427
162531
  `);
162428
162532
  }
162429
162533
  function setupDependencies(deps) {
@@ -162568,7 +162672,7 @@ function setupArgvOf(values) {
162568
162672
  const usdcNames = new Set(COMMAND_BY_NAME.get("setup").flags.filter((spec) => spec.usdc).map((spec) => spec.name));
162569
162673
  const argv2 = [];
162570
162674
  for (const [name, value] of values) {
162571
- if (typeof name !== "string" || !name.startsWith("--")) continue;
162675
+ if (typeof name !== "string" || !name.startsWith("--") || name === "--own-address") continue;
162572
162676
  if (value === true) argv2.push(name);
162573
162677
  else for (const each of Array.isArray(value) ? value : [value]) argv2.push(name, usdcNames.has(name) ? usdcFlagText(String(each)) : String(each));
162574
162678
  }
@@ -162593,21 +162697,33 @@ async function runSetupCommand(values, deps) {
162593
162697
  const home = setupHome(deps.env);
162594
162698
  const interactive = !values.has("--yes");
162595
162699
  const torRunRecords = !values.has("--prefer-tor") && !values.has("--no-tor") && childRunProxy(deps.env).args[0] === "--tor";
162700
+ const terminal = deps.stdinIsTTY() && deps.stdoutIsTTY?.() === true;
162701
+ if (values.has("--own-address") && !terminal) {
162702
+ throw new PublicFailure(`setup_own_address_needs_terminal: your own address lets your agent send money to it without asking, so setup takes it only when you type or paste it in your own terminal (this run has none: a script, a pipe, or an agent's tool); nothing was changed. Run ${oneTimeCommandLine("setup", deps.walletVersion().npm)} in a terminal and paste the address at its question`);
162703
+ }
162704
+ if (values.has("--own-address") && !isOwnAddressText(values.get("--own-address"))) {
162705
+ throw new UsageError(`--own-address takes one Solana address (32 to 44 letters and digits, as your wallet app shows it)${NOT_SHOWN}`, COMMAND_BY_NAME.get("setup"));
162706
+ }
162596
162707
  if (interactive && !deps.stdinIsTTY()) {
162597
162708
  const line = ["setup", "--yes", ...setupArgvOf(values), ...childRunProxy(deps.env).args].map(shellWord).join(" ");
162598
162709
  throw new UsageError(
162599
- `setup asks its questions in a terminal; in a script or a pipe pass --yes: ${oneTimeCommandLine(line, deps.walletVersion().npm)} (add any of --max-payment, --max-session, --max-cumulative, --prefer-tor, --private-inference)`,
162710
+ `setup asks its questions in a terminal; in a script or a pipe pass --yes: ${oneTimeCommandLine(line, deps.walletVersion().npm)} (add any of --max-payment, --max-session, --max-cumulative, --prefer-tor, --private-inference; your own wallet address is asked only in a terminal)`,
162600
162711
  COMMAND_BY_NAME.get("setup")
162601
162712
  );
162602
162713
  }
162714
+ let ownAddress = values.get("--own-address");
162603
162715
  const old = setupConfigOf(home);
162604
162716
  const behindTor = (wanted) => wanted && !values.has("--no-tor") && !inProxiedRun(deps.env);
162605
162717
  if (!interactive && behindTor(values.has("--prefer-tor") || values.has("--private-inference") || old?.tor === true)) {
162606
- return setupBehindTor(setupArgvOf(values), deps);
162718
+ await setupBehindTor(setupArgvOf(values), deps);
162719
+ if (ownAddress) io.out(`${recordOwnAddress(home, ownAddress, deps).join("\n")}
162720
+ `);
162721
+ return void 0;
162607
162722
  }
162608
162723
  const missing = missingPieces(home, old);
162609
162724
  const npmVersion = deps.walletVersion().npm;
162610
162725
  const introduced = values.has("--yes") && inProxiedRun(deps.env);
162726
+ const asksOwnFirst = interactive && Boolean(old) && terminal && !ownAddress;
162611
162727
  io.out([
162612
162728
  ...introduced ? [] : [
162613
162729
  "Shinjuku Shield: a self-custody wallet that pays x402 URLs privately from a shielded USDC balance.",
@@ -162615,7 +162731,7 @@ async function runSetupCommand(values, deps) {
162615
162731
  `An agent host then starts it with one command: npx -y --no-audit --no-fund ${npxPackageWord(npmVersion)} mcp`,
162616
162732
  ""
162617
162733
  ],
162618
- old ? `Status: ${missing.includes("pool-retired") ? `set up before for a retired pool: ${retiredPoolText(old.pool, npmVersion).split("; run ")[0]}. This run moves config.json to the new pool and makes its wallet` : missing.length ? `set up before; missing now: ${missing.join(", ")}` : "set up; every piece is checked again"} (home ${home}).` : `Home: ${home}`,
162734
+ old ? `Status: ${missing.includes("pool-retired") ? `set up before for a retired pool: ${retiredPoolText(old.pool, npmVersion).split("; run ")[0]}. This run moves config.json to the new pool and makes its wallet` : missing.length ? `set up before; missing now: ${missing.join(", ")}` : asksOwnFirst ? "set up" : "set up; every piece is checked again"} (home ${home}).` : `Home: ${home}`,
162619
162735
  ""
162620
162736
  ].join("\n"));
162621
162737
  const options = {
@@ -162646,6 +162762,26 @@ async function runSetupCommand(values, deps) {
162646
162762
  const answer = String(await deps.ask(`Set your LLM to private inference now? ${PRIVATE_INFERENCE_WHY} [y/N] `)).trim();
162647
162763
  privateInference = /^y(es)?$/i.test(answer);
162648
162764
  }
162765
+ if (!ownAddress && terminal) ownAddress = await askOwnAddress(deps);
162766
+ } else if (asksOwnFirst) {
162767
+ ownAddress = await askOwnAddress(deps, old.unshieldTo ?? []);
162768
+ if (!ownAddress && !missing.length) io.out("Every piece of the setup is checked again now.\n");
162769
+ }
162770
+ if (old && ownAddress && !missing.length && !setupArgvOf(values).length && !torRunRecords) {
162771
+ let saved;
162772
+ try {
162773
+ saved = addOwnAddress({ home, address: ownAddress }, setupDependencies(deps));
162774
+ } catch (error) {
162775
+ if (/^setup_own_address_[a-z_]+: /.test(String(error?.message))) throw new PublicFailure(error.message);
162776
+ throw error;
162777
+ }
162778
+ io.out([
162779
+ `Saved: ${ownAddress} is your own address now, so your agent sends money back to you there without asking each time (no restart of your agent app needed).`,
162780
+ ...saved.dropped.length ? [`config.json keeps ${MAX_OWN_ADDRESSES} own addresses: the oldest (${saved.dropped.join(", ")}) now ask before each send.`] : [],
162781
+ "Nothing else changed. To check every piece of the setup again, run setup again and press Enter at the address question.",
162782
+ ""
162783
+ ].join("\n"));
162784
+ return void 0;
162649
162785
  }
162650
162786
  const inference = inferenceRecordOf(old, privateInference);
162651
162787
  if (privateInference === true && (options.tor !== true || torRunRecords) && old?.tor !== true) {
@@ -162663,6 +162799,9 @@ async function runSetupCommand(values, deps) {
162663
162799
  ...privateInference === true && !values.has("--private-inference") ? ["--private-inference"] : []
162664
162800
  ];
162665
162801
  await setupBehindTor(["--yes", ...answers, ...setupArgvOf(values)], deps);
162802
+ if (ownAddress) io.out(`${recordOwnAddress(home, ownAddress, deps).join("\n")}
162803
+
162804
+ `);
162666
162805
  await offerFundNow(deps, { tor: setupConfigOf(home)?.tor === true });
162667
162806
  return void 0;
162668
162807
  }
@@ -162685,6 +162824,7 @@ Copy it to two offline places now; it is the only way to restore this wallet.
162685
162824
  if (/^(setup_[a-z_]+|secret_file_utf16): /.test(String(error?.message))) throw new PublicFailure(error.message);
162686
162825
  throw error;
162687
162826
  }
162827
+ const own = ownAddress ? recordOwnAddress(home, ownAddress, deps) : summary.ownAddresses?.length ? [ownAddressLine(summary.ownAddresses)] : [];
162688
162828
  const lines = [
162689
162829
  "",
162690
162830
  "Shinjuku Shield is set up.",
@@ -162693,7 +162833,8 @@ Copy it to two offline places now; it is the only way to restore this wallet.
162693
162833
  ` passphrase ${summary.passphraseFile}${summary.passphraseGenerated ? " (a new random passphrase; only you can read the file; it is never printed)" : ""}`,
162694
162834
  ` proof tools ${summary.proofTools.folder} (${summary.proofTools.downloaded ? `${summary.proofTools.downloaded} files downloaded, every hash checked` : "already here, every hash checked"})`,
162695
162835
  // The setup warm ended well (setup/setupWarm.mjs); nothing is said when it did not.
162696
- ...summary.firstBalanceReady ? [" balance prepared during setup: your first balance after a deposit shows sooner"] : []
162836
+ ...summary.firstBalanceReady ? [" balance prepared during setup: your first balance after a deposit shows sooner"] : [],
162837
+ ...own
162697
162838
  ];
162698
162839
  if (summary.wallet.status === "created") {
162699
162840
  lines.push(
@@ -162739,9 +162880,12 @@ ${summary.privateInference === true ? privateInferencePath(config, { apiKey }).t
162739
162880
  function validateAddFunds(values) {
162740
162881
  const route = values.get("--route");
162741
162882
  if (!FUNDS_ROUTES.includes(route)) throw new UsageError("--route must be exchange, changenow, or privacy-cash (the value given is not repeated here)");
162883
+ if (BigInt(values.get("--amount")) < PAY_ME_MIN_ATOMIC) {
162884
+ throw new UsageError(`--amount must be at least ${usdcFlagText(PAY_ME_MIN_ATOMIC.toString())} USDC: the facilitator moves no smaller deposit into the pool`);
162885
+ }
162742
162886
  const unit = RELAYED_DEPOSIT_UNIT[values.get("--pool")];
162743
- if (unit && BigInt(values.get("--amount")) % BigInt(unit) !== 0n) {
162744
- throw new UsageError(`--amount must be a whole number of USDC for this pool (a multiple of ${usdcFlagText(unit)} USDC, like 2): a relayed deposit takes whole units only. For an amount with cents (from 0.01 USDC), ask your agent to add funds: the MCP tool wallet_add_funds gives a one-time address that takes any amount`);
162887
+ if (route === "privacy-cash" && unit && BigInt(values.get("--amount")) % BigInt(unit) !== 0n) {
162888
+ throw new UsageError(`--route privacy-cash takes a whole number of USDC (a multiple of ${usdcFlagText(unit)} USDC, like 2): an odd amount singles out your Privacy Cash withdrawal. For an amount with cents (from 0.01 USDC), use --route exchange`);
162745
162889
  }
162746
162890
  if (values.has("--pockets") && !(/^(0|[1-9][0-9]?)$/.test(values.get("--pockets")) && Number(values.get("--pockets")) <= 20)) {
162747
162891
  throw new UsageError("--pockets must be a whole number from 0 to 20");
@@ -162905,7 +163049,7 @@ var COMMANDS = [
162905
163049
  env: [],
162906
163050
  flags: [
162907
163051
  flag2("--route", "<exchange|changenow|privacy-cash>", "where the USDC comes from: an exchange withdrawal, a ChangeNOW swap, or your own wallet through the Privacy Cash crowd", { required: true }),
162908
- flag2("--amount", "<usdc>", "USDC to add: a whole number of USDC (2 = 2 USDC)", { required: true, check: "atomic", usdc: true }),
163052
+ flag2("--amount", "<usdc>", "USDC to add, from 0.01 (2 = 2 USDC, 0.05 = 5 cents); --route privacy-cash takes whole USDC", { required: true, check: "atomic", usdc: true }),
162909
163053
  flag2("--pool", "<address>", "the shielded pool", { required: true, check: "pubkey" }),
162910
163054
  flag2("--program", "<address>", "the shielded pool program", { required: true, check: "pubkey" }),
162911
163055
  flag2("--proof-tools", "<file>", "the public proof-tools manifest (the deposit, the pocket fill, and the balance read use it)", { required: true }),
@@ -162930,6 +163074,7 @@ var COMMANDS = [
162930
163074
  ],
162931
163075
  examples: [
162932
163076
  `${CLI2} add-funds --route exchange --amount 2 ${FUNDS_BASE}`,
163077
+ `${CLI2} add-funds --route exchange --amount 0.05 ${FUNDS_BASE}`,
162933
163078
  `${CLI2} add-funds --route changenow --amount 3 ${FUNDS_BASE}`,
162934
163079
  `${CLI2} add-funds --route exchange --amount 2 --split 2 ${FUNDS_BASE}`,
162935
163080
  `${CLI2} add-funds --route exchange --amount 5 --split auto ${FUNDS_BASE}`,
@@ -162938,14 +163083,15 @@ var COMMANDS = [
162938
163083
  notes: [
162939
163084
  "One command instead of six. It runs the steps you would run by hand, in order, and waits between them by itself: deposit --relay (it records a one-time address of this wallet, then proves the deposit once that address holds the amount), deposit --relay --submit (our relayer moves it into the pool and pays every fee), pocket fill (only with --pockets 1 or more; after the dwell: 3 other pool events after your deposit, or a 1-minute cap, whichever comes first; a balance --refresh right before the fill reads the pool's history first), and balance --refresh. The privacy-cash route first runs privacy-cash deposit, waits its 24-hour hold, and only then records the one-time address and runs privacy-cash withdraw.",
162940
163085
  'Stages, as it prints them: address > funding > deposit > balance > ready, with dwell > pocket before balance when --pockets is 1 or more (privacy-cash: crowd-deposit > crowd-hold > address > crowd-withdraw > funding > ...). Each wait says what it waits for and when it tries again; the last line says "ready" with your balance and what the route still shows. The start line, the funding instructions, and every status line name what the route shows, and to whom. The balance you hold already pays during any wait: add-funds never holds the wallet while it waits.',
162941
- "exchange: withdraw exactly --amount USDC on Solana (SPL) from an exchange to the one-time address it prints. The exchange knows its customer; the chain shows only an exchange paying a new address. Minutes, then the dwell (at most about a minute).",
163086
+ "exchange: withdraw exactly --amount USDC on Solana (SPL) from an exchange to the one-time address it prints (or scan its QR code with a phone wallet). The exchange knows its customer; the chain shows only an exchange paying a new address. Minutes, then the dwell (at most about a minute).",
163087
+ "--amount takes any amount from 0.01 USDC, as the MCP tool wallet_add_funds. A whole number of USDC is the most private: many deposits have the same size. An amount with cents goes in as one deposit, and its size is easier to pick out on chain.",
162942
163088
  "changenow: a FIXED-rate ChangeNOW swap that delivers exactly --amount USDC on Solana to that address, paid with Monero (best: it hides the sender) or BTC or ETH. ChangeNOW sees the amount and the address. Minutes, then the dwell.",
162943
163089
  "privacy-cash: your own wallet (--from-key-file, with about 0.002 SOL) pays --crowd-amount into Privacy Cash; at least 24 hours (add-funds waits by this machine's clock; the withdrawal also checks the chain's time) and 10 real deposits later, Privacy Cash pays the one-time address, so the chain shows no transfer from your wallet to it (about a day; Privacy Cash's server still sees both steps with --via sdk). --crowd-amount is whole USDC and must cover --amount plus Privacy Cash's fee (0.35% plus about 0.72 USDC on 2026-09-30); with --via sdk the change left there must be 10 USDC or more, so a 2 USDC deposit takes --crowd-amount 13. Without --crowd-amount the withdrawal spends notes the key holds there already; the one-time address is read only 24 hours after the later of the newest Privacy Cash deposit this wallet file records (any key) and the moment this run chose the privacy-cash route; a deposit made outside this wallet file is covered only by that second rule. This route's order in add-funds (nothing reads the one-time address before the hold has passed; a random 30 to 120 minutes after a settle-only read) is checked by a run with no money, not yet live end to end.",
162944
163090
  "Until any USDC moved (the one-time address is not funded, and no Privacy Cash deposit was made), a run may change its route: rerun it with the other --route (and that route's flags); the one-time address stays the same. If more than --amount reaches the address, the deposit takes --amount and the rest stays at the address: add-funds and funds-status say how much, and what taking it back shows.",
162945
163091
  "Ctrl-C, a closed terminal, or a reboot loses nothing: every step keeps its own record. Rerun this same command and it continues at the stage where it stopped. To keep it running in the background: nohup <this command> > add-funds.log 2>&1 & (Windows: Start-Process). funds-status shows the stage from any terminal, and whether add-funds still runs.",
162946
163092
  "Back up (backup-export) when it prints the one-time address and again when it says ready: add-funds makes no backup itself. A restore from the recovery text alone still finds the deposit once it landed (recover), and USDC that waits at the one-time address (deposit-scan).",
162947
163093
  "A pocket fill moves your own money into your own pocket, so it takes none of the payment budget init set (--max-payment, --max-cumulative). Each payment from a pocket to a seller takes that budget, the same as any other payment.",
162948
- "It changes no rule: the crowd hold, the dwell, whole-USDC deposits, and every refusal are the single commands' own. A refusal that a later run clears (the hold, the crowd count, the dwell, a paused facilitator) is a wait; any other refusal stops it with that command's own line and hint (exit 1), and the rerun continues after your fix. It has no override flag: run the single command (for example privacy-cash withdraw --hold-override) when you choose to give up a wait.",
163094
+ "It changes no rule: the crowd hold, the dwell, the deposit amounts, and every refusal are the single commands' own. A refusal that a later run clears (the hold, the crowd count, the dwell, a paused facilitator) is a wait; any other refusal stops it with that command's own line and hint (exit 1), and the rerun continues after your fix. It has no override flag: run the single command (for example privacy-cash withdraw --hold-override) when you choose to give up a wait.",
162949
163095
  "--tor (or --proxy): each step runs as its own Tor run, as if you typed it: a new circuit and its own Tor check. With --tor, --service may be the facilitator's http://<v3 onion>/ origin.",
162950
163096
  `--history-rpc: the balance --refresh and pocket fill steps (and the dwell check) read the pool's public history from it (${CLI2} help balance says what it sees); the deposit and Privacy Cash steps never get it. Without it, those two steps read from the history proxy this wallet pins for the pool (its onion with --tor, else https on the facilitator's pay host; --history-rpc own keeps them on your own RPC). add-funds does not keep it: give it again on each rerun. It never falls back to your own RPC: a failed source fails the step with history_rpc_failed, and add-funds handles that as it handles the step's other refusals.`,
162951
163097
  "In a terminal it shows one line per step and ends with a short summary (ready with your balance, or where it stopped and what to do); the wallet's own progress lines (Tor, the RPC relay, the pool history, the memo reads) show only with --verbose, and every warning or failure shows always. With --json, or when its output goes to a file or a program, it ends with the status as JSON, as funds-status prints it, and shows every progress line. While it waits for the USDC it checks the one-time address every 20 s for the first 5 minutes, then every 2 minutes.",
@@ -163562,6 +163708,7 @@ var COMMANDS = [
163562
163708
  bool2("--prefer-tor", "record Tor for mcp: it then runs behind Tor (needs Tor with HTTPTunnelPort 127.0.0.1:9080); mcp --no-tor skips it for one run. Setup itself then runs behind Tor too. setup --tor does the same: it runs setup behind Tor and records Tor for mcp"),
163563
163709
  bool2("--no-tor", "run this setup without Tor although Tor is recorded or asked for (the facilitator then sees this machine's IP address download the release and the proof tools); config.json still records Tor. With --tor it only stops --tor from recording Tor (the run itself still goes over Tor)"),
163564
163710
  bool2("--private-inference", 'answer yes to "Set your LLM to private inference now?": your agent buys AI answers from the AI provider, paid from your private balance over Tor. Records inference.enabled and Tor (as --prefer-tor) in config.json, and prints the path to the first private call. Without it, --yes keeps what config.json records'),
163711
+ flag2("--own-address", "<address>", `optional, in your own terminal only (the same as the answer to setup's address question): your own wallet address (Phantom, Solflare, an exchange's deposit address), for sending money back to yourself: the MCP sends to it without asking each time (as mcp --unshield-to), also in an agent app that cannot show a question. Not this wallet's own address. Refused in a script, a pipe, or an agent's tool (no terminal), also with --yes: an agent must never approve an address. A rerun adds it to the ones config.json records (at most ${MAX_OWN_ADDRESSES})`),
163565
163712
  flag2("--passphrase-file", "<file>", "your own passphrase file (8 or more characters); default: setup writes a new random passphrase to <home>/passphrase.txt, readable by you alone. Needed for a wallet that exists already"),
163566
163713
  flag2("--facilitator", "<https origin>", `the facilitator whose release, pool, and proof tools setup uses; default ${GUIDE.facilitator}`, { check: "origin" }),
163567
163714
  flag2("--add-to", "<claude-code|claude-desktop|cursor>", `also add the server (npx -y --no-audit --no-fund ${NPX_AT_VERSION} mcp: the host starts this npm version; a wallet not installed from npm writes shinjuku-shield without a version) to this agent host's config; repeat it for more. Without it, setup prints the configs`, { repeat: true }),
@@ -163574,7 +163721,7 @@ var COMMANDS = [
163574
163721
  `${CLI2} setup --yes --private-inference`
163575
163722
  ],
163576
163723
  notes: [
163577
- `npx -y --no-audit --no-fund ${COMMAND_AT_VERSION} setup runs the same command. Without --yes it asks in the terminal: the caps (Enter keeps the default), Tor, and private inference.`,
163724
+ `npx -y --no-audit --no-fund ${COMMAND_AT_VERSION} setup runs the same command. Without --yes a first setup asks in the terminal: Tor, private inference, and your own wallet address (Enter skips it). A rerun in a terminal asks only for your own wallet address: on a complete setup an address given there is saved and nothing else runs; Enter checks every piece again.`,
163578
163725
  `When Tor is recorded or asked for (a yes, --tor, --prefer-tor, or --private-inference), setup itself runs behind Tor: the release, the proof-tool downloads, and the RPC requests go over Tor, so the facilitator never sees this machine's IP address make the wallet. config.json then records Tor for mcp ("tor": true), so the MCP runs behind Tor too. Tor must run first (torrc: HTTPTunnelPort 127.0.0.1:9080); --no-tor runs it without Tor.`,
163579
163726
  `Private inference (a yes, or --private-inference): ${PRIVATE_INFERENCE_WHY} Setup records Tor, then prints five steps with real commands: add-funds (it shields the USDC), the first call (echo "Say hello" | ${CLI2} infer -, about 0.0011 USDC; no flags: config.json gives the wallet, the caps, and Tor), infer start for an agent app (the local AI server in the background: base_url and api key), and an MCP restart for inference_chat (and inference_server, which starts the same local AI server from the agent). In a terminal it then offers to run add-funds; it never pays an inference call.`,
163580
163727
  "The wallet home is SHIELDED_WALLET_HOME, default ~/.carbon-shielded-wallet (every command of this CLI uses it). setup writes there: config.json, passphrase.txt (mode 0600; on Windows an access list of your account alone), the wallet (<pool>/wallet.json, made by init with the production pool, program, network, and USDC), a recovery file recovery-<time>.txt, and proof-tools/<dir>/.",
@@ -164216,7 +164363,7 @@ function overviewText() {
164216
164363
  "",
164217
164364
  "Exit codes: 0 done; 1 the command ran and failed (one-line reason); 2 the command line is wrong (shows a working example).",
164218
164365
  `Start here: ${oneTimeCommandLine("setup", npm)} (or ${CLI2} setup), then the agent host runs npx -y --no-audit --no-fund ${npxPackageWord(npm)} mcp, and your agent adds funds with wallet_add_funds (from 0.01 USDC).`,
164219
- `Manual path, without setup: ${CLI2} help init, then ${CLI2} help add-funds (whole USDC only).`,
164366
+ `Manual path, without setup: ${CLI2} help init, then ${CLI2} help add-funds (from 0.01 USDC).`,
164220
164367
  `Lost the wallet file? ${CLI2} help restore (from the init recovery file)`,
164221
164368
  ""
164222
164369
  ].join("\n");
@@ -164329,7 +164476,7 @@ function checkValue(spec, value) {
164329
164476
  if (spec.check === "pubkey") {
164330
164477
  let canonical5 = false;
164331
164478
  try {
164332
- canonical5 = new import_web3138.PublicKey(value).toBase58() === value;
164479
+ canonical5 = new import_web3139.PublicKey(value).toBase58() === value;
164333
164480
  } catch {
164334
164481
  canonical5 = false;
164335
164482
  }
@@ -164492,7 +164639,7 @@ var UNSHIELD_MADE_ID = /* @__PURE__ */ Symbol("unshield id made here");
164492
164639
  var unshieldRefusal = (reason, text3) => new PublicFailure(`p03_unshield_${reason}: ${text3}`);
164493
164640
  function walletOwnAddress(store) {
164494
164641
  try {
164495
- return import_web3138.Keypair.fromSecretKey(Uint8Array.from(store.state.solanaSecretKey), { skipValidation: true }).publicKey;
164642
+ return import_web3139.Keypair.fromSecretKey(Uint8Array.from(store.state.solanaSecretKey), { skipValidation: true }).publicKey;
164496
164643
  } catch {
164497
164644
  return void 0;
164498
164645
  }
@@ -164518,13 +164665,13 @@ async function runUnshieldCommand(args, values, deps, io) {
164518
164665
  };
164519
164666
  let store;
164520
164667
  const openStore = () => store ??= (deps.openWalletStore ?? openWalletStore)(resolve39(values.get("--wallet")), payerPassphrase(values, deps));
164521
- const owner = values.has("--recipient-owner") ? new import_web3138.PublicKey(values.get("--recipient-owner")) : new import_web3138.PublicKey(values.get("--to"));
164668
+ const owner = values.has("--recipient-owner") ? new import_web3139.PublicKey(values.get("--recipient-owner")) : new import_web3139.PublicKey(values.get("--to"));
164522
164669
  if (!values.has("--recipient-owner")) {
164523
164670
  const wallet = openStore();
164524
164671
  if (walletOwnAddress(wallet)?.equals(owner)) {
164525
164672
  throw unshieldRefusal("recipient_owner_is_deposit_key", `--to is this wallet's own public address (its own key): an exit there would show on chain that the money is yours, so this private wallet does not send there; nothing was sent. Send to another address (best: one that never sent USDC into this wallet), like ${UNSHIELD_EXAMPLE}`);
164526
164673
  }
164527
- const mint = new import_web3138.PublicKey(wallet.state.asset);
164674
+ const mint = new import_web3139.PublicKey(wallet.state.asset);
164528
164675
  const check = deps.unshieldRecipientHoldsUsdc ?? ((input) => recipientHoldsUsdc({ ...input, connection: connectRelay(spendRpcUrl(values, deps), "finalized") }));
164529
164676
  let holds;
164530
164677
  try {
@@ -165137,19 +165284,22 @@ var RELAYED_DEPOSIT_UNIT = Object.freeze({
165137
165284
  CDtxyRjCUs2pu7t3U9TwtpnZwyxBFZ4dKXvb4RCmXHXv: "1000000"
165138
165285
  });
165139
165286
  async function runDeposit(args, values, deps) {
165140
- const unit = values.has("--relay") && !values.has("--pool-denomination") ? RELAYED_DEPOSIT_UNIT[values.get("--pool")] : void 0;
165287
+ const poolUnit = RELAYED_DEPOSIT_UNIT[values.get("--pool")];
165288
+ const amount = String(values.get("--amount") ?? "");
165289
+ const offUnit = poolUnit !== void 0 && /^[0-9]{1,20}$/.test(amount) && BigInt(amount) % BigInt(poolUnit) !== 0n;
165290
+ const unit = values.has("--relay") && !values.has("--pool-denomination") && !offUnit ? poolUnit : void 0;
165141
165291
  try {
165142
165292
  await deps.researchWalletMain(["deposit", ...args, ...unit ? ["--pool-denomination", unit] : []]);
165143
165293
  } catch (error) {
165144
165294
  if (error?.message !== "p03_deposit_relay_fund_one_time_address") throw error;
165145
165295
  let address5;
165146
165296
  try {
165147
- address5 = new import_web3138.PublicKey(error.oneTimeAddress).toBase58();
165297
+ address5 = new import_web3139.PublicKey(error.oneTimeAddress).toBase58();
165148
165298
  } catch {
165149
165299
  throw error;
165150
165300
  }
165151
165301
  const decimals = Number(error.decimals ?? 6);
165152
- const asset = error.asset === GUIDE.mint ? "USDC" : `tokens of mint ${new import_web3138.PublicKey(error.asset).toBase58()}`;
165302
+ const asset = error.asset === GUIDE.mint ? "USDC" : `tokens of mint ${new import_web3139.PublicKey(error.asset).toBase58()}`;
165153
165303
  const held = BigInt(error.heldAtomic);
165154
165304
  const unitAtomic = BigInt(values.get("--pool-denomination") ?? unit ?? "1");
165155
165305
  const usable = held / unitAtomic * unitAtomic;
@@ -165321,6 +165471,12 @@ async function runBalance(args, values, deps, io) {
165321
165471
  source: values.has("--refresh") ? "refresh" : "local",
165322
165472
  now: deps.now()
165323
165473
  });
165474
+ const refreshArgs = values.has("--refresh") || report.status === "current" || report.status === "empty" ? void 0 : homeRefreshArgs(store, deps);
165475
+ if (refreshArgs) {
165476
+ io.err("reading your private balance from the chain\n");
165477
+ const shownFlags = ["--json", "--verbose"].filter((name) => values.has(name));
165478
+ if (await runShinjukuWallet(["balance", ...refreshArgs, ...shownFlags], deps) === 0) return;
165479
+ }
165324
165480
  const shown = earlierDeposits.map((outcome) => ({
165325
165481
  pool: outcome.pool,
165326
165482
  state: outcome.state,
@@ -165351,6 +165507,16 @@ async function runBalance(args, values, deps, io) {
165351
165507
  io.out(`${JSON.stringify({ ...report, ...shown.length ? { earlierDeposits: shown } : {}, ...unfinished2.length ? { unfinishedUnshields: unfinished2 } : {} }, null, 2)}
165352
165508
  `);
165353
165509
  }
165510
+ function homeRefreshArgs(store, deps) {
165511
+ let config;
165512
+ try {
165513
+ config = readSetupConfig(setupHome(deps.env));
165514
+ } catch {
165515
+ return void 0;
165516
+ }
165517
+ if (config?.pool !== store.state.pool || !config.program || !config.proofTools || !config.memoService) return void 0;
165518
+ return ["--pool", config.pool, "--refresh", "--program", config.program, "--proof-tools", config.proofTools, "--service", config.memoService, "--fresh"];
165519
+ }
165354
165520
  async function adoptOneTimeForBalance({ store, values, deps, io }) {
165355
165521
  try {
165356
165522
  const home = setupHome(deps.env);