shinjuku-shielded 0.3.14 → 0.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/manifest.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema": "carbon-shielded-wallet/bundle-manifest/v1",
3
3
  "file": "shinjuku-wallet.mjs",
4
- "sha256": "db079e3700b17388873252fb91358d1918801ff69d1ee26f4e3e9f21bccbab34",
5
- "bytes": 11121670,
6
- "sourceCommit": "181724d44e72d6b49f062fb641761811e66e23a6",
4
+ "sha256": "b32bffc727e1e7e33fb134a3918638006259033511e1a2aaf455d037da1f70b3",
5
+ "bytes": 11124201,
6
+ "sourceCommit": "4b3c69bd892409a11b931e3a01f254a8b2f4af0f",
7
7
  "sourceDirty": false,
8
8
  "installRecordMatchesLockfile": true,
9
9
  "releaseRule": "valid only when two fresh npm ci --ignore-scripts builds on different OSes give the same sha256; installRecordMatchesLockfile true means every entry npm installed equals its package-lock.json entry and every entry not marked optional, devOptional included, is installed: an optional entry (a package for another platform) may be absent",
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
4
4
  "mcpName": "io.github.ShinjukuStaition/shinjuku-mcp",
5
5
  "description": "Shinjuku Shielded, the private x402 facilitator on Solana: the self-custody wallet CLI and its MCP server. Shield, pay, and unshield USDC from any MCP agent, under caps you set.",
6
- "shinjukuRelease": "db079e37",
7
- "shinjukuSha256": "db079e3700b17388873252fb91358d1918801ff69d1ee26f4e3e9f21bccbab34",
6
+ "shinjukuRelease": "b32bffc7",
7
+ "shinjukuSha256": "b32bffc727e1e7e33fb134a3918638006259033511e1a2aaf455d037da1f70b3",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -58607,9 +58607,6 @@ async function prepareRelayedDepositLeased({ store, connection, programId, proof
58607
58607
  const minimum = closing ? config.closingSourceMinimumAtomic : pocketIndex !== void 0 && config.pocketMinimumAtomic !== void 0 ? config.pocketMinimumAtomic : config.minimumAtomic;
58608
58608
  if (legacySource && amount < minimum) throw legacyBelowMinimum(legacySource, amount, minimum);
58609
58609
  if (amount < minimum) fail11(pocketIndex !== void 0 && config.pocketMinimumAtomic === void 0 ? "relay_pocket_reshield_not_offered" : "relay_amount_below_facilitator_minimum");
58610
- if (!record && pocketIndex === void 0 && !legacySource && config.depositUnitAtomic !== void 0 && amount % config.depositUnitAtomic !== 0n) {
58611
- fail11("relay_amount_not_whole_unit");
58612
- }
58613
58610
  const pool = await poolState(connection, id);
58614
58611
  if (amount > pool.maxDepositValue) fail11("amount_above_pool_deposit_cap");
58615
58612
  if (BigInt(pool.depositCount) >= BigInt(pool.maxDeposits)) fail11("pool_at_deposit_cap");
@@ -58713,7 +58710,6 @@ async function readRelayConfig({ serviceUrl: serviceUrl2, fetchImpl, id }) {
58713
58710
  minimumAtomic: BigInt(config.minimumAtomic),
58714
58711
  closesSource: config.sourceCloseAuthority !== void 0,
58715
58712
  ...config.pocketMinimumAtomic === void 0 ? {} : { pocketMinimumAtomic: BigInt(config.pocketMinimumAtomic) },
58716
- ...config.depositUnitAtomic === void 0 ? {} : { depositUnitAtomic: BigInt(config.depositUnitAtomic) },
58717
58713
  ...config.closingSourceMinimumAtomic === void 0 ? {} : { closingSourceMinimumAtomic: BigInt(config.closingSourceMinimumAtomic) }
58718
58714
  };
58719
58715
  }
@@ -61876,6 +61872,8 @@ var init_errorCodes = __esm({
61876
61872
  "mcp_unshield_to_not_allowed",
61877
61873
  "mcp_unshield_to_invalid",
61878
61874
  "mcp_shield_cap_exceeded",
61875
+ "mcp_shield_above_balance_cap",
61876
+ "mcp_balance_unknown",
61879
61877
  "mcp_unshield_cap_exceeded",
61880
61878
  "mcp_request_id_reused",
61881
61879
  "mcp_shield_failed",
@@ -62362,6 +62360,7 @@ var init_errorCodes = __esm({
62362
62360
  "float_not_ready",
62363
62361
  "float_seller_uses_our_facilitator",
62364
62362
  "float_payment_in_flight",
62363
+ "float_quote_open",
62365
62364
  "float_already_paid",
62366
62365
  "float_paid",
62367
62366
  "p03_payer_float_wallet_pool_mismatch",
@@ -90984,9 +90983,9 @@ async function payViaFloat({
90984
90983
  });
90985
90984
  } catch (error) {
90986
90985
  if (seen.refusal) {
90987
- if (seen.refusal.code === "float_payment_in_flight" && !regenerated && innerUnsent()) {
90986
+ if (REGENERATE_CODES.has(seen.refusal.code) && !regenerated && innerUnsent()) {
90988
90987
  regenerated = true;
90989
- const refundTo = await ownRefundAddress(store, connection);
90988
+ const refundTo = await (deps.ownRefundAddress ?? ownRefundAddress)(store, connection);
90990
90989
  const body = canonicalBody({ requestId, resourceUrl: record.url, requirement: record.requirement, refundTo });
90991
90990
  record = { ...record, floatId: body.floatId, bodyBase64: body.bodyBase64, refundTo };
90992
90991
  store.writeWal(walName2, record);
@@ -91090,7 +91089,7 @@ function paidResult(record, replayed) {
91090
91089
  float: { floatId: record.floatId }
91091
91090
  };
91092
91091
  }
91093
- var import_web3109, FLOAT_PAYMENT_SCHEMA, FLOAT_PAYMENT_WAL_PREFIX, FLOAT_CONFIG_SCHEMA, FLOAT_PAY_PATH, MAX_BODY_BYTES, FLOAT_REFUND_ETA_SECONDS, REFUND_SETTLED_AFTER_MS, FINISHING_POLL_MS, FINISHING_LIMIT_MS, SELLER_TIMEOUT_MS, CONFIG_TIMEOUT_MS, DECIMAL3, POSITIVE, PENDING_CODES, hash9, fail18, floatPaymentWalName, plainText;
91092
+ var import_web3109, FLOAT_PAYMENT_SCHEMA, REGENERATE_CODES, FLOAT_PAYMENT_WAL_PREFIX, FLOAT_CONFIG_SCHEMA, FLOAT_PAY_PATH, MAX_BODY_BYTES, FLOAT_REFUND_ETA_SECONDS, REFUND_SETTLED_AFTER_MS, FINISHING_POLL_MS, FINISHING_LIMIT_MS, SELLER_TIMEOUT_MS, CONFIG_TIMEOUT_MS, DECIMAL3, POSITIVE, PENDING_CODES, hash9, fail18, floatPaymentWalName, plainText;
91094
91093
  var init_floatPay = __esm({
91095
91094
  "wallet/src/pay/floatPay.mjs"() {
91096
91095
  "use strict";
@@ -91107,6 +91106,7 @@ var init_floatPay = __esm({
91107
91106
  init_proxy();
91108
91107
  init_payResource();
91109
91108
  FLOAT_PAYMENT_SCHEMA = "carbon-shielded-wallet/float-payment/v1";
91109
+ REGENERATE_CODES = /* @__PURE__ */ new Set(["float_payment_in_flight", "float_quote_open"]);
91110
91110
  FLOAT_PAYMENT_WAL_PREFIX = "float-";
91111
91111
  FLOAT_CONFIG_SCHEMA = "carbon-shielded-x402/float-config/v1";
91112
91112
  FLOAT_PAY_PATH = "/api/x402/float/pay/";
@@ -118735,6 +118735,8 @@ function snapshotFromRecovery({ state, programId, recovery, history, walStates,
118735
118735
  });
118736
118736
  const slot3 = history?.finalizedPoolSnapshot?.slot;
118737
118737
  if (slot3 === void 0 || !canonicalAtomic(String(slot3))) throw new Error("balance_snapshot_slot_invalid");
118738
+ const anchorIndexes = Array.isArray(history?.finalizedPoolSnapshot?.anchors) ? history.finalizedPoolSnapshot.anchors.map((anchor) => BigInt(anchor.index)) : [];
118739
+ const anchorIndex = anchorIndexes.length ? anchorIndexes.reduce((a, b) => b > a ? b : a).toString() : void 0;
118738
118740
  return {
118739
118741
  schema: BALANCE_SNAPSHOT_SCHEMA,
118740
118742
  pool: state.pool,
@@ -118742,6 +118744,7 @@ function snapshotFromRecovery({ state, programId, recovery, history, walStates,
118742
118744
  refreshedAt: new Date(now).toISOString(),
118743
118745
  slot: String(slot3),
118744
118746
  poolIndex: String(recovery.poolIndex),
118747
+ ...anchorIndex === void 0 ? {} : { anchorIndex },
118745
118748
  fresh: fresh === true,
118746
118749
  walStates: { ...walStates },
118747
118750
  outputs
@@ -119021,7 +119024,8 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
119021
119024
  const reservedAtomic = unknownInputs ? unspentAtomic : sum2([...reserved].filter((commitment) => unspent.has(commitment)).map((commitment) => unspent.get(commitment).valueAtomic));
119022
119025
  detail.reservedAtomic = reservedAtomic.toString();
119023
119026
  const spendable = unspentAtomic - reservedAtomic;
119024
- detail.fundedAccounts = unknownInputs ? 0 : [...unspent.values()].filter((output2) => output2.kind === "account" && BigInt(output2.valueAtomic) > 0n && !reserved.has(output2.commitment)).length;
119027
+ const anchorIndex = canonicalAtomic(snapshot.anchorIndex) ? BigInt(snapshot.anchorIndex) : void 0;
119028
+ detail.fundedAccounts = unknownInputs ? 0 : [...unspent.values()].filter((output2) => output2.kind === "account" && BigInt(output2.valueAtomic) > 0n && !reserved.has(output2.commitment) && (anchorIndex === void 0 || BigInt(output2.leafIndex) < anchorIndex)).length;
119025
119029
  const next = detail.inFlight.length ? "finish or cancel the work in detail.inFlight; the wallet runs one payment at a time" : pendingDeposits.length ? `deposit ${pendingDeposits[0].depositId} is not finished: rerun the same deposit command with --submit --service <facilitator URL>` : pocketsUnscanned ? scanPockets2 : unspentAtomic === 0n && successionMovableAtomic > 0n ? "this pool was succeeded by a new pool: its value moves there (a move per account, the facilitator pays the fee) and counts in this balance until then" : unspentAtomic === 0n ? "nothing to spend: deposit (shinjuku-wallet help deposit), or run balance --refresh if a payment to this wallet settled" : void 0;
119026
119030
  return report("current", spendable + successionMovableAtomic, next);
119027
119031
  }
@@ -124142,6 +124146,7 @@ function writeCache(key, record, ctx) {
124142
124146
  // wallet/src/mcp/tools.mjs
124143
124147
  init_pool_denomination();
124144
124148
  init_serviceUrl();
124149
+ init_anchorClock();
124145
124150
  var shownCaps = (caps) => Object.fromEntries(Object.entries(caps).map(([name, value]) => [name, String(value) === NO_LIMIT_ATOMIC2 ? "none" : value]));
124146
124151
  var RESOURCE_PAYMENT_WAL_PREFIX2 = "resource-payment-";
124147
124152
  var SELF_PAY_ORIGIN3 = "https://self-pay.invalid/";
@@ -124406,7 +124411,7 @@ async function readOwnPublicBalance({ session, store }) {
124406
124411
  solLamports: String(solAccount?.lamports ?? 0)
124407
124412
  };
124408
124413
  }
124409
- async function runShield({ session, depositId, amountAtomic, unitAtomic, onStage, steps = {} }) {
124414
+ async function runShield({ session, depositId, amountAtomic, onStage, steps = {} }) {
124410
124415
  const store = session.openStore();
124411
124416
  const existing = store.readWal(`deposit-${depositId}`);
124412
124417
  if (existing && !existing.relay) return runSelfBroadcastShield({ session, depositId, onStage });
@@ -124424,9 +124429,8 @@ async function runShield({ session, depositId, amountAtomic, unitAtomic, onStage
124424
124429
  ...common,
124425
124430
  amountAtomic,
124426
124431
  ownKey: true,
124427
- // The CLI deposit's own admission check (shielded-wallet.mjs runSelfCustodyDeposit), with the pool's deposit unit.
124432
+ // The CLI deposit's own admission check (shielded-wallet.mjs runSelfCustodyDeposit); any amount (no deposit unit).
124428
124433
  checkAmount: ({ amount, pool, sourceAmount, vaultAmount }) => resolveAgentDepositAmount({
124429
- denominationAtomic: unitAtomic,
124430
124434
  requestedAtomic: amount,
124431
124435
  walletBalanceAtomic: sourceAmount,
124432
124436
  maxDepositValueAtomic: pool.maxDepositValue,
@@ -124469,7 +124473,7 @@ var usdcText = (atomic3, decimals = 6) => {
124469
124473
  const text3 = formatAtomic(BigInt(atomic3), decimals);
124470
124474
  return text3.includes(".") ? text3.replace(/0+$/, "").replace(/\.$/, "") : text3;
124471
124475
  };
124472
- async function runShieldFromElsewhere({ session, depositId, amountAtomic, unitAtomic, onStage, steps = {} }) {
124476
+ async function runShieldFromElsewhere({ session, depositId, amountAtomic, onStage, steps = {} }) {
124473
124477
  const store = session.openStore();
124474
124478
  const common = {
124475
124479
  store,
@@ -124486,9 +124490,8 @@ async function runShieldFromElsewhere({ session, depositId, amountAtomic, unitAt
124486
124490
  await (steps.prepare ?? prepareRelayedDeposit)({
124487
124491
  ...common,
124488
124492
  amountAtomic,
124489
- // The CLI deposit --relay admission check, with the pool's deposit unit; the source is the one-time address.
124493
+ // The CLI deposit --relay admission check (any amount, no deposit unit); the source is the one-time address.
124490
124494
  checkAmount: ({ amount, pool, sourceAmount, vaultAmount }) => resolveAgentDepositAmount({
124491
- denominationAtomic: unitAtomic,
124492
124495
  requestedAtomic: amount,
124493
124496
  walletBalanceAtomic: sourceAmount,
124494
124497
  maxDepositValueAtomic: pool.maxDepositValue,
@@ -124716,10 +124719,13 @@ function localBalance(session) {
124716
124719
  return null;
124717
124720
  }
124718
124721
  }
124722
+ var CAP_BALANCE_WAIT_MS = 35e3;
124723
+ var CAP_BALANCE_RETRY_MS = 5e3;
124719
124724
  var REFRESH_RETRY_MS = 6e4;
124720
124725
  var SWITCH_WAIT_MS = 4e4;
124721
124726
  var ELICIT_TIMEOUT_MS = 5 * 6e4;
124722
124727
  var POLL_SECONDS2 = 30;
124728
+ var MAX_SHIELDED_BALANCE_ATOMIC = 5000000n;
124723
124729
  function createWalletTools({
124724
124730
  session,
124725
124731
  policy,
@@ -124731,7 +124737,9 @@ function createWalletTools({
124731
124737
  client,
124732
124738
  exportBackup,
124733
124739
  balanceRefreshWaitMs = BALANCE_REFRESH_WAIT_MS,
124734
- elicitTimeoutMs = ELICIT_TIMEOUT_MS
124740
+ elicitTimeoutMs = ELICIT_TIMEOUT_MS,
124741
+ capBalanceWaitMs = CAP_BALANCE_WAIT_MS,
124742
+ capBalanceRetryMs = CAP_BALANCE_RETRY_MS
124735
124743
  }) {
124736
124744
  const outDir = join26(dirname23(session.walletPath), "mcp-out");
124737
124745
  const knownIds = /* @__PURE__ */ new Map();
@@ -125565,6 +125573,19 @@ function createWalletTools({
125565
125573
  const again = (kind, job) => `call ${KINDS[kind].tool} again with request_id "${job.requestId}", amount_atomic "${job.amountAtomic}"${job.to ? `, and to "${job.to}"` : ""}`;
125566
125574
  const unboundNext = (requestId, amountAtomic) => `Nothing was signed. Do not retry this request_id. Tell your owner: the self-payment of this unshield has no recipient bound to it, so this server will not choose one. The owner finishes it with the CLI: shinjuku-wallet unshield (the same --wallet, proof tools, --profile, --program, --facilitator) --unshield-id ${moneyId(requestId)} --amount ${amountAtomic} --to <recipient USDC account> --recipient-owner <recipient owner>`;
125567
125575
  const preApproved = () => [...new Set(unshieldTo)];
125576
+ async function anchorWaitAfterDeposit() {
125577
+ try {
125578
+ const read = await underLease((store) => readBalanceSnapshot(store));
125579
+ const anchorIndex = read.snapshot?.anchorIndex;
125580
+ if (anchorIndex === void 0) return {};
125581
+ const waiting = read.snapshot.outputs.some((output2) => output2.kind === "account" && output2.spendStatus === "unspent" && BigInt(output2.leafIndex) >= BigInt(anchorIndex));
125582
+ if (!waiting) return {};
125583
+ noteAnchorOrigin(session.memoServiceUrl);
125584
+ return { spendableAtMs: Date.now() + nextAnchorWaitMs(Date.now(), 0) };
125585
+ } catch {
125586
+ return {};
125587
+ }
125588
+ }
125568
125589
  function doneNext(kind, job) {
125569
125590
  if (kind === "unshield") {
125570
125591
  return `the USDC was sent to ${job.to}; tell the user (exitTransaction). wallet_balance shows the new shielded balance${job.result?.backupPath ? `. An encrypted backup of the wallet was saved at ${job.result.backupPath}` : ""}`;
@@ -125574,6 +125595,9 @@ function createWalletTools({
125574
125595
  return `the deposit landed. The wallet now reads it from the chain so it becomes spendable: call wallet_balance in ${POLL_SECONDS2} s (x402_pay and wallet_unshield also wait for it by themselves)`;
125575
125596
  }
125576
125597
  if (!post.refreshed) return `the deposit landed; reading it from the chain failed (${post.refreshError}): call wallet_balance in a minute, it retries by itself`;
125598
+ if (post.spendableAtMs !== void 0 && post.spendableAtMs > Date.now()) {
125599
+ return `the deposit landed and is in the shielded balance. It is spendable after the next pool anchor, in about ${Math.ceil((post.spendableAtMs - Date.now()) / 1e3)} s: x402_pay and wallet_unshield wait for it by themselves, so call them now if the user wants to${post.backupPath ? `. An encrypted backup of the wallet was saved at ${post.backupPath}` : ""}`;
125600
+ }
125577
125601
  return `the deposit landed and is in the shielded balance: x402_pay and wallet_unshield can spend it now${post.backupPath ? `. An encrypted backup of the wallet was saved at ${post.backupPath}` : ""}`;
125578
125602
  }
125579
125603
  const ELSEWHERE_PRIVACY = "the chain shows the other wallet paying this new one-time address, and that address depositing this amount into the pool (both public; the facilitator also sees the amount and time). Later payments from the shielded balance do not show which deposit paid them. With few depositors in the pool, an amount few other deposits share, or a payment soon after the deposit, is easier to pair";
@@ -125691,8 +125715,6 @@ function createWalletTools({
125691
125715
  else if (/another_deposit_unresolved/.test(code)) {
125692
125716
  const waiting = waitingElsewhereIds();
125693
125717
  next = waiting.length > 0 ? `Nothing new was moved. A wallet_shield_from_elsewhere address of this wallet still waits for its USDC (request_id ${waiting.map((id) => `"${id}"`).join(", ")}), and the wallet runs one deposit at a time. Either fund it and call wallet_shield_from_elsewhere again with that request_id until it is done, or, if nothing was sent to it, close it: call wallet_shield_from_elsewhere with action "cancel" and request_id "${waiting[0]}" (it refuses when any USDC or any transaction already reached the address; then only the owner's CLI deposit-reclaim frees it). Then call this one again` : `Nothing new was moved. Another deposit of this wallet is not finished (a wallet_shield, or a wallet_shield_from_elsewhere): call that tool again with its request_id until it is done, then call this one again. If no tool knows it, tell your owner: ${code} (the CLI's deposit records show which one)`;
125694
- } else if (/relay_amount_not_whole_unit/.test(code)) {
125695
- next = `Nothing was moved and no address was made. This pool takes deposits in whole units of its pocket size (the facilitator's /api/x402/deposit shows depositUnitAtomic): call ${KINDS[kind].tool} with a NEW request_id and amount_atomic a whole multiple of it`;
125696
125718
  } else if (kind === "shield" && /relay_amount_below_facilitator_minimum/.test(code)) {
125697
125719
  next = "Nothing was moved. The facilitator does not relay a deposit this small: call wallet_shield with a NEW request_id and a larger amount_atomic";
125698
125720
  } else if (kind === "shield" && /relay_not_offered|relay_config_mismatch|relay_config_invalid|relay_service_required|proof_tools_missing/.test(code)) {
@@ -125730,13 +125752,6 @@ function createWalletTools({
125730
125752
  if (typeof amount !== "string" || !POSITIVE_ATOMIC4.test(amount)) {
125731
125753
  return { refusal: ["mcp_argument_invalid", "amount_atomic must be a positive whole number of atomic units (USDC: 1000000 = 1 USDC)", 'pass amount_atomic as a string, like "1000000"'] };
125732
125754
  }
125733
- if (kind !== "unshield" && money.depositUnitAtomic && BigInt(amount) % BigInt(money.depositUnitAtomic) !== 0n) {
125734
- return { refusal: [
125735
- "mcp_argument_invalid",
125736
- `amount_atomic must be a whole multiple of ${money.depositUnitAtomic} on this pool (whole deposit units); nothing was moved`,
125737
- `pass a multiple of ${money.depositUnitAtomic}, like ${money.depositUnitAtomic}`
125738
- ] };
125739
- }
125740
125755
  if (kind === "unshield") {
125741
125756
  let canonical5 = false;
125742
125757
  try {
@@ -125902,6 +125917,41 @@ function createWalletTools({
125902
125917
  const detail = `this MCP session already moved ${total} of its ${spec.capFlag} ${max}; ${amount} more is above it`;
125903
125918
  return refuse9(spec.capCode, `${detail}; nothing ${record ? "more " : ""}was moved`, ownerNext(detail), { requestId: request.requestId });
125904
125919
  }
125920
+ if (kind !== "unshield" && !record) {
125921
+ let stale;
125922
+ try {
125923
+ stale = shieldedUnknown(session.openStore());
125924
+ } catch {
125925
+ stale = true;
125926
+ }
125927
+ let one = stale ? null : localBalance(session);
125928
+ const unknown = () => one?.balanceAtomic === null || one?.balanceAtomic === void 0;
125929
+ for (const deadline = Date.now() + capBalanceWaitMs; unknown(); ) {
125930
+ await refreshShared().catch(() => {
125931
+ });
125932
+ one = localBalance(session);
125933
+ if (!unknown() || Date.now() + capBalanceRetryMs > deadline) break;
125934
+ await new Promise((resolve36) => setTimeout(resolve36, capBalanceRetryMs));
125935
+ }
125936
+ if (unknown()) {
125937
+ return refuse9(
125938
+ "mcp_balance_unknown",
125939
+ `the shielded balance could not be read just now (a payment from moments ago is not final yet, the chain read failed, or another wallet command holds this wallet), so the wallet cannot check the ${usdcText(MAX_SHIELDED_BALANCE_ATOMIC)} USDC balance cap; nothing was moved`,
125940
+ `call ${spec.tool} again with the same request_id and amount_atomic in ${POLL_SECONDS2} s`,
125941
+ { requestId: request.requestId }
125942
+ );
125943
+ }
125944
+ const room = MAX_SHIELDED_BALANCE_ATOMIC - BigInt(one.balanceAtomic);
125945
+ if (BigInt(request.amountAtomic) > room) {
125946
+ const left = room > 0n ? room : 0n;
125947
+ return refuse9(
125948
+ "mcp_shield_above_balance_cap",
125949
+ `the shielded balance may hold at most ${usdcText(MAX_SHIELDED_BALANCE_ATOMIC)} USDC; it holds ${usdcText(one.balanceAtomic)} USDC, so at most ${usdcText(left)} USDC more can be shielded now; nothing was moved`,
125950
+ left > 0n ? `Tell the user the cap. Call ${spec.tool} with a NEW request_id and amount_atomic at most "${left}"` : "Tell the user the shielded balance is at its cap: spend some with x402_pay, or send some out with wallet_unshield, before shielding more",
125951
+ { requestId: request.requestId, balanceAtomic: one.balanceAtomic, maxBalanceAtomic: MAX_SHIELDED_BALANCE_ATOMIC.toString(), roomAtomic: left.toString() }
125952
+ );
125953
+ }
125954
+ }
125905
125955
  if (!deferCount) {
125906
125956
  moved[spec.counter] += amount;
125907
125957
  counted.set(key, amount);
@@ -126007,7 +126057,6 @@ function createWalletTools({
126007
126057
  session,
126008
126058
  depositId: moneyId(job.requestId),
126009
126059
  amountAtomic: job.amountAtomic,
126010
- unitAtomic: money.depositUnitAtomic,
126011
126060
  onStage,
126012
126061
  steps: { prepare: ops.prepareOwnRelayed, submit: ops.submitOwnRelayed }
126013
126062
  });
@@ -126017,14 +126066,16 @@ function createWalletTools({
126017
126066
  session,
126018
126067
  depositId: elsewhereId(job.requestId),
126019
126068
  amountAtomic: job.amountAtomic,
126020
- unitAtomic: money.depositUnitAtomic,
126021
126069
  onStage,
126022
126070
  steps: { prepare: ops.prepareRelayed, submit: ops.submitRelayed }
126023
126071
  });
126024
126072
  }
126025
126073
  function afterShield(job) {
126026
126074
  job.post = { state: "running" };
126027
- job.postPromise = refreshShared().then(() => ({ refreshed: true }), (error) => ({ refreshed: false, refreshError: errorCode2(error, "mcp_refresh_failed") })).then((refresh) => underLease((store) => backupNow(store)).catch((error) => typeof exportBackup === "function" ? { backupPath: null, backupError: errorCode2(error, "wallet_locked") } : {}).then((backup) => {
126075
+ job.postPromise = refreshShared().then(
126076
+ () => anchorWaitAfterDeposit().then((wait2) => ({ refreshed: true, ...wait2 })),
126077
+ (error) => ({ refreshed: false, refreshError: errorCode2(error, "mcp_refresh_failed") })
126078
+ ).then((refresh) => underLease((store) => backupNow(store)).catch((error) => typeof exportBackup === "function" ? { backupPath: null, backupError: errorCode2(error, "wallet_locked") } : {}).then((backup) => {
126028
126079
  job.post = { state: "done", ...refresh, ...backup };
126029
126080
  })).finally(() => {
126030
126081
  if (money.autoPockets === true) autoPockets.onShielded();
@@ -126158,11 +126209,11 @@ function createWalletTools({
126158
126209
  const shield = {
126159
126210
  name: "wallet_shield",
126160
126211
  title: "Shield USDC (make it private)",
126161
- description: `Use this when the user says "shield 5 USDC", "make my USDC private", or "add funds to my shielded balance": it moves amount_atomic of the plain public USDC on this wallet's own Solana key (wallet_balance unshieldedAtomic) into the SHIELDED balance, which x402_pay and wallet_unshield spend. The USDC stays the user's. Cost: no SOL. The wallet's key only signs an approval of the amount; the facilitator's relayer submits the deposit and pays every network fee and rent. The facilitator relays deposits of at least its minimum (1 USDC by default). Privacy: the chain shows this key depositing this amount; later payments from the shielded balance do not show this wallet. It takes a few minutes: the first call starts it and answers stage (proving, submitting, done); call wallet_shield again with the SAME request_id and amount_atomic in ${POLL_SECONDS2} s for the status. When the deposit lands, the wallet reads it from the chain by itself (refreshing: true until it is spendable) and writes an encrypted backup (backupPath). The same request_id never deposits twice, also after a restart. One deposit at a time. An optional owner cap (--max-shield) may bound each call and this session (mcp_shield_cap_exceeded: tell the owner). Refusals: mcp_argument_invalid, mcp_request_id_reused, wallet_locked, mcp_tool_disabled, mcp_shield_failed, and the deposit's own p03_deposit_* codes.`,
126212
+ description: `Use this when the user says "shield 5 USDC", "make my USDC private", or "add funds to my shielded balance": it moves amount_atomic of the plain public USDC on this wallet's own Solana key (wallet_balance unshieldedAtomic) into the SHIELDED balance, which x402_pay and wallet_unshield spend. The USDC stays the user's. Cost: no SOL. The wallet's key only signs an approval of the amount; the facilitator's relayer submits the deposit and pays every network fee and rent. Any amount from 0.01 USDC (amount_atomic 10000; the facilitator's minimum) works, cents included, while the shielded balance stays at most 5 USDC (mcp_shield_above_balance_cap says how much more fits). Privacy: the chain shows this key depositing this amount; later payments from the shielded balance do not show this wallet. It takes a few minutes: the first call starts it and answers stage (proving, submitting, done); call wallet_shield again with the SAME request_id and amount_atomic in ${POLL_SECONDS2} s for the status. When the deposit lands, the wallet reads it from the chain by itself (refreshing: true until it is spendable) and writes an encrypted backup (backupPath). The same request_id never deposits twice, also after a restart. One deposit at a time. An optional owner cap (--max-shield) may bound each call and this session (mcp_shield_cap_exceeded: tell the owner). Refusals: mcp_argument_invalid, mcp_request_id_reused, wallet_locked, mcp_tool_disabled, mcp_shield_failed, and the deposit's own p03_deposit_* codes.`,
126162
126213
  inputSchema: {
126163
126214
  type: "object",
126164
126215
  properties: {
126165
- amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to shield, in atomic units (1000000 = 1 USDC). A whole number of USDC on this service's pools." },
126216
+ amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to shield, in atomic units (1000000 = 1 USDC, 370000 = 0.37 USDC): any amount from 10000 (0.01 USDC) while the balance stays at most 5 USDC." },
126166
126217
  request_id: { type: "string", pattern: "^[A-Za-z0-9_-]{1,44}$", description: "Your stable name for this deposit, like shield-1; pass it again to read the status." }
126167
126218
  },
126168
126219
  required: ["amount_atomic", "request_id"],
@@ -126195,7 +126246,7 @@ function createWalletTools({
126195
126246
  inputSchema: {
126196
126247
  type: "object",
126197
126248
  properties: {
126198
- amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to shield, in atomic units (1000000 = 1 USDC). A whole number of USDC on this service's pools. Required unless action is cancel." },
126249
+ amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to shield, in atomic units (1000000 = 1 USDC, 370000 = 0.37 USDC): any amount from 10000 (0.01 USDC) while the balance stays at most 5 USDC. Required unless action is cancel." },
126199
126250
  request_id: { type: "string", pattern: "^[A-Za-z0-9_-]{1,44}$", description: "Your stable name for this deposit, like from-phantom-1; pass it again (with the same amount_atomic) to read the status." },
126200
126251
  action: { type: "string", enum: ["deposit", "cancel"], description: "deposit (the default): start or read this deposit. cancel: close this request's one-time address while nothing at all reached it (no amount_atomic needed)." }
126201
126252
  },
@@ -126772,8 +126823,7 @@ function moneyOptions({
126772
126823
  exitProofToolsPath,
126773
126824
  profilePath,
126774
126825
  proofToolsPath,
126775
- memoServiceUrl,
126776
- depositUnitAtomic
126826
+ memoServiceUrl
126777
126827
  }, asText = (value) => value === void 0 || value === null ? "" : String(value)) {
126778
126828
  if (noShield === true && allowShield === true) setupInvalid("--no-shield and --allow-shield contradict each other; pass one");
126779
126829
  const shieldCap = asText(maxShieldAtomic);
@@ -126828,8 +126878,7 @@ function moneyOptions({
126828
126878
  ...shieldOn && shieldCap ? { maxShieldAtomic: shieldCap } : {},
126829
126879
  allowUnshield: unshieldOn,
126830
126880
  unshieldTo: Object.freeze([...new Set(addresses)]),
126831
- ...unshieldOn ? { ...unshieldCap ? { maxUnshieldAtomic: unshieldCap } : {}, exitProofTools, profile, facilitatorOrigin: facilitatorOrigin2 } : {},
126832
- ...depositUnitAtomic ? { depositUnitAtomic: asText(depositUnitAtomic) } : {}
126881
+ ...unshieldOn ? { ...unshieldCap ? { maxUnshieldAtomic: unshieldCap } : {}, exitProofTools, profile, facilitatorOrigin: facilitatorOrigin2 } : {}
126833
126882
  });
126834
126883
  }
126835
126884
  async function runMcpServer({ input = process.stdin, output: output2 = process.stdout, errorOutput = process.stderr, updateCheck, ...options } = {}) {
@@ -126876,7 +126925,6 @@ async function openWalletServer({
126876
126925
  maxUnshieldAtomic,
126877
126926
  exitProofToolsPath,
126878
126927
  profilePath,
126879
- depositUnitAtomic,
126880
126928
  autoPockets = true,
126881
126929
  readyPockets,
126882
126930
  discoverWarmup = false,
@@ -126904,8 +126952,7 @@ async function openWalletServer({
126904
126952
  exitProofToolsPath,
126905
126953
  profilePath,
126906
126954
  proofToolsPath,
126907
- memoServiceUrl,
126908
- depositUnitAtomic
126955
+ memoServiceUrl
126909
126956
  }, asText);
126910
126957
  const live = {
126911
126958
  ...proofToolsPath ? { proofToolsPath: resolve23(proofToolsPath) } : {},
@@ -131995,8 +132042,6 @@ function mcpServerOptions(values, deps) {
131995
132042
  ...values.has("--max-unshield") ? { maxUnshieldAtomic: values.get("--max-unshield") } : {},
131996
132043
  ...values.has("--exit-proof-tools") ? { exitProofToolsPath: values.get("--exit-proof-tools") } : {},
131997
132044
  ...values.has("--profile") ? { profilePath: values.get("--profile") } : {},
131998
- // A wallet_shield deposit takes whole units of this service's pools, as a relayed deposit does.
131999
- ...!values.has("--no-shield") && RELAYED_DEPOSIT_UNIT[pool] ? { depositUnitAtomic: RELAYED_DEPOSIT_UNIT[pool] } : {},
132000
132045
  // Automatic pockets: on by default (they fill with the unshield inputs, which config.json from setup holds).
132001
132046
  autoPockets: !values.has("--no-auto-pockets"),
132002
132047
  ...values.has("--ready-pockets") ? { readyPockets: values.get("--ready-pockets") } : {},
@@ -134491,9 +134536,6 @@ function relayDepositFailureHint(reason) {
134491
134536
  if (/p03_deposit_token_balance_unreadable/.test(reason)) {
134492
134537
  return "the RPC did not answer a USDC balance read (after 5 tries), so the wallet stopped: it never reads a failed read as 0. Do NOT send more USDC because of this; nothing was sent. Rerun the same command later, or use another --rpc";
134493
134538
  }
134494
- if (/p03_deposit_relay_amount_not_whole_unit/.test(reason)) {
134495
- return "this pool takes deposits in whole units of its pocket size (GET <facilitator>/api/x402/deposit shows depositUnitAtomic), so no amount marks one depositor; nothing was recorded or sent. Rerun with --amount a whole multiple of it";
134496
- }
134497
134539
  if (/p03_deposit_relay_amount_below_facilitator_minimum/.test(reason)) {
134498
134540
  return "the facilitator relays deposits of at least its minimum (GET <facilitator>/api/x402/deposit shows minimumAtomic); nothing was recorded. Rerun with a larger --amount";
134499
134541
  }