@needmoretruth/nmts-cli 0.34.3 → 0.35.0
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/CHANGELOG.md +24 -0
- package/README.ko.md +15 -15
- package/README.md +1 -1
- package/dist/api-advice.js +4 -0
- package/dist/arg-options.d.ts +167 -0
- package/dist/arg-options.js +10 -0
- package/dist/args.d.ts +2 -157
- package/dist/args.js +2 -0
- package/dist/artifact-about.d.ts +1 -1
- package/dist/commands/extend.d.ts +2 -0
- package/dist/commands/extend.js +8 -1
- package/dist/commands/push-wallet.js +6 -0
- package/dist/commands/push.d.ts +2 -0
- package/dist/commands/put-payer.d.ts +8 -0
- package/dist/commands/put-payer.js +41 -0
- package/dist/commands/put-wallet.d.ts +9 -3
- package/dist/commands/put-wallet.js +105 -185
- package/dist/commands/put.d.ts +5 -7
- package/dist/commands/put.js +3 -26
- package/dist/commands/wallet-donate.d.ts +3 -0
- package/dist/commands/wallet-donate.js +6 -2
- package/dist/commands/wallet-list.d.ts +18 -0
- package/dist/commands/wallet-list.js +116 -0
- package/dist/commands/wallet-send.d.ts +4 -0
- package/dist/commands/wallet-send.js +6 -2
- package/dist/commands/wallet-use.d.ts +7 -0
- package/dist/commands/wallet-use.js +55 -0
- package/dist/commands/wallet.d.ts +4 -0
- package/dist/commands/wallet.js +20 -8
- package/dist/extend-plan.d.ts +2 -0
- package/dist/help.js +3 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +14 -1
- package/dist/main.js +4 -4
- package/dist/product.d.ts +1 -1
- package/dist/product.js +1 -1
- package/dist/risk.d.ts +4 -0
- package/dist/risk.js +6 -0
- package/dist/shared/lib/drive/manifest-ops.d.ts +8 -30
- package/dist/shared/lib/drive/manifest-ops.js +7 -39
- package/dist/shared/lib/drive/manifest-settings-patch.d.ts +52 -0
- package/dist/shared/lib/drive/manifest-settings-patch.js +110 -0
- package/dist/shared/lib/drive/manifest-settings.d.ts +55 -4
- package/dist/shared/lib/drive/manifest-settings.js +66 -28
- package/dist/shared/lib/wallet/discover.d.ts +54 -0
- package/dist/shared/lib/wallet/discover.js +66 -0
- package/dist/standing-tip.d.ts +3 -0
- package/dist/standing-tip.js +1 -0
- package/dist/upload-wallet-put.d.ts +118 -0
- package/dist/upload-wallet-put.js +221 -0
- package/dist/upload-wallet.d.ts +3 -0
- package/dist/upload-wallet.js +2 -0
- package/dist/wallet-list-chain.d.ts +4 -0
- package/dist/wallet-list-chain.js +21 -0
- package/dist/wallet-pay-index.d.ts +21 -0
- package/dist/wallet-pay-index.js +68 -0
- package/dist/wallet-sign-seams.d.ts +57 -0
- package/dist/wallet-sign-seams.js +16 -0
- package/dist/wallet-sign.d.ts +3 -53
- package/dist/wallet-sign.js +22 -15
- package/dist/wallet.d.ts +13 -13
- package/dist/wallet.js +15 -15
- package/docs/commands/wallet.md +18 -4
- package/package.json +1 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// `nmts wallet list` — every wallet this NMTS key has, what is in it, and which one pays.
|
|
2
|
+
//
|
|
3
|
+
// ⛔ IT READS. Nothing here signs, and nothing here writes the account's choice: `wallet use N`
|
|
4
|
+
// does that. Running this repeatedly costs nothing but questions to a public node.
|
|
5
|
+
//
|
|
6
|
+
// ⛔ WHICH WALLETS EXIST IS NOT A LIST ANYBODY KEEPS. One key derives a wallet at every index
|
|
7
|
+
// (NCF-3 §1.3), so "how many do I have" is answered by WALKING — ask about a number, then the
|
|
8
|
+
// next, and stop after twenty unused ones in a row. That rule is the browser's own, copied
|
|
9
|
+
// byte-for-byte (`shared/lib/wallet/discover.ts`), so the same key shows the same wallets in
|
|
10
|
+
// both programs. ⚠ A wallet funded further out than the gap is not found by the walk; it is not
|
|
11
|
+
// lost — `--wallet N` and `wallet use N` reach any number directly.
|
|
12
|
+
//
|
|
13
|
+
// ⛔ A BALANCE THAT COULD NOT BE READ IS PRINTED AS THAT, NEVER AS ZERO, and the run exits
|
|
14
|
+
// non-zero — the same rule as `nmts wallet`, for the same reason: an empty wallet and an
|
|
15
|
+
// unanswered question look identical on a screen, and one of them is a reason to stop.
|
|
16
|
+
import { requireAccountCode } from "../code-access.js";
|
|
17
|
+
import { readCredentialsFile } from "../credentials.js";
|
|
18
|
+
import { readFileList } from "../manifest.js";
|
|
19
|
+
import { resolveNetwork } from "../network.js";
|
|
20
|
+
import { BINARY_NAME } from "../product.js";
|
|
21
|
+
import { resolveServer } from "../server.js";
|
|
22
|
+
import { openSession } from "../session.js";
|
|
23
|
+
import { activeWalletOf, walletCountOf } from "../shared/lib/drive/manifest-settings.js";
|
|
24
|
+
import { discoverWallets } from "../shared/lib/wallet/discover.js";
|
|
25
|
+
import { coinAmount, readBalances, walCoinType, walletAddress, SUI_COIN_TYPE, } from "../wallet.js";
|
|
26
|
+
/** The table's column widths. A Sui address is 66 characters and gets two spaces after it. */
|
|
27
|
+
const NUMBER_W = 8;
|
|
28
|
+
const ADDRESS_W = 68;
|
|
29
|
+
const AMOUNT_W = 21;
|
|
30
|
+
export async function walletList(options = {}) {
|
|
31
|
+
const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
|
|
32
|
+
const resolved = await requireAccountCode();
|
|
33
|
+
const stored = resolved.source === "file" || resolved.source === "file-locked" ? readCredentialsFile() : null;
|
|
34
|
+
const server = resolveServer(options.server ?? stored?.server);
|
|
35
|
+
const network = resolveNetwork(server, options.network ?? stored?.network);
|
|
36
|
+
const walType = walCoinType(network);
|
|
37
|
+
const settings = await (options.readWalletSettings ?? (() => walletSettings(options)))();
|
|
38
|
+
const open = options.openChain ??
|
|
39
|
+
(async (net, addr) => (await import("../wallet-chain.js")).chainReader(net, addr));
|
|
40
|
+
const history = options.hasHistory ??
|
|
41
|
+
(async (net, addr) => (await import("../wallet-list-chain.js")).hasHistory(net, addr));
|
|
42
|
+
// Every number the walk asked about, so nothing is derived or read twice.
|
|
43
|
+
const seen = new Map();
|
|
44
|
+
const scan = await discoverWallets(async (index) => {
|
|
45
|
+
const address = await walletAddress(resolved.code, index);
|
|
46
|
+
const balances = await readBalances(await open(network, address), walType);
|
|
47
|
+
seen.set(index, { index, address, balances });
|
|
48
|
+
const held = (b) => b.read && b.baseUnits > 0n;
|
|
49
|
+
if (held(balances.sui) || held(balances.wal))
|
|
50
|
+
return true;
|
|
51
|
+
return history(network, address);
|
|
52
|
+
}, { count: settings.count });
|
|
53
|
+
// What is printed: the wallets this account has made, plus any the walk found beyond them. The
|
|
54
|
+
// rest of the walk is the gap — empty wallets nobody has asked for.
|
|
55
|
+
const rows = [...seen.values()]
|
|
56
|
+
.filter((row) => row.index < settings.count || scan.used.includes(row.index))
|
|
57
|
+
.sort((a, b) => a.index - b.index);
|
|
58
|
+
const unread = rows.some((row) => !row.balances.sui.read || !row.balances.wal.read);
|
|
59
|
+
if (options.json === true) {
|
|
60
|
+
say(JSON.stringify({
|
|
61
|
+
network,
|
|
62
|
+
active: settings.active,
|
|
63
|
+
scanned: scan.scanned,
|
|
64
|
+
wallets: rows.map((row) => ({
|
|
65
|
+
index: row.index,
|
|
66
|
+
address: row.address,
|
|
67
|
+
pays: row.index === settings.active,
|
|
68
|
+
sui: asJson(row.balances.sui, SUI_COIN_TYPE),
|
|
69
|
+
wal: asJson(row.balances.wal, walType),
|
|
70
|
+
})),
|
|
71
|
+
}));
|
|
72
|
+
return unread ? 1 : 0;
|
|
73
|
+
}
|
|
74
|
+
// ⚠ The columns are laid out with the same widths the rows use, from the same constants — a
|
|
75
|
+
// header written out by hand drifts one space at a time until nothing lines up.
|
|
76
|
+
say(`${"Wallet".padEnd(NUMBER_W)}${"Address".padEnd(ADDRESS_W)}${"SUI".padEnd(AMOUNT_W)}WAL`);
|
|
77
|
+
for (const row of rows) {
|
|
78
|
+
const pays = row.index === settings.active ? " pays" : "";
|
|
79
|
+
say(`${String(row.index).padEnd(NUMBER_W)}${row.address.padEnd(ADDRESS_W)}` +
|
|
80
|
+
`${inWords(row.balances.sui).padEnd(AMOUNT_W)}${inWords(row.balances.wal)}${pays}`);
|
|
81
|
+
}
|
|
82
|
+
say(``);
|
|
83
|
+
say(` Every wallet above comes from this NMTS key, at the number in the first column. The one`);
|
|
84
|
+
say(` marked "pays" is the one storage is paid from — \`${BINARY_NAME} wallet use <number>\` moves it,`);
|
|
85
|
+
say(` on this account rather than on this machine. Numbers come from the key, so a wallet`);
|
|
86
|
+
say(` cannot be deleted.`);
|
|
87
|
+
say(` The list stops after twenty unused wallets in a row; a wallet further out than that is`);
|
|
88
|
+
say(` reached by its number.`);
|
|
89
|
+
if (unread) {
|
|
90
|
+
say(` A balance that could not be read is printed as that and never as zero, and this command`);
|
|
91
|
+
say(` exits non-zero when it happens.`);
|
|
92
|
+
}
|
|
93
|
+
return unread ? 1 : 0;
|
|
94
|
+
}
|
|
95
|
+
/** The account's own numbers, read out of the sealed file list. */
|
|
96
|
+
async function walletSettings(options) {
|
|
97
|
+
const session = await openSession({ server: options.server, network: options.network });
|
|
98
|
+
const list = await readFileList(session.server, session.apiKey, session.code, session.accountId);
|
|
99
|
+
const settings = list.manifest?.settings;
|
|
100
|
+
return { active: activeWalletOf(settings), count: walletCountOf(settings) };
|
|
101
|
+
}
|
|
102
|
+
/** One balance, for a person. The same two sentences `nmts wallet` prints. */
|
|
103
|
+
function inWords(balance) {
|
|
104
|
+
return balance.read ? coinAmount(balance.baseUnits) : `⛔ could not be read`;
|
|
105
|
+
}
|
|
106
|
+
/** One balance, for a program. `baseUnits` is a string: a balance outruns a JSON number. */
|
|
107
|
+
function asJson(balance, coinType) {
|
|
108
|
+
if (!balance.read)
|
|
109
|
+
return { coinType, read: false, error: balance.why };
|
|
110
|
+
return {
|
|
111
|
+
coinType,
|
|
112
|
+
read: true,
|
|
113
|
+
baseUnits: balance.baseUnits.toString(),
|
|
114
|
+
amount: coinAmount(balance.baseUnits),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -12,6 +12,10 @@ export interface WalletSendOptions {
|
|
|
12
12
|
dryRun?: boolean;
|
|
13
13
|
/** A ceiling on the fee, in SUI ("0.01"). Clamped to the usable range, never refused. */
|
|
14
14
|
feeCap?: string | undefined;
|
|
15
|
+
/** `--wallet N`: which wallet pays, this run only. Absent = the account's own number. */
|
|
16
|
+
wallet?: string | undefined;
|
|
17
|
+
/** ⚠ A SEAM, NOT AN OPTION — where that number is read from (`wallet-pay-index.ts`). */
|
|
18
|
+
readActiveWallet?: () => Promise<number>;
|
|
15
19
|
/** The instant to measure the wallet agreement against. */
|
|
16
20
|
now?: number;
|
|
17
21
|
/** ⚠ A SEAM, NOT AN OPTION — no flag reaches it. */
|
|
@@ -21,6 +21,7 @@ import { resolveServer } from "../server.js";
|
|
|
21
21
|
import { explorerTxUrl } from "../shared/lib/wallet/activity.js";
|
|
22
22
|
import { clampGasBudgetMist, maxSendableBaseUnits, parseTokenAmountToBaseUnits, validateSendForm, } from "../shared/lib/wallet/send-rules.js";
|
|
23
23
|
import { coinAmount, walCoinType, walletAddress } from "../wallet.js";
|
|
24
|
+
import { payingWalletIndex } from "../wallet-pay-index.js";
|
|
24
25
|
import { recordWalletSpend, requireWalletGrant } from "../wallet-grant.js";
|
|
25
26
|
const COIN_WORDS = {
|
|
26
27
|
invalidAddress: "That is not a Sui address: it is 0x followed by 64 hexadecimal characters.",
|
|
@@ -48,7 +49,10 @@ export async function walletSend(operands, options = {}) {
|
|
|
48
49
|
});
|
|
49
50
|
}
|
|
50
51
|
const resolved = await requireAccountCode();
|
|
51
|
-
|
|
52
|
+
// ⛔ WHICH WALLET PAYS, FIRST — everything below is about one address: the balances that are
|
|
53
|
+
// read, the fee that is measured, the line the review prints, and the key that signs.
|
|
54
|
+
const wallet = await payingWalletIndex(options);
|
|
55
|
+
const address = await walletAddress(resolved.code, wallet);
|
|
52
56
|
const stored = resolved.source === "file" || resolved.source === "file-locked" ? readCredentialsFile() : null;
|
|
53
57
|
const server = resolveServer(options.server ?? stored?.server);
|
|
54
58
|
const network = resolveNetwork(server, options.network ?? stored?.network);
|
|
@@ -140,7 +144,7 @@ export async function walletSend(operands, options = {}) {
|
|
|
140
144
|
requireWalletGrant("send", spend, new Date(options.now ?? Date.now()));
|
|
141
145
|
// ⑦ The signature.
|
|
142
146
|
const sign = options.sign ?? (await import("../wallet-sign.js")).signTransfer;
|
|
143
|
-
const digest = await sign({ network, code: resolved.code, shape });
|
|
147
|
+
const digest = await sign({ network, code: resolved.code, wallet, shape });
|
|
144
148
|
recordWalletSpend(spend);
|
|
145
149
|
if (options.json) {
|
|
146
150
|
say(JSON.stringify({ ...facts, signed: true, digest, explorerUrl: explorerTxUrl(digest, network) }));
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// `nmts wallet use <number>` — which of this key's wallets pays for storage from now on.
|
|
2
|
+
//
|
|
3
|
+
// ⛔ IT WRITES THE ACCOUNT'S CHOICE, NOT THIS MACHINE'S. The number lives inside the sealed file
|
|
4
|
+
// list (`activeWallet`), where the server cannot read it and where a phone, a laptop and this
|
|
5
|
+
// tool all find the same answer. That is the whole point: money leaving from one address on one
|
|
6
|
+
// device and another address on another is how a balance goes missing without anything failing.
|
|
7
|
+
//
|
|
8
|
+
// ⛔ THE LIST IS WHERE THE SETTING LIVES, so an account with no list has nowhere to put it — the
|
|
9
|
+
// same refusal `nmts padding` gives, for the same reason, and the same way out: upload once.
|
|
10
|
+
//
|
|
11
|
+
// ⛔ NOTHING IS DELETED AND NOTHING IS CREATED. Every number a key can derive already exists
|
|
12
|
+
// (NCF-3 §1.3); this says which of them the next payment comes out of, and takes the count up
|
|
13
|
+
// with it so every screen lists the wallet it just named.
|
|
14
|
+
import { NmtsError } from "../errors.js";
|
|
15
|
+
import { readFileList } from "../manifest.js";
|
|
16
|
+
import { applyManyToList } from "../manifest-write.js";
|
|
17
|
+
import { BINARY_NAME } from "../product.js";
|
|
18
|
+
import { openSession } from "../session.js";
|
|
19
|
+
import { walletIndexOf } from "../wallet-pay-index.js";
|
|
20
|
+
import { walletAddress } from "../wallet.js";
|
|
21
|
+
export async function walletUse(said, options = {}) {
|
|
22
|
+
const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
|
|
23
|
+
if (said === undefined || said === "") {
|
|
24
|
+
throw new NmtsError(`Say which wallet: \`${BINARY_NAME} wallet use <number>\`.`, {
|
|
25
|
+
exitCode: 2,
|
|
26
|
+
nextStep: `\`${BINARY_NAME} wallet list\` shows this key's wallets and which one pays now.`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// ⛔ BEFORE THE NETWORK. A misspelled number is a command line to fix, not a question to ask the
|
|
30
|
+
// server, and a run that opened a session first would refuse for the wrong reason.
|
|
31
|
+
const index = walletIndexOf(said);
|
|
32
|
+
const session = await openSession({ server: options.server, network: options.network });
|
|
33
|
+
const before = await readFileList(session.server, session.apiKey, session.code, session.accountId);
|
|
34
|
+
if (before.manifest === null) {
|
|
35
|
+
throw new NmtsError(`This account has no file list yet; which wallet pays lives in the list, and there is nothing to write it into.`, { exitCode: 4, nextStep: `Upload once (\`${BINARY_NAME} put\`) and set it after.` });
|
|
36
|
+
}
|
|
37
|
+
const result = await applyManyToList(session, () => [], { activeWallet: index });
|
|
38
|
+
const address = await walletAddress(session.code, index);
|
|
39
|
+
if (options.json === true) {
|
|
40
|
+
say(JSON.stringify({ wallet: index, address, changed: result.changed }));
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
if (!result.changed) {
|
|
44
|
+
say(`Wallet ${index} was already the one that pays. Nothing changed.`);
|
|
45
|
+
say(`Address ${address}`);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
say(`Wallet ${index} pays from now on.`);
|
|
49
|
+
say(`Address ${address}`);
|
|
50
|
+
say(``);
|
|
51
|
+
say(` Storage paid from this account's wallet now leaves this address, on every device — the`);
|
|
52
|
+
say(` number rides in the sealed file list, not on this machine. What is already stored is`);
|
|
53
|
+
say(` unaffected, and the wallets you were using keep whatever is in them.`);
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
@@ -7,6 +7,10 @@ export interface WalletOptions {
|
|
|
7
7
|
write?: (line: string) => void;
|
|
8
8
|
/** `wallet address --qr`: the address as a code a phone can scan, drawn in the terminal. */
|
|
9
9
|
qr?: boolean;
|
|
10
|
+
/** `wallet address --index N`: which of this key's wallets to print. Absent = the first one. */
|
|
11
|
+
index?: string | undefined;
|
|
12
|
+
/** `wallet send`/`donate --wallet N`: which wallet pays, this run only (`wallet-pay-index.ts`). */
|
|
13
|
+
wallet?: string | undefined;
|
|
10
14
|
/** `wallet send`: the operands after "send", and its flags (`wallet-send.ts`). */
|
|
11
15
|
rest?: readonly string[];
|
|
12
16
|
yes?: boolean;
|
package/dist/commands/wallet.js
CHANGED
|
@@ -22,9 +22,10 @@ import { NmtsError } from "../errors.js";
|
|
|
22
22
|
import { resolveNetwork } from "../network.js";
|
|
23
23
|
import { BINARY_NAME } from "../product.js";
|
|
24
24
|
import { resolveServer } from "../server.js";
|
|
25
|
-
import {
|
|
25
|
+
import { walletIndexOf } from "../wallet-pay-index.js";
|
|
26
|
+
import { BUILT_IN_WALLET_INDEX, coinAmount, readBalances, walCoinType, walletAddress, SUI_COIN_TYPE, } from "../wallet.js";
|
|
26
27
|
/** What the operand may say. Anything else is a command line to correct, not a guess to act on. */
|
|
27
|
-
const MODES = ["address", "balance", "activity", "storage", "send", "donate", "swap", "hall"];
|
|
28
|
+
const MODES = ["address", "balance", "list", "use", "activity", "storage", "send", "donate", "swap", "hall"];
|
|
28
29
|
function modeOf(what) {
|
|
29
30
|
if (what === undefined)
|
|
30
31
|
return "balance";
|
|
@@ -33,7 +34,9 @@ function modeOf(what) {
|
|
|
33
34
|
return found;
|
|
34
35
|
throw new NmtsError(`\`${BINARY_NAME} wallet ${what}\` is not something this command does.`, {
|
|
35
36
|
exitCode: 2,
|
|
36
|
-
nextStep: `\`${BINARY_NAME} wallet\` shows the address and the balances; \`${BINARY_NAME} wallet
|
|
37
|
+
nextStep: `\`${BINARY_NAME} wallet\` shows the address and the balances; \`${BINARY_NAME} wallet list\` ` +
|
|
38
|
+
`shows every wallet this key has and which one pays, and \`${BINARY_NAME} wallet use <number>\` ` +
|
|
39
|
+
`moves that. \`${BINARY_NAME} wallet address\` ` +
|
|
37
40
|
`shows the address alone, without touching a network (add --qr for a code to scan); ` +
|
|
38
41
|
`\`${BINARY_NAME} wallet activity\` lists the recent transactions; \`${BINARY_NAME} wallet storage\` ` +
|
|
39
42
|
`lists the storage resources it holds. None of those signs. \`${BINARY_NAME} wallet send <SUI|WAL> ` +
|
|
@@ -48,6 +51,11 @@ export async function wallet(what, options = {}) {
|
|
|
48
51
|
// The two lists live in files of their own; each reads through a seam of its own.
|
|
49
52
|
if (mode === "activity")
|
|
50
53
|
return (await import("./wallet-activity.js")).walletActivity(options);
|
|
54
|
+
// Every wallet of this key and which one pays: listing reads, choosing writes one line of the list.
|
|
55
|
+
if (mode === "list")
|
|
56
|
+
return (await import("./wallet-list.js")).walletList(options);
|
|
57
|
+
if (mode === "use")
|
|
58
|
+
return (await import("./wallet-use.js")).walletUse(options.rest?.[0], options);
|
|
51
59
|
if (mode === "storage") {
|
|
52
60
|
const [sub, ...more] = options.rest ?? [];
|
|
53
61
|
if (sub === "split" || sub === "merge" || sub === "transfer") {
|
|
@@ -70,13 +78,17 @@ export async function wallet(what, options = {}) {
|
|
|
70
78
|
if (mode === "hall")
|
|
71
79
|
return (await import("./wallet-hall.js")).walletHall(options);
|
|
72
80
|
const resolved = await requireAccountCode();
|
|
73
|
-
|
|
81
|
+
// ⛔ `wallet address --index N` IS OFFLINE — the person named the number, so there is no list to
|
|
82
|
+
// read; without a number it is the first wallet. "Which one pays" is a question the list
|
|
83
|
+
// answers, and `wallet list` is where it is asked.
|
|
84
|
+
const index = options.index === undefined || options.index === "" ? BUILT_IN_WALLET_INDEX : walletIndexOf(options.index);
|
|
85
|
+
const address = await walletAddress(resolved.code, index);
|
|
74
86
|
if (mode === "address") {
|
|
75
87
|
if (options.json) {
|
|
76
|
-
// ⛔ NO `network` FIELD. There is no network in this answer —
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
say(JSON.stringify({ address }));
|
|
88
|
+
// ⛔ NO `network` FIELD. There is no network in this answer — a wallet is the same wallet on
|
|
89
|
+
// every chain — and a field naming one would invite a reader to believe this address was
|
|
90
|
+
// looked up somewhere. The NUMBER is here, because it is what the address came from.
|
|
91
|
+
say(JSON.stringify({ address, index }));
|
|
80
92
|
return 0;
|
|
81
93
|
}
|
|
82
94
|
say(`Address ${address}`);
|
package/dist/extend-plan.d.ts
CHANGED
|
@@ -143,6 +143,8 @@ export type SignExtension = (input: {
|
|
|
143
143
|
network: string;
|
|
144
144
|
/** ⛔ The NMTS key. It never leaves this machine: it derives the wallet and nothing else. */
|
|
145
145
|
code: string;
|
|
146
|
+
/** Which of this key's wallets pays — the account's own number (`wallet-pay-index.ts`). */
|
|
147
|
+
wallet: number;
|
|
146
148
|
objectIds: readonly string[];
|
|
147
149
|
epochs: number;
|
|
148
150
|
}) => Promise<string>;
|
package/dist/help.js
CHANGED
|
@@ -52,7 +52,9 @@ export function helpText(version) {
|
|
|
52
52
|
` refused in mode auto. Read first: terms · privacy · notices`,
|
|
53
53
|
` wallet Show the account's wallet address, and its SUI and WAL balances`,
|
|
54
54
|
` wallet address Just the address, derived on this machine — no network call.`,
|
|
55
|
-
` \`--qr\` draws it as a code a phone can scan`,
|
|
55
|
+
` \`--qr\` draws it as a code a phone can scan; \`--index N\` another wallet`,
|
|
56
|
+
` wallet list Every wallet this key has, with balances, and which one pays`,
|
|
57
|
+
` wallet use <number> Pay from that wallet from now on — on the account, not this machine`,
|
|
56
58
|
` wallet activity The wallet's recent transactions, named where the chain proves it`,
|
|
57
59
|
` wallet storage The storage resources (size × time) the wallet holds, unbound`,
|
|
58
60
|
` wallet storage split <id> --size <n>|--epochs <n> · merge <id> <id> · transfer <id> <address>`,
|
package/dist/index.d.ts
CHANGED
|
@@ -28,15 +28,24 @@ export type { BlobProtocol, UploadApi, UploadResult, UploadStep } from "./upload
|
|
|
28
28
|
export type { PaddingRule } from "./shared/lib/crypto/size-padding.ts";
|
|
29
29
|
export { DEFAULT_PART_BYTES } from "./seal.ts";
|
|
30
30
|
export { createBlobProtocol, readCurrentEpoch } from "./walrus-write.ts";
|
|
31
|
+
export { walletPut } from "./upload-wallet-put.ts";
|
|
32
|
+
export type { WalletPutContext, WalletPutFile, WalletPutOutcome, WalletPutReview, WalletPutSeams, } from "./upload-wallet-put.ts";
|
|
33
|
+
export { DEFAULT_UPLOAD_EPOCHS } from "./upload-wallet-plan.ts";
|
|
34
|
+
export type { PartQuote, StorageChoice, UploadBudget, WalletUploadReads } from "./upload-wallet-plan.ts";
|
|
35
|
+
export type { Spend } from "./wallet-grant.ts";
|
|
31
36
|
export { fetchFile, fetchWithKey } from "./download.ts";
|
|
32
37
|
export type { FetchedFile, FetchInput } from "./download.ts";
|
|
33
38
|
export { fileSink } from "./download-sink.ts";
|
|
34
39
|
export type { PlaintextSink } from "./download-sink.ts";
|
|
35
40
|
export { AGGREGATOR_ENV_VAR, readBlob, RELAY_ENV_VAR, SUI_RPC_ENV_VAR } from "./walrus.ts";
|
|
36
41
|
export type { ReadOptions } from "./walrus.ts";
|
|
37
|
-
export { coinAmount, readBalances, walletAddress } from "./wallet.ts";
|
|
42
|
+
export { coinAmount, readBalances, walCoinType, walletAddress } from "./wallet.ts";
|
|
38
43
|
export type { ChainReader, CoinBalance, WalletBalances } from "./wallet.ts";
|
|
39
44
|
export { chainReader } from "./wallet-chain.ts";
|
|
40
45
|
export { signerAddress, signExtension, signTransfer } from "./wallet-sign.ts";
|
|
41
46
|
export type { SignTransfer } from "./wallet-sign.ts";
|
|
47
|
+
export { activeWalletOf, walletCountOf, WALLET_INDEX_LIMIT } from "./shared/lib/drive/manifest-settings.ts";
|
|
48
|
+
export { discoverWallets, WALLET_SCAN_GAP } from "./shared/lib/wallet/discover.ts";
|
|
49
|
+
export type { WalletProbe, WalletScan } from "./shared/lib/wallet/discover.ts";
|
|
50
|
+
export { hasHistory } from "./wallet-list-chain.ts";
|
|
42
51
|
export { HOME_URL, PRODUCT_NAME, SOURCE_URL, SUPPORT_EMAIL, VERSION } from "./product.ts";
|
package/dist/index.js
CHANGED
|
@@ -37,13 +37,26 @@ export { clearItemRecord, clearReservation } from "./upload-store.js";
|
|
|
37
37
|
export { UploadError } from "./upload-wire.js";
|
|
38
38
|
export { DEFAULT_PART_BYTES } from "./seal.js";
|
|
39
39
|
export { createBlobProtocol, readCurrentEpoch } from "./walrus-write.js";
|
|
40
|
+
// Uploading paid by the account's OWN WALLET instead of credits: the same seal-buy-push-record
|
|
41
|
+
// path, priced and refused before anything is signed. `nmts put --pay wallet` is this plus a
|
|
42
|
+
// terminal; the spending ledger and the standing gift are the command's and are not here.
|
|
43
|
+
export { walletPut } from "./upload-wallet-put.js";
|
|
44
|
+
export { DEFAULT_UPLOAD_EPOCHS } from "./upload-wallet-plan.js";
|
|
40
45
|
// Downloading: fetching sealed parts from the storage network and opening them here.
|
|
41
46
|
export { fetchFile, fetchWithKey } from "./download.js";
|
|
42
47
|
export { fileSink } from "./download-sink.js";
|
|
43
48
|
export { AGGREGATOR_ENV_VAR, readBlob, RELAY_ENV_VAR, SUI_RPC_ENV_VAR } from "./walrus.js";
|
|
44
49
|
// The wallet the NMTS key derives: reading it, and signing with it.
|
|
45
|
-
export { coinAmount, readBalances, walletAddress } from "./wallet.js";
|
|
50
|
+
export { coinAmount, readBalances, walCoinType, walletAddress } from "./wallet.js";
|
|
46
51
|
export { chainReader } from "./wallet-chain.js";
|
|
47
52
|
export { signerAddress, signExtension, signTransfer } from "./wallet-sign.js";
|
|
53
|
+
// WHICH of this key's wallets, and which of them have been used. One key derives a wallet at every
|
|
54
|
+
// index (NCF-3 §1.3), so the account's own number is read out of the sealed list and the rest is a
|
|
55
|
+
// walk. Both are here so that a library built on this package finds the SAME wallets under the same
|
|
56
|
+
// numbers as `nmts wallet list` — a second walk, or a second reading of the setting, would be a
|
|
57
|
+
// second answer to a question that has to have exactly one.
|
|
58
|
+
export { activeWalletOf, walletCountOf, WALLET_INDEX_LIMIT } from "./shared/lib/drive/manifest-settings.js";
|
|
59
|
+
export { discoverWallets, WALLET_SCAN_GAP } from "./shared/lib/wallet/discover.js";
|
|
60
|
+
export { hasHistory } from "./wallet-list-chain.js";
|
|
48
61
|
// What this package is.
|
|
49
62
|
export { HOME_URL, PRODUCT_NAME, SOURCE_URL, SUPPORT_EMAIL, VERSION } from "./product.js";
|
package/dist/main.js
CHANGED
|
@@ -124,7 +124,7 @@ export async function run(argv) {
|
|
|
124
124
|
return await wallet(args.operands[0], {
|
|
125
125
|
server: args.server, network: args.network, json: args.json, qr: args.qr,
|
|
126
126
|
rest: args.operands.slice(1), yes: args.yes, dryRun: args.dryRun, feeCap: args.feeCap,
|
|
127
|
-
to: args.to, venue: args.venue, slippageBps: args.slippageBps, acceptExtremes: args.acceptExtremes, size: args.size, epochs: args.epochs, name: args.name, remove: args.remove,
|
|
127
|
+
to: args.to, venue: args.venue, slippageBps: args.slippageBps, acceptExtremes: args.acceptExtremes, size: args.size, epochs: args.epochs, name: args.name, remove: args.remove, index: args.index, wallet: args.wallet,
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
130
|
case "expiring": {
|
|
@@ -149,7 +149,7 @@ export async function run(argv) {
|
|
|
149
149
|
epochs: args.epochs,
|
|
150
150
|
dryRun: args.dryRun,
|
|
151
151
|
yes: args.yes,
|
|
152
|
-
json: args.json,
|
|
152
|
+
json: args.json, wallet: args.wallet,
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
case "create": {
|
|
@@ -191,7 +191,7 @@ export async function run(argv) {
|
|
|
191
191
|
pay: args.pay,
|
|
192
192
|
epochs: args.epochs,
|
|
193
193
|
storage: args.storage,
|
|
194
|
-
json: args.json,
|
|
194
|
+
json: args.json, wallet: args.wallet,
|
|
195
195
|
});
|
|
196
196
|
}
|
|
197
197
|
case "push": {
|
|
@@ -208,7 +208,7 @@ export async function run(argv) {
|
|
|
208
208
|
pay: args.pay,
|
|
209
209
|
epochs: args.epochs,
|
|
210
210
|
storage: args.storage,
|
|
211
|
-
json: args.json,
|
|
211
|
+
json: args.json, wallet: args.wallet,
|
|
212
212
|
});
|
|
213
213
|
}
|
|
214
214
|
case "rm": {
|
package/dist/product.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare const BINARY_NAME = "nmts";
|
|
|
9
9
|
* beside it. Here rather than in `main.ts` because the MCP server has to say it too, and a
|
|
10
10
|
* command importing the entry point is a cycle waiting to bite.
|
|
11
11
|
*/
|
|
12
|
-
export declare const VERSION = "0.
|
|
12
|
+
export declare const VERSION = "0.35.0";
|
|
13
13
|
/** Where the product lives, for messages that need to send somebody somewhere real. */
|
|
14
14
|
export declare const HOME_URL = "https://nmts.me";
|
|
15
15
|
/** The source, so a person holding only the built program can find what it was built from. */
|
package/dist/product.js
CHANGED
|
@@ -18,7 +18,7 @@ export const BINARY_NAME = "nmts";
|
|
|
18
18
|
* beside it. Here rather than in `main.ts` because the MCP server has to say it too, and a
|
|
19
19
|
* command importing the entry point is a cycle waiting to bite.
|
|
20
20
|
*/
|
|
21
|
-
export const VERSION = "0.
|
|
21
|
+
export const VERSION = "0.35.0";
|
|
22
22
|
/** Where the product lives, for messages that need to send somebody somewhere real. */
|
|
23
23
|
export const HOME_URL = "https://nmts.me";
|
|
24
24
|
/** The source, so a person holding only the built program can find what it was built from. */
|
package/dist/risk.d.ts
CHANGED
|
@@ -344,6 +344,10 @@ export declare const ACTS: {
|
|
|
344
344
|
readonly what: "Send a gift to the developer from the wallet.";
|
|
345
345
|
readonly asksItself: true;
|
|
346
346
|
};
|
|
347
|
+
readonly "wallet.use": {
|
|
348
|
+
readonly tier: "low";
|
|
349
|
+
readonly what: "Change which of this key's wallets pays for storage.";
|
|
350
|
+
};
|
|
347
351
|
readonly "wallet.hall": {
|
|
348
352
|
readonly tier: "none";
|
|
349
353
|
};
|
package/dist/risk.js
CHANGED
|
@@ -125,6 +125,10 @@ export const ACTS = {
|
|
|
125
125
|
// ⛔ READING THE HALL IS `none` AND LISTING A NAME IS `medium`. The read signs nothing and asks
|
|
126
126
|
// nobody's wallet a question; setting a name PUBLISHES a name beside an address on a public
|
|
127
127
|
// page, which is undone by one more run of the same command but cannot be unseen.
|
|
128
|
+
// ⛔ `list` READS; `use` WRITES — it changes which address every later payment leaves from, on
|
|
129
|
+
// every device. Low rather than high because it signs nothing and moves nothing, and running
|
|
130
|
+
// it again puts the old number back.
|
|
131
|
+
"wallet.use": { tier: "low", what: "Change which of this key's wallets pays for storage." },
|
|
128
132
|
"wallet.hall": { tier: "none" },
|
|
129
133
|
"wallet.hall.set": { tier: "medium", what: "Publish this name beside your wallet's address in the gift hall of fame." },
|
|
130
134
|
"wallet.storage.reshape": { tier: "high", lock: "wallet", what: "Cut or join a storage resource the wallet holds — a signed transaction.", asksItself: true },
|
|
@@ -156,6 +160,8 @@ export function actOf(args) {
|
|
|
156
160
|
// both land on the act that publishes; the bare command is a read like `wallet` itself.
|
|
157
161
|
if (sub === "hall")
|
|
158
162
|
return args.name !== undefined || args.remove ? "wallet.hall.set" : "wallet.hall";
|
|
163
|
+
if (sub === "use")
|
|
164
|
+
return "wallet.use";
|
|
159
165
|
return sub === "send" ? "wallet.send" : sub === "swap" ? "wallet.swap" : sub === "donate" ? "wallet.donate" : "wallet";
|
|
160
166
|
case "put":
|
|
161
167
|
return args.pay === "wallet" ? "put.wallet" : "put";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ManifestEntry } from "./manifest-codec.ts";
|
|
2
2
|
/** One drive edit, in a form that can be replayed onto a newer list. */
|
|
3
3
|
export type ManifestIntent =
|
|
4
4
|
/** Insert or replace by id. Upload-commit and folder-create both land here. */
|
|
@@ -115,37 +115,15 @@ export type ManifestIntent =
|
|
|
115
115
|
*/
|
|
116
116
|
export declare function applyIntent(entries: readonly ManifestEntry[], intent: ManifestIntent): readonly ManifestEntry[];
|
|
117
117
|
/**
|
|
118
|
-
*
|
|
118
|
+
* The account settings' own vocabulary — the padding rule, one settings edit, and how an edit
|
|
119
|
+
* lands — comes back out of `manifest-settings-patch.ts`, beside the fields it names.
|
|
119
120
|
*
|
|
120
|
-
* ⛔
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
121
|
+
* ⛔ RE-EXPORTED RATHER THAN MOVED AWAY. Every caller in both packages spells it `manifest-ops`,
|
|
122
|
+
* and a settings patch IS one of this file's intents as far as they are concerned. It moved
|
|
123
|
+
* because the settings write path had grown into the file's ceiling (`check:size`), and
|
|
124
|
+
* splitting it puts each rule beside the field it is about.
|
|
124
125
|
*/
|
|
125
|
-
export type PaddingMode
|
|
126
|
-
/**
|
|
127
|
-
* One account-settings edit, as DESIRED STATE per field — never "toggle", so replaying it onto a
|
|
128
|
-
* list another device wrote lands the same answer. A default value means absence in the codec.
|
|
129
|
-
*/
|
|
130
|
-
export interface SettingsPatch {
|
|
131
|
-
developerMode?: boolean;
|
|
132
|
-
textScalePct?: number;
|
|
133
|
-
/** Which rule seals future uploads: `"padme"` = the default, `"none"` = the file's exact length. */
|
|
134
|
-
paddingMode?: PaddingMode;
|
|
135
|
-
/** Credits held back with each credit-paid upload, 0 to `DEPOSIT_MAX_CREDITS`. */
|
|
136
|
-
depositDefault?: number;
|
|
137
|
-
/** The standing tip in tenths of a percent (0 = none) and the instant its terms were agreed to (0 clears). */
|
|
138
|
-
tipTenths?: number;
|
|
139
|
-
tipConsentAt?: number;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Apply one settings patch, returning new settings. Returns the SAME reference when nothing
|
|
143
|
-
* changed, so callers can skip a save (a version bump every other device must download).
|
|
144
|
-
*
|
|
145
|
-
* A text scale is CLAMPED into the codec's bounds here — this is the one write path, so a value
|
|
146
|
-
* the slider or the typed field lets through never reaches the wire out of range.
|
|
147
|
-
*/
|
|
148
|
-
export declare function applySettingsPatch(settings: AccountSettings, patch: SettingsPatch): AccountSettings;
|
|
126
|
+
export { applySettingsPatch, type PaddingMode, type SettingsPatch, } from "./manifest-settings-patch.ts";
|
|
149
127
|
/** Replay a whole queue in order. Used to rebuild after a version conflict. */
|
|
150
128
|
export declare function applyIntents(entries: readonly ManifestEntry[], intents: readonly ManifestIntent[]): readonly ManifestEntry[];
|
|
151
129
|
/**
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Runtime import (relative + .ts — the header's node --test rule) for the bounds the patch obeys.
|
|
2
|
-
import { TEXT_SCALE_DEFAULT_PCT, TEXT_SCALE_MAX_PCT, TEXT_SCALE_MIN_PCT, } from "./manifest-codec.js";
|
|
3
|
-
import { applyDepositPatch, applyTipPatch } from "./manifest-settings.js";
|
|
4
1
|
/**
|
|
5
2
|
* Apply one intent, returning a new list. The input is never mutated: the store keeps the
|
|
6
3
|
* pre-save snapshot around to rebuild from after a version conflict.
|
|
@@ -160,44 +157,15 @@ function withShares(e, shares, at) {
|
|
|
160
157
|
return { ...e, shares, updatedAt: at };
|
|
161
158
|
}
|
|
162
159
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
160
|
+
* The account settings' own vocabulary — the padding rule, one settings edit, and how an edit
|
|
161
|
+
* lands — comes back out of `manifest-settings-patch.ts`, beside the fields it names.
|
|
165
162
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
163
|
+
* ⛔ RE-EXPORTED RATHER THAN MOVED AWAY. Every caller in both packages spells it `manifest-ops`,
|
|
164
|
+
* and a settings patch IS one of this file's intents as far as they are concerned. It moved
|
|
165
|
+
* because the settings write path had grown into the file's ceiling (`check:size`), and
|
|
166
|
+
* splitting it puts each rule beside the field it is about.
|
|
168
167
|
*/
|
|
169
|
-
export
|
|
170
|
-
const next = { ...settings };
|
|
171
|
-
if (patch.developerMode !== undefined) {
|
|
172
|
-
if (patch.developerMode)
|
|
173
|
-
next.developerMode = true;
|
|
174
|
-
else
|
|
175
|
-
delete next.developerMode;
|
|
176
|
-
}
|
|
177
|
-
if (patch.paddingMode !== undefined) {
|
|
178
|
-
// The default is spelled as absence, like every other field here — so two devices that both
|
|
179
|
-
// "choose the default" write the same bytes and neither bumps the list's version.
|
|
180
|
-
if (patch.paddingMode === "pow2" || patch.paddingMode === "none")
|
|
181
|
-
next.paddingMode = patch.paddingMode;
|
|
182
|
-
else
|
|
183
|
-
delete next.paddingMode;
|
|
184
|
-
}
|
|
185
|
-
if (patch.textScalePct !== undefined && Number.isFinite(patch.textScalePct)) {
|
|
186
|
-
const pct = Math.round(Math.min(TEXT_SCALE_MAX_PCT, Math.max(TEXT_SCALE_MIN_PCT, patch.textScalePct)));
|
|
187
|
-
if (pct === TEXT_SCALE_DEFAULT_PCT)
|
|
188
|
-
delete next.textScalePct;
|
|
189
|
-
else
|
|
190
|
-
next.textScalePct = pct;
|
|
191
|
-
}
|
|
192
|
-
applyDepositPatch(next, patch.depositDefault);
|
|
193
|
-
applyTipPatch(next, patch.tipTenths, patch.tipConsentAt);
|
|
194
|
-
const same = (next.developerMode === true) === (settings.developerMode === true) &&
|
|
195
|
-
next.paddingMode === settings.paddingMode && next.depositDefault === settings.depositDefault &&
|
|
196
|
-
next.textScalePct === settings.textScalePct &&
|
|
197
|
-
next.tipTenths === settings.tipTenths &&
|
|
198
|
-
next.tipConsentAt === settings.tipConsentAt;
|
|
199
|
-
return same ? settings : next;
|
|
200
|
-
}
|
|
168
|
+
export { applySettingsPatch, } from "./manifest-settings-patch.js";
|
|
201
169
|
/** Replay a whole queue in order. Used to rebuild after a version conflict. */
|
|
202
170
|
export function applyIntents(entries, intents) {
|
|
203
171
|
return intents.reduce(applyIntent, entries);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type AccountSettings } from "./manifest-settings.ts";
|
|
2
|
+
/** Folds a deposit patch into a settings copy: out-of-range is clamped, the full deposit clears. */
|
|
3
|
+
export declare function applyDepositPatch(next: AccountSettings, depositDefault?: number): void;
|
|
4
|
+
/** Folds a tip patch into a settings copy: 0 clears, above the cap is capped, fractions are rounded. */
|
|
5
|
+
export declare function applyTipPatch(next: AccountSettings, tipTenths?: number, tipConsentAt?: number): void;
|
|
6
|
+
/**
|
|
7
|
+
* Folds a wallet patch into a settings copy: out-of-range is clamped, wallet 0 and a list of one
|
|
8
|
+
* clear, and the count is left holding the paying wallet.
|
|
9
|
+
*
|
|
10
|
+
* ⛔ THE COUNT FOLLOWS THE NUMBER, in this one write path. `wallet use 7` on a list of three
|
|
11
|
+
* wallets means the person is paying from a wallet the screen would not have drawn; raising the
|
|
12
|
+
* count here is what makes the two facts agree on every device at once, rather than in whichever
|
|
13
|
+
* screen happens to notice.
|
|
14
|
+
*/
|
|
15
|
+
export declare function applyWalletPatch(next: AccountSettings, activeWallet?: number, walletCount?: number): void;
|
|
16
|
+
/**
|
|
17
|
+
* Which size-padding rule an account seals its next upload under.
|
|
18
|
+
*
|
|
19
|
+
* ⛔ DECLARED BESIDE THE FIELD THAT CARRIES IT, not in `lib/crypto/padding.ts` where the padding
|
|
20
|
+
* itself lives — `padding.ts` imports it through `manifest-ops.ts`, which re-exports it. The
|
|
21
|
+
* direction matters: this file is copied byte-for-byte into the `nmts` command-line package,
|
|
22
|
+
* and a type reaching out of it into the crypto tree would drag that whole tree along with it
|
|
23
|
+
* for the sake of two string literals.
|
|
24
|
+
*/
|
|
25
|
+
export type PaddingMode = "padme" | "pow2" | "none";
|
|
26
|
+
/**
|
|
27
|
+
* One account-settings edit, as DESIRED STATE per field — never "toggle", so replaying it onto a
|
|
28
|
+
* list another device wrote lands the same answer. A default value means absence in the codec.
|
|
29
|
+
*/
|
|
30
|
+
export interface SettingsPatch {
|
|
31
|
+
developerMode?: boolean;
|
|
32
|
+
textScalePct?: number;
|
|
33
|
+
/** Which rule seals future uploads: `"padme"` = the default, `"none"` = the file's exact length. */
|
|
34
|
+
paddingMode?: PaddingMode;
|
|
35
|
+
/** Credits held back with each credit-paid upload, 0 to `DEPOSIT_MAX_CREDITS`. */
|
|
36
|
+
depositDefault?: number;
|
|
37
|
+
/** The standing tip in tenths of a percent (0 = none) and the instant its terms were agreed to (0 clears). */
|
|
38
|
+
tipTenths?: number;
|
|
39
|
+
tipConsentAt?: number;
|
|
40
|
+
/** Which wallet pays, by index (0 = the wallet every account starts with). */
|
|
41
|
+
activeWallet?: number;
|
|
42
|
+
/** How many wallets this account's list holds. Raised to hold `activeWallet`, never lowered. */
|
|
43
|
+
walletCount?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Apply one settings patch, returning new settings. Returns the SAME reference when nothing
|
|
47
|
+
* changed, so callers can skip a save (a version bump every other device must download).
|
|
48
|
+
*
|
|
49
|
+
* A text scale is CLAMPED into the codec's bounds here — this is the one write path, so a value
|
|
50
|
+
* the slider or the typed field lets through never reaches the wire out of range.
|
|
51
|
+
*/
|
|
52
|
+
export declare function applySettingsPatch(settings: AccountSettings, patch: SettingsPatch): AccountSettings;
|