shinjuku-shield 0.3.84 → 0.3.85
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 +5 -5
- package/manifest.json +4 -4
- package/package.json +3 -3
- package/shinjuku-wallet.mjs +173 -41
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.
|
|
9
|
+
This is version 0.3.85. 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.
|
|
15
|
+
npx -y --no-audit --no-fund shinjuku-shield@0.3.85 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.
|
|
27
|
+
npm install -g shinjuku@0.3.85
|
|
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.
|
|
39
|
+
claude mcp add --scope user shinjuku-shielded -- npx -y --no-audit --no-fund shinjuku-shield@0.3.85 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.
|
|
45
|
+
{ "mcpServers": { "shinjuku-shielded": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.85", "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": "
|
|
5
|
-
"bytes":
|
|
6
|
-
"sourceCommit": "
|
|
4
|
+
"sha256": "654041bce4139bfb928180cc1a3c1ff05e570d9fc51ddfa718e9d0ef7fbf9095",
|
|
5
|
+
"bytes": 12513236,
|
|
6
|
+
"sourceCommit": "1e2a8c09784450c8bc0a5d391c428d08f699c44a",
|
|
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",
|
|
@@ -1282,5 +1282,5 @@
|
|
|
1282
1282
|
"version": "1.8.0"
|
|
1283
1283
|
}
|
|
1284
1284
|
],
|
|
1285
|
-
"publishedAt": "2026-10-
|
|
1285
|
+
"publishedAt": "2026-10-23T20:33:40.235Z"
|
|
1286
1286
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shinjuku-shield",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.85",
|
|
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": "
|
|
7
|
-
"shinjukuSha256": "
|
|
6
|
+
"shinjukuRelease": "654041bc",
|
|
7
|
+
"shinjukuSha256": "654041bce4139bfb928180cc1a3c1ff05e570d9fc51ddfa718e9d0ef7fbf9095",
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
9
9
|
"author": "Shinjuku Shielded",
|
|
10
10
|
"homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
|
package/shinjuku-wallet.mjs
CHANGED
|
@@ -62798,7 +62798,7 @@ async function readRelayConfig({ serviceUrl: serviceUrl2, fetchImpl, id }) {
|
|
|
62798
62798
|
async function submitRelayedDeposit(input) {
|
|
62799
62799
|
return withWalletLease(input.store, input.deps ?? {}, () => submitRelayedDepositLeased(input));
|
|
62800
62800
|
}
|
|
62801
|
-
async function submitRelayedDepositLeased({ store, connection, programId, proofTools, depositId, serviceUrl: serviceUrl2, deps = {} }) {
|
|
62801
|
+
async function submitRelayedDepositLeased({ store, connection, programId, proofTools, depositId, serviceUrl: serviceUrl2, historyUrl, deps = {} }) {
|
|
62802
62802
|
assertDepositId(depositId);
|
|
62803
62803
|
const name = walName(depositId);
|
|
62804
62804
|
const record = store.readWal(name);
|
|
@@ -62812,7 +62812,8 @@ async function submitRelayedDepositLeased({ store, connection, programId, proofT
|
|
|
62812
62812
|
const source = new import_web3101.PublicKey(record.relay.tokenAccount);
|
|
62813
62813
|
const pause3 = deps.pause ?? delay;
|
|
62814
62814
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
62815
|
-
const
|
|
62815
|
+
const historyConnection = connection.historyRpcChoice !== void 0 || connection.historyRpc ? connection : connectSplit(connection, historyUrl);
|
|
62816
|
+
const loadHistory = () => finalizedHistory({ store, connection: historyConnection, id, proofTools, deps });
|
|
62816
62817
|
const refuse19 = async (reason) => {
|
|
62817
62818
|
record.relay.approvedOnChain = (await relaySource(connection, id, record)).approved;
|
|
62818
62819
|
record.relay.lastRefusal = { reason, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -62981,8 +62982,42 @@ function settleTransaction(connection, signature2) {
|
|
|
62981
62982
|
maxSupportedTransactionVersion: 1
|
|
62982
62983
|
}));
|
|
62983
62984
|
}
|
|
62985
|
+
async function foundSettle(connection, id, record, intent) {
|
|
62986
|
+
let entries;
|
|
62987
|
+
try {
|
|
62988
|
+
if (typeof connection.getSignaturesForAddress !== "function") return void 0;
|
|
62989
|
+
entries = await retryRpc2(() => connection.getSignaturesForAddress(intent.address, { limit: SETTLE_SEARCH_LIMIT }, "finalized"));
|
|
62990
|
+
} catch {
|
|
62991
|
+
return void 0;
|
|
62992
|
+
}
|
|
62993
|
+
if (!Array.isArray(entries)) return void 0;
|
|
62994
|
+
for (const entry of [...entries].reverse()) {
|
|
62995
|
+
if (entry?.err !== null || typeof entry.signature !== "string") continue;
|
|
62996
|
+
const transaction = await settleTransaction(connection, entry.signature).catch(() => void 0);
|
|
62997
|
+
if (!transaction?.meta || transaction.meta.err) continue;
|
|
62998
|
+
const landing = returnedIntent(transaction, id, record);
|
|
62999
|
+
if (landing) return { signature: entry.signature, transaction, landing };
|
|
63000
|
+
}
|
|
63001
|
+
return void 0;
|
|
63002
|
+
}
|
|
62984
63003
|
async function relayReceipt({ connection, id, record, intent, loadHistory, transaction: read, walkMs }) {
|
|
62985
63004
|
const signature2 = record.relay.result?.signature;
|
|
63005
|
+
if (!signature2) {
|
|
63006
|
+
const found = await foundSettle(connection, id, record, intent);
|
|
63007
|
+
if (found) {
|
|
63008
|
+
const { transaction, landing } = found;
|
|
63009
|
+
record.relay.result = { signature: found.signature, slot: transaction.slot };
|
|
63010
|
+
return {
|
|
63011
|
+
intent: intent.address.toBase58(),
|
|
63012
|
+
indexAfter: landing.indexAfter,
|
|
63013
|
+
signature: found.signature,
|
|
63014
|
+
slot: transaction.slot,
|
|
63015
|
+
feeLamports: transaction.meta.fee,
|
|
63016
|
+
broadcaster: landing.broadcaster.toBase58(),
|
|
63017
|
+
source: landing.source.toBase58()
|
|
63018
|
+
};
|
|
63019
|
+
}
|
|
63020
|
+
}
|
|
62986
63021
|
if (signature2) {
|
|
62987
63022
|
const transaction = (read ? await read : void 0) ?? await settleTransaction(connection, signature2);
|
|
62988
63023
|
const landing = transaction?.meta && !transaction.meta.err ? returnedIntent(transaction, id, record) ?? settleFunder(transaction, id, intent.address) : void 0;
|
|
@@ -63378,7 +63413,7 @@ async function publishDepositMemo({ serviceUrl: serviceUrl2, record, fetchImpl =
|
|
|
63378
63413
|
const stored = await fetchMemoFromService(serviceUrl2, record.memoDigest, { fetchImpl });
|
|
63379
63414
|
if (!stored || !Buffer.from(stored).equals(bytes2)) fail11("memo_publish_readback_mismatch");
|
|
63380
63415
|
}
|
|
63381
|
-
var import_web3101, DEPOSIT_WAL_SCHEMA, P03_BINDING, BUFFER_SIZE, DEPOSIT_INTENT_LEN, DEPOSIT_RECORD_LEN, CLOSE_BUFFER, COMPACT_DEPOSIT_INTENT, BUFFER_LIFETIME_SLOTS, BUFFER_MIN_REMAINING_SLOTS, FEE_MARGIN_LAMPORTS, RESOLVED_STATES, LANDED_STATES, hex, field32, walName, LANDED_WALK_MS, SLOW_WALK, delay, DEPOSIT_RELAY_SCHEMA, DEPOSIT_RECLAIM_SCHEMA, DEPOSIT_RELAY_CONFIG_SCHEMA, APPROVE_COMPUTE_UNITS, RECLAIM_COMPUTE_UNITS, RELAY_ROUNDS, RELAY_KEY_PROBES, MAX_SCAN_COUNT, RELAY_REQUEST_TIMEOUT_MS, RELAY_MAX_WAIT_SECONDS, RELAY_REFUSALS, RECLAIM_REFUSALS, relayHex, SPLIT_MAX, DEFAULT_SPLIT_PARTS, SPLIT_UNIT_ATOMIC, FUNDS_PLAN_SCHEMA, FUNDS_PLAN_PREFIX, relayKeyPool, __test;
|
|
63416
|
+
var import_web3101, DEPOSIT_WAL_SCHEMA, P03_BINDING, BUFFER_SIZE, DEPOSIT_INTENT_LEN, DEPOSIT_RECORD_LEN, CLOSE_BUFFER, COMPACT_DEPOSIT_INTENT, BUFFER_LIFETIME_SLOTS, BUFFER_MIN_REMAINING_SLOTS, FEE_MARGIN_LAMPORTS, RESOLVED_STATES, LANDED_STATES, hex, field32, walName, LANDED_WALK_MS, SLOW_WALK, delay, DEPOSIT_RELAY_SCHEMA, DEPOSIT_RECLAIM_SCHEMA, DEPOSIT_RELAY_CONFIG_SCHEMA, APPROVE_COMPUTE_UNITS, RECLAIM_COMPUTE_UNITS, RELAY_ROUNDS, RELAY_KEY_PROBES, MAX_SCAN_COUNT, RELAY_REQUEST_TIMEOUT_MS, RELAY_MAX_WAIT_SECONDS, RELAY_REFUSALS, RECLAIM_REFUSALS, relayHex, SPLIT_MAX, DEFAULT_SPLIT_PARTS, SPLIT_UNIT_ATOMIC, FUNDS_PLAN_SCHEMA, FUNDS_PLAN_PREFIX, relayKeyPool, SETTLE_SEARCH_LIMIT, __test;
|
|
63382
63417
|
var init_deposit = __esm({
|
|
63383
63418
|
"wallet/src/deposit/deposit.mjs"() {
|
|
63384
63419
|
"use strict";
|
|
@@ -63486,6 +63521,7 @@ var init_deposit = __esm({
|
|
|
63486
63521
|
FUNDS_PLAN_SCHEMA = "carbon-shielded-wallet/add-funds/v1";
|
|
63487
63522
|
FUNDS_PLAN_PREFIX = "add-funds-";
|
|
63488
63523
|
relayKeyPool = (store, record) => record.relay.keyPool ?? record.pool ?? store.state.pool;
|
|
63524
|
+
SETTLE_SEARCH_LIMIT = 10;
|
|
63489
63525
|
__test = Object.freeze({ parseBuffer });
|
|
63490
63526
|
}
|
|
63491
63527
|
});
|
|
@@ -75305,6 +75341,7 @@ async function runSelfCustodyDeposit(args) {
|
|
|
75305
75341
|
throw new Error("deposit requires --program, --deposit-id, --amount <atomic>, --proof-tools, and --rpc-file <file> (or --rpc, or UNDERMARKET_SOLANA_RPC_URL)");
|
|
75306
75342
|
}
|
|
75307
75343
|
const proofTools = loadP03PublicPaymentProofTools(flag(args, "--proof-tools"));
|
|
75344
|
+
const historyUrl = historyRpcUrl(flag(args, "--history-rpc"));
|
|
75308
75345
|
const path = walletPath(pool2);
|
|
75309
75346
|
const relay = args.includes("--relay");
|
|
75310
75347
|
const indexFlag = flag(args, "--one-time-index");
|
|
@@ -75361,7 +75398,7 @@ async function runSelfCustodyDeposit(args) {
|
|
|
75361
75398
|
}, null, 2));
|
|
75362
75399
|
return;
|
|
75363
75400
|
}
|
|
75364
|
-
const result = await (relay ? submitRelayedDeposit : submitDeposit)({ ...common, serviceUrl: flag(args, "--service") });
|
|
75401
|
+
const result = await (relay ? submitRelayedDeposit : submitDeposit)({ ...common, serviceUrl: flag(args, "--service"), ...relay ? { historyUrl } : {} });
|
|
75365
75402
|
console.log(JSON.stringify({
|
|
75366
75403
|
...result,
|
|
75367
75404
|
next: result.state === "published" ? "run recover; export and verify two fresh backups" : "rerun with --submit --service <facilitator URL> to publish the memo"
|
|
@@ -151811,7 +151848,7 @@ function parseArgs2(args) {
|
|
|
151811
151848
|
if (!options["dry-run"] && FOR_RUN.some((key) => !options[key])) fail41("arguments_missing");
|
|
151812
151849
|
return options;
|
|
151813
151850
|
}
|
|
151814
|
-
async function main5(args = process.argv.slice(2)) {
|
|
151851
|
+
async function main5(args = process.argv.slice(2), { output: output2 = (result) => console.log(JSON.stringify(result)) } = {}) {
|
|
151815
151852
|
if (args.length === 1 && args[0] === "--help") {
|
|
151816
151853
|
console.log(USAGE3);
|
|
151817
151854
|
return;
|
|
@@ -151894,20 +151931,20 @@ async function main5(args = process.argv.slice(2)) {
|
|
|
151894
151931
|
return { id, amountAtomic: record.amountAtomic, accountCommitment: record.mergeAccountCommitment, noteCommitments: record.mergeNoteCommitments ?? [] };
|
|
151895
151932
|
});
|
|
151896
151933
|
if (dryRun) {
|
|
151897
|
-
|
|
151934
|
+
output2({
|
|
151898
151935
|
dryRun: true,
|
|
151899
151936
|
resume: resume.map((entry) => ({ amountAtomic: entry.amountAtomic, amount: usdc9(entry.amountAtomic) })),
|
|
151900
151937
|
next: "run the same command without --dry-run: it finishes these merges first (never pays twice)"
|
|
151901
|
-
})
|
|
151938
|
+
});
|
|
151902
151939
|
return;
|
|
151903
151940
|
}
|
|
151904
151941
|
const results2 = [];
|
|
151905
151942
|
for (const entry of resume) results2.push(await runOne(entry));
|
|
151906
|
-
|
|
151943
|
+
output2({
|
|
151907
151944
|
success: true,
|
|
151908
151945
|
resumed: results2,
|
|
151909
151946
|
next: "after the next pool anchor (up to 5 minutes; about 30 s on a pool with sub-anchors): balance --refresh, then rerun merge (it plans the next round, or says nothing is left) or your payment"
|
|
151910
|
-
})
|
|
151947
|
+
});
|
|
151911
151948
|
return;
|
|
151912
151949
|
}
|
|
151913
151950
|
const snapshotRead = readBalanceSnapshot(store);
|
|
@@ -151915,8 +151952,8 @@ async function main5(args = process.argv.slice(2)) {
|
|
|
151915
151952
|
if (balance.status !== "current" || snapshotRead.snapshot?.programId !== String(options.program)) fail41("balance_walk_not_current_run_balance_refresh");
|
|
151916
151953
|
if (balance.detail.inFlight.some((item) => item.kind !== "merge")) fail41("in_flight_work_finish_first");
|
|
151917
151954
|
const plan = planMerge({ snapshot: snapshotRead.snapshot, exitReserved: exitReservedNoteCommitments(store) });
|
|
151918
|
-
const shown = (
|
|
151919
|
-
const merges = plan.merge.map((
|
|
151955
|
+
const shown = (output3) => ({ amountAtomic: output3.valueAtomic, amount: usdc9(output3.valueAtomic) });
|
|
151956
|
+
const merges = plan.merge.map((output3) => ({ id: nextMergeId(store, output3.commitment), amountAtomic: output3.valueAtomic, accountCommitment: output3.commitment })).filter((entry) => entry.id !== void 0);
|
|
151920
151957
|
const summary = {
|
|
151921
151958
|
totalAtomic: plan.totalAtomic,
|
|
151922
151959
|
total: usdc9(plan.totalAtomic),
|
|
@@ -151928,19 +151965,19 @@ async function main5(args = process.argv.slice(2)) {
|
|
|
151928
151965
|
const nothing = merges.length === 0;
|
|
151929
151966
|
const why = !plan.keep || plan.merge.length + plan.later.length === 0 ? "nothing to merge: one funded account (or none) holds the shielded balance, so one payment already draws it all (with up to 3 notes)" : plan.merge.length === 0 ? `nothing to merge this round: a payment already draws ${plan.notes.length} notes, the most one spend takes is ${MAX_RESPEND_NOTES}. Make one payment first (its change joins those notes into one account), then rerun merge` : void 0;
|
|
151930
151967
|
if (dryRun || nothing) {
|
|
151931
|
-
|
|
151968
|
+
output2({ dryRun, ...summary, ...why ? { next: why } : nothing ? { next: "nothing left to merge" } : {
|
|
151932
151969
|
next: "run the same command without --dry-run (with --proof-tools, --profile, --facilitator); nothing was signed or sent"
|
|
151933
|
-
} })
|
|
151970
|
+
} });
|
|
151934
151971
|
return;
|
|
151935
151972
|
}
|
|
151936
151973
|
const results = [];
|
|
151937
151974
|
for (const entry of merges) results.push(await runOne(entry));
|
|
151938
|
-
|
|
151975
|
+
output2({
|
|
151939
151976
|
success: true,
|
|
151940
151977
|
...summary,
|
|
151941
151978
|
merged: results,
|
|
151942
151979
|
next: plan.later.length ? "after the next pool anchor (up to 5 minutes; about 30 s on a pool with sub-anchors): make your payment (it draws the kept account and the new notes), then balance --refresh and rerun merge for the accounts in later" : "after the next pool anchor (up to 5 minutes; about 30 s on a pool with sub-anchors), rerun your payment: it draws the kept account plus the new notes. balance --refresh shows the new state"
|
|
151943
|
-
})
|
|
151980
|
+
});
|
|
151944
151981
|
} finally {
|
|
151945
151982
|
await release();
|
|
151946
151983
|
}
|
|
@@ -154914,7 +154951,8 @@ function fundsView(store, plan, { now = Date.now(), running } = {}) {
|
|
|
154914
154951
|
state: last?.stopped ? "stopped" : idle ? "not running" : last?.until ? "waiting" : "next",
|
|
154915
154952
|
...last?.waitsFor ? { waitsFor: last.waitsFor } : {},
|
|
154916
154953
|
...last?.until && !idle ? { until: last.until } : {},
|
|
154917
|
-
|
|
154954
|
+
// A wait's code (K29 T13): the terminal says the wait in words only.
|
|
154955
|
+
...last?.stopped ? { code: last.stopped } : last?.code ? { code: last.code } : {},
|
|
154918
154956
|
...last?.said?.length ? { said: last.said } : {}
|
|
154919
154957
|
};
|
|
154920
154958
|
});
|
|
@@ -155087,20 +155125,44 @@ var TRANSIENT = [
|
|
|
155087
155125
|
/^E[A-Z0-9_]{1,40}$/
|
|
155088
155126
|
];
|
|
155089
155127
|
var SOON_CODES = /(_retry_same_|_rerun$|wallet_process_locked|wallet_locked|_wait_next_epoch|_anchor_not_finalized)/;
|
|
155090
|
-
|
|
155128
|
+
var RETRY_WORDS = [
|
|
155129
|
+
[/^p03_deposit_landed_history_walk_slow_retry_later$/, {
|
|
155130
|
+
now: "the deposit is in the pool; the wallet could not read the pool history in time to confirm it, so it reads again",
|
|
155131
|
+
waitsFor: "a new read of the pool history to confirm the deposit (the deposit is in the pool)"
|
|
155132
|
+
}],
|
|
155133
|
+
[/^p03_deposit_(landed_history_pending|receipt_transaction_unavailable|receipt_evidence_missing)_retry_later$/, {
|
|
155134
|
+
now: "the deposit is in the pool; the chain does not show the wallet its receipt yet, so it reads again",
|
|
155135
|
+
waitsFor: "the chain to show the deposit's receipt (the deposit is in the pool)"
|
|
155136
|
+
}],
|
|
155137
|
+
[/^p03_deposit_relay_not_settled_rerun_same_id$/, {
|
|
155138
|
+
now: "the facilitator has not moved the deposit into the pool yet, so the wallet asks again",
|
|
155139
|
+
waitsFor: "the facilitator to move the deposit into the pool"
|
|
155140
|
+
}],
|
|
155141
|
+
[/^rpc_unreachable$/, { now: "the RPC did not answer, so the wallet asks again", waitsFor: "an answer from the RPC (it did not answer)" }],
|
|
155142
|
+
[/^rpc_rate_limited$/, { now: "the RPC asked the wallet to slow down, so it asks again", waitsFor: "the RPC to take requests again (it asked the wallet to slow down)" }],
|
|
155143
|
+
[/^(wallet_process_locked|p03_pocket_cli_wallet_locked)$/, {
|
|
155144
|
+
now: "another wallet command uses this wallet right now, so add-funds tries again",
|
|
155145
|
+
waitsFor: "the other wallet command to finish with this wallet"
|
|
155146
|
+
}]
|
|
155147
|
+
];
|
|
155148
|
+
var RETRY_ANY = { now: "the step did not finish this time and nothing was lost, so it runs again", waitsFor: "a new try of the step (nothing was lost)" };
|
|
155149
|
+
var NO_CODE = { now: "the step ended without an error code (it was stopped, or a connection was refused), so it runs again", waitsFor: "a new try of the step (it ended without an error code)" };
|
|
155150
|
+
function transient(ctx, run5, words2) {
|
|
155091
155151
|
ctx.transient += 1;
|
|
155092
|
-
if (ctx.transient > MAX_TRANSIENT) return stop2(run5,
|
|
155093
|
-
|
|
155152
|
+
if (ctx.transient > MAX_TRANSIENT) return { ...stop2(run5, words2.waitsFor), tries: true };
|
|
155153
|
+
const code2 = run5.code ?? run5.codes[0];
|
|
155154
|
+
return wait(has(run5, SOON_CODES) ? SOON_MS : LATER_MS, words2.waitsFor, { transient: true, said: words2.now, ...code2 ? { code: code2 } : {} });
|
|
155094
155155
|
}
|
|
155095
155156
|
function otherwise(ctx, run5) {
|
|
155096
155157
|
if (run5.aborted) return stop2(run5, "stopped");
|
|
155097
155158
|
if (run5.exit === 2) return stop2(run5, "the step's command line was refused");
|
|
155098
|
-
if (!run5.code && !run5.codes.length) return transient(ctx, run5,
|
|
155159
|
+
if (!run5.code && !run5.codes.length) return transient(ctx, run5, NO_CODE);
|
|
155099
155160
|
if (has(run5, FACILITATOR_LATER)) {
|
|
155100
155161
|
return wait(LATER_MS, has(run5, /_paused_retry_later$/) ? "the facilitator to take this step again (it is paused right now)" : "the facilitator's relayer to have its fee money again");
|
|
155101
155162
|
}
|
|
155102
155163
|
if (run5.codes.some((code2) => TRANSIENT.some((pattern) => pattern.test(code2)))) {
|
|
155103
|
-
|
|
155164
|
+
const codes = [run5.code, ...run5.codes].filter(Boolean);
|
|
155165
|
+
return transient(ctx, run5, RETRY_WORDS.find(([pattern]) => codes.some((code2) => pattern.test(code2)))?.[1] ?? RETRY_ANY);
|
|
155104
155166
|
}
|
|
155105
155167
|
return stop2(run5, `the step refused (${run5.code ?? run5.codes[0]})`);
|
|
155106
155168
|
}
|
|
@@ -155121,7 +155183,7 @@ function depositArgs(ctx, submit, part) {
|
|
|
155121
155183
|
usdcFlag(part.amountAtomic),
|
|
155122
155184
|
"--service",
|
|
155123
155185
|
values.service,
|
|
155124
|
-
...submit ? ["--submit"] : []
|
|
155186
|
+
...submit ? ["--submit", ...historyRpcArgs(ctx)] : []
|
|
155125
155187
|
];
|
|
155126
155188
|
}
|
|
155127
155189
|
function crowdFlags(ctx) {
|
|
@@ -155687,20 +155749,26 @@ async function addFunds(values, deps) {
|
|
|
155687
155749
|
}
|
|
155688
155750
|
const label = outcome.label ?? next ?? stage;
|
|
155689
155751
|
if (outcome.kind === "stop") {
|
|
155690
|
-
const said = (outcome.run?.held ?? []).filter((text3) => !/^(Help|next): /.test(text3));
|
|
155691
|
-
if (!said.length && outcome.run?.last) said.push(outcome.run.last);
|
|
155752
|
+
const said = outcome.tries ? [] : (outcome.run?.held ?? []).filter((text3) => !/^(Help|next): /.test(text3));
|
|
155753
|
+
if (!said.length && outcome.run?.last && !outcome.tries) said.push(outcome.run.last);
|
|
155692
155754
|
for (const text3 of said) io.err(` ${text3}
|
|
155693
155755
|
`);
|
|
155694
155756
|
plan.status = { stage: label, at: iso2(deps.now()), stopped: outcome.run?.code ?? outcome.run?.codes?.[0] ?? "refused", waitsFor: outcome.waitsFor, ...said.length ? { said } : {} };
|
|
155695
155757
|
save2();
|
|
155696
|
-
throw failure("stopped", `the ${label} step needs you: ${outcome.waitsFor}${said.length ? " (its own line and hint are above)" : ""}. Fix that, then rerun this same add-funds command: it continues at ${label}`);
|
|
155758
|
+
throw failure("stopped", outcome.tries ? `add-funds stopped after ${MAX_TRANSIENT} tries of the ${label} step: it still waits for ${outcome.waitsFor}. Nothing was lost. Run this same add-funds command again later: it continues at ${label}` : `the ${label} step needs you: ${outcome.waitsFor}${said.length ? " (its own line and hint are above)" : ""}. Fix that, then rerun this same add-funds command: it continues at ${label}`);
|
|
155697
155759
|
}
|
|
155698
155760
|
const until = iso2(deps.now() + outcome.ms);
|
|
155699
|
-
plan.status = { stage: label, at: iso2(deps.now()), waitsFor: outcome.waitsFor, until };
|
|
155761
|
+
plan.status = { stage: label, at: iso2(deps.now()), waitsFor: outcome.waitsFor, until, ...outcome.code ? { code: outcome.code } : {} };
|
|
155700
155762
|
save2();
|
|
155701
|
-
if (outcome.
|
|
155763
|
+
if (outcome.transient) {
|
|
155764
|
+
const at = `${until.slice(11, 19)} UTC`;
|
|
155765
|
+
ctx.say(label, values.has("--once") ? `${outcome.said} on the next add-funds run (--once ends this one here; the next try can start at ${at})` : ctx.transient === 1 ? `${outcome.said} at ${at} by itself (nothing to do)` : `try ${ctx.transient} of ${MAX_TRANSIENT}: still waits for ${outcome.waitsFor}; next try at ${at}`);
|
|
155766
|
+
lastWait = outcome.waitsFor;
|
|
155767
|
+
} else if (outcome.waitsFor !== lastWait) {
|
|
155702
155768
|
ctx.say(label, `waits for ${outcome.waitsFor}; next try at ${until}${outcome.every ? ` (${outcome.every})` : ""}`);
|
|
155703
155769
|
lastWait = outcome.waitsFor;
|
|
155770
|
+
} else if (!outcome.every) {
|
|
155771
|
+
ctx.say(label, `still waits for ${outcome.waitsFor}; next try at ${until}`);
|
|
155704
155772
|
}
|
|
155705
155773
|
if (values.has("--once")) {
|
|
155706
155774
|
plan.status = { ...plan.status, ended: iso2(deps.now()) };
|
|
@@ -155708,7 +155776,7 @@ async function addFunds(values, deps) {
|
|
|
155708
155776
|
printEnd(deps, fundsView(store, plan, { now: deps.now(), running: false }), ctx.terminal);
|
|
155709
155777
|
return;
|
|
155710
155778
|
}
|
|
155711
|
-
await sleep3(outcome.ms, abort.signal);
|
|
155779
|
+
await (deps.sleep ?? sleep3)(outcome.ms, abort.signal);
|
|
155712
155780
|
}
|
|
155713
155781
|
plan.status = { ...plan.status ?? { stage: currentStage(store, plan) }, ended: iso2(deps.now()) };
|
|
155714
155782
|
save2();
|
|
@@ -158018,7 +158086,7 @@ var COMMANDS = [
|
|
|
158018
158086
|
flag2("--profile", "<file>", "the facilitator's public seller profile, public-profile.json (pocket fill; needed only with --pockets 1 or more)"),
|
|
158019
158087
|
flag2("--pockets", "<n>", "0 to 20; default 0 (no pocket and no dwell). 1 or more fills that many pockets after the deposit (pocket fill). A new payment never pays from a pocket: it pays through Shinjuku from the shielded balance, so most wallets leave this out", { check: "count" }),
|
|
158020
158088
|
flag2("--pocket-size", "<usdc>", "the size of each pocket, in USDC; default 1 (1 USDC), the size our facilitator uses", { check: "atomic", usdc: true }),
|
|
158021
|
-
{ ...HISTORY_RPC_FLAG, about: `add-funds passes it only to the steps that read the pool's history (balance --refresh, pocket fill,
|
|
158089
|
+
{ ...HISTORY_RPC_FLAG, about: `add-funds passes it only to the steps that read the pool's history (balance --refresh, pocket fill, its dwell check, and the deposit's walk that confirms a deposit that landed without the facilitator's answer; never to a Privacy Cash step): ${HISTORY_RPC_FLAG.about}` },
|
|
158022
158090
|
flag2("--from-key-file", "<file>", "--route privacy-cash: a solana-keygen JSON key of your own wallet that pays into Privacy Cash and owns its notes"),
|
|
158023
158091
|
flag2("--crowd-amount", "<usdc>", "--route privacy-cash: whole USDC to put into Privacy Cash first (13 = 13 USDC); leave it out to spend notes that key already holds there", { check: "atomic", usdc: true }),
|
|
158024
158092
|
{ ...PRIVACY_CASH_VIA_FLAG, about: `--route privacy-cash: ${PRIVACY_CASH_VIA_FLAG.about}` },
|
|
@@ -158103,6 +158171,7 @@ var COMMANDS = [
|
|
|
158103
158171
|
flag2("--deposit-id", "<id>", "your name for this deposit; reuse it for every retry", { required: true }),
|
|
158104
158172
|
flag2("--amount", "<usdc>", "USDC to deposit (0.03 = 3 cents)", { required: true, check: "atomic", usdc: true }),
|
|
158105
158173
|
...rpcFlags,
|
|
158174
|
+
{ ...HISTORY_RPC_FLAG, about: `with --relay --submit, only for the pool-history walk that confirms a deposit that landed without the facilitator's answer: ${HISTORY_RPC_FLAG.about}` },
|
|
158106
158175
|
bool2("--submit", "send the recorded deposit (second run)"),
|
|
158107
158176
|
flag2("--service", "<url>", "facilitator that stores the deposit memo (use with --submit; with --relay, on both runs)"),
|
|
158108
158177
|
bool2("--relay", "deposit from a one-time address of this wallet that holds only USDC; the facilitator pays every fee and rent, so it needs no SOL"),
|
|
@@ -158805,7 +158874,7 @@ var COMMANDS = [
|
|
|
158805
158874
|
"After setup it needs only --amount and --to: config.json gives the wallet, the passphrase file, the proof tools, the profile, the program, and the facilitator. Without setup, give every flag marked required (the old form also takes --to <USDC token account> --recipient-owner <owner>).",
|
|
158806
158875
|
"The wallet pays exactly --amount to a note it owns and keeps the rest private. The facilitator relays the note's exit to the recipient and pays every fee. No wallet key signs.",
|
|
158807
158876
|
"The recipient must already hold USDC (a Solana address with a USDC account, for example a Phantom address that received USDC); the command checks it first and sends nothing to an address without one. It refuses this wallet's own address: an exit there would show on chain that the money is yours. Best: an address that never sent USDC into this wallet.",
|
|
158808
|
-
|
|
158877
|
+
"--amount all sends the whole private balance. When the balance is in more than one part, the same command joins the parts first (a private move inside this wallet, no fee; nothing leaves it; about 2 minutes per extra part, after a random wait of up to 60 s), then sends all of it. Ctrl+C stops it; the same command again goes on and never sends twice.",
|
|
158809
158878
|
"The command prints the unshield id first. If it stops, run the same command with --unshield-id <that id> added: it finishes that unshield and never sends twice.",
|
|
158810
158879
|
"In a terminal it ends with one plain line (what was sent, to whom, the exit transaction) and the new private balance: after an exit the wallet reads the balance again itself (the same walk as balance --refresh, over the same connection; Tor when setup recorded it). A program, or --json, gets the result as JSON.",
|
|
158811
158880
|
`--parts and --max-delay-minutes make a private send (${CLI2} unshield --amount 3 --parts 3 --max-delay-minutes 20 --to <your wallet address>): the plan (part sizes, part ids <unshield-id>-p1.., earliest times) is saved in the wallet before part 1, each part is one unshield, and a stopped run continues at the first part not yet exited when the same command with its --unshield-id runs again. The wallet is free for other commands while a part waits. Keep the command running until it prints its result.`,
|
|
@@ -159631,15 +159700,17 @@ async function runUnshieldCommand(args, values, deps, io) {
|
|
|
159631
159700
|
setArg("--to", getAssociatedTokenAddressSync(mint, owner, true, TOKEN_PROGRAM_ID).toBase58());
|
|
159632
159701
|
setArg("--recipient-owner", owner.toBase58());
|
|
159633
159702
|
}
|
|
159634
|
-
if (values.get("--amount") === "all") setArg("--amount", await sendAllAtomic(values, deps, io, openStore));
|
|
159635
159703
|
let id = values.get("--unshield-id");
|
|
159636
|
-
|
|
159704
|
+
const ensureId = () => {
|
|
159705
|
+
if (id !== void 0) return;
|
|
159637
159706
|
id = `unshield-${randomBytes35(6).toString("hex")}`;
|
|
159638
159707
|
setArg("--unshield-id", id);
|
|
159639
159708
|
values.set(UNSHIELD_MADE_ID, id);
|
|
159640
159709
|
io.err(`unshield id: ${id} (if this command stops, run it again with --unshield-id ${id} added: it finishes this unshield and never sends twice)
|
|
159641
159710
|
`);
|
|
159642
|
-
}
|
|
159711
|
+
};
|
|
159712
|
+
if (values.get("--amount") === "all") setArg("--amount", await sendAllAtomic(values, deps, io, openStore, ensureId));
|
|
159713
|
+
ensureId();
|
|
159643
159714
|
const gated = await spendGate(callArgs, values, deps, io, { amountAtomic: values.get("--amount"), again: ` with --unshield-id ${id}` });
|
|
159644
159715
|
let answer;
|
|
159645
159716
|
const returned = await deps.unshieldMain(gated, { output: (result) => {
|
|
@@ -159743,25 +159814,86 @@ async function refreshAfterUnshield(walletFile, values, deps, io) {
|
|
|
159743
159814
|
restore();
|
|
159744
159815
|
}
|
|
159745
159816
|
}
|
|
159746
|
-
async function sendAllAtomic(values, deps, io, openStore) {
|
|
159817
|
+
async function sendAllAtomic(values, deps, io, openStore, ensureId) {
|
|
159747
159818
|
const store = openStore();
|
|
159748
159819
|
let report = computeBalance({ store, snapshotRead: readBalanceSnapshot(store), source: "local", now: deps.now() });
|
|
159749
159820
|
if (report.status !== "current") report = await refreshAfterUnshield(values.get("--wallet"), values, deps, io) ?? report;
|
|
159821
|
+
const notCurrent = (read) => new PublicFailure(`balance_not_current: --amount all needs the current private balance, and it is not current (${balanceNotCurrentWhy(read)}); nothing was sent. Run ${CLI2} balance --refresh in a minute, then this command again, or give the amount, like ${UNSHIELD_EXAMPLE}`);
|
|
159750
159822
|
const empty = report.status === "empty" || report.status === "current" && report.atomic === "0";
|
|
159751
|
-
if (!empty && (report.status !== "current" || !/^[1-9][0-9]*$/.test(String(report.atomic ?? "")))) {
|
|
159752
|
-
throw new PublicFailure(`balance_not_current: --amount all needs the current private balance, and it is not current (${balanceNotCurrentWhy(report)}); nothing was sent. Run ${CLI2} balance --refresh in a minute, then this command again, or give the amount, like ${UNSHIELD_EXAMPLE}`);
|
|
159753
|
-
}
|
|
159754
159823
|
if (empty) {
|
|
159755
159824
|
throw new PublicFailure(`no_spendable_account: the private balance is 0 USDC, so --amount all has nothing to send; nothing was sent. Add funds first (${CLI2} help add-funds)`);
|
|
159756
159825
|
}
|
|
159757
159826
|
const parts = joinParts(store);
|
|
159758
|
-
|
|
159759
|
-
|
|
159760
|
-
|
|
159761
|
-
io.err(`--amount all: the whole private balance, ${usdcOr(report.atomic)}
|
|
159827
|
+
const join40 = parts?.joining > 0 || parts?.parts > 1 && report.status === "current";
|
|
159828
|
+
if (join40) report = await joinForSendAll(report, parts, store, values, deps, io, ensureId);
|
|
159829
|
+
if (report.status !== "current" || !/^[1-9][0-9]*$/.test(String(report.atomic ?? ""))) throw notCurrent(report);
|
|
159830
|
+
if (!join40) io.err(`--amount all: the whole private balance, ${usdcOr(report.atomic)}
|
|
159762
159831
|
`);
|
|
159763
159832
|
return report.atomic;
|
|
159764
159833
|
}
|
|
159834
|
+
var SEND_ALL_JOIN_ROUNDS = 3;
|
|
159835
|
+
async function joinForSendAll(report, parts, store, values, deps, io, ensureId) {
|
|
159836
|
+
const count2 = parts.parts + parts.joining;
|
|
159837
|
+
const known = /^[0-9]+$/.test(String(report.atomic ?? ""));
|
|
159838
|
+
io.err(`--amount all: the private balance${known ? `, ${usdcOr(report.atomic)},` : ""} is in ${count2} parts, and one unshield sends from one part. The wallet joins the parts first (a private move inside this wallet, no fee; nothing leaves it), then sends all of it: about ${joinMinutes(count2)} min. Ctrl+C stops it; the same command again goes on and never sends twice
|
|
159839
|
+
`);
|
|
159840
|
+
ensureId();
|
|
159841
|
+
if (parts.joining === 0) {
|
|
159842
|
+
const waitMs = Math.floor((deps.random ?? Math.random)() * (JOIN_SOON_MAX_MS + 1));
|
|
159843
|
+
io.err(`the join starts in ${Math.round(waitMs / 1e3)} s: a random wait, so the chain does not show the join right after your request
|
|
159844
|
+
`);
|
|
159845
|
+
await (deps.sleep ?? ((ms) => new Promise((done) => {
|
|
159846
|
+
setTimeout(done, ms);
|
|
159847
|
+
})))(waitMs);
|
|
159848
|
+
}
|
|
159849
|
+
const history = values.get("--history-rpc");
|
|
159850
|
+
const mergeArgs = [
|
|
159851
|
+
"--wallet",
|
|
159852
|
+
values.get("--wallet"),
|
|
159853
|
+
"--passphrase-file",
|
|
159854
|
+
values.get("--passphrase-file"),
|
|
159855
|
+
"--program",
|
|
159856
|
+
values.get("--program"),
|
|
159857
|
+
"--proof-tools",
|
|
159858
|
+
values.get("--payer-proof-tools"),
|
|
159859
|
+
"--profile",
|
|
159860
|
+
values.get("--profile"),
|
|
159861
|
+
"--facilitator",
|
|
159862
|
+
new URL(values.get("--facilitator")).origin,
|
|
159863
|
+
...values.has("--rpc-file") ? ["--rpc-file", values.get("--rpc-file")] : [],
|
|
159864
|
+
...history ? ["--history-rpc", history] : []
|
|
159865
|
+
];
|
|
159866
|
+
const startedAt = deps.now();
|
|
159867
|
+
let joined = 0;
|
|
159868
|
+
let left = parts;
|
|
159869
|
+
let keep;
|
|
159870
|
+
let read;
|
|
159871
|
+
for (let round = 1; round <= SEND_ALL_JOIN_ROUNDS; round += 1) {
|
|
159872
|
+
let answer;
|
|
159873
|
+
try {
|
|
159874
|
+
await deps.mergeMain(mergeArgs, { output: (result) => {
|
|
159875
|
+
answer = result;
|
|
159876
|
+
} });
|
|
159877
|
+
} catch (error) {
|
|
159878
|
+
io.err("note: the join stopped, so the unshield did not start; nothing was sent\n");
|
|
159879
|
+
throw error;
|
|
159880
|
+
}
|
|
159881
|
+
const merged = (answer?.merged ?? answer?.resumed ?? []).filter((entry) => entry?.state === "merged").length;
|
|
159882
|
+
joined += merged;
|
|
159883
|
+
keep = answer?.keep?.amountAtomic ?? keep;
|
|
159884
|
+
read = await refreshAfterUnshield(values.get("--wallet"), values, deps, io);
|
|
159885
|
+
left = joinParts(store);
|
|
159886
|
+
if (read?.status === "current" && left?.parts <= 1 && left.joining === 0) {
|
|
159887
|
+
const seconds2 = Math.max(0, Math.round((deps.now() - startedAt) / 1e3));
|
|
159888
|
+
io.err(`joined the ${count2} parts in ${Math.floor(seconds2 / 60)} min ${seconds2 % 60} s; sending all ${usdcOr(read.atomic)} now
|
|
159889
|
+
`);
|
|
159890
|
+
return read;
|
|
159891
|
+
}
|
|
159892
|
+
if (!read || read.status !== "current" || merged === 0) break;
|
|
159893
|
+
}
|
|
159894
|
+
const remaining = left ? left.parts + left.joining : void 0;
|
|
159895
|
+
throw new PublicFailure(`unshield_joining_accounts: the wallet joined ${joined} part${joined === 1 ? "" : "s"} of your private balance into the largest one` + (remaining > 1 ? `, and it is still in ${remaining} parts` : read ? ", but the balance walk after the join is not current yet" : ", but the balance could not be read again after the join") + `; nothing was sent. Send a fixed amount up to the largest part${keep ? ` (${usdcOr(keep)})` : ""} now, like ${UNSHIELD_EXAMPLE} (that send also joins the parts it draws), or run ${CLI2} unshield --amount all --to <your wallet address> again in a few minutes: it goes on with the next join`);
|
|
159896
|
+
}
|
|
159765
159897
|
async function runUnshieldCancel(values, deps, io) {
|
|
159766
159898
|
const walletFile = resolve38(values.get("--wallet"));
|
|
159767
159899
|
const passphrase2 = payerPassphrase(values, deps);
|