@needmoretruth/nmts-cli 0.34.4 → 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.
Files changed (63) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.ko.md +15 -15
  3. package/README.md +1 -1
  4. package/dist/arg-options.d.ts +167 -0
  5. package/dist/arg-options.js +10 -0
  6. package/dist/args.d.ts +2 -157
  7. package/dist/args.js +2 -0
  8. package/dist/artifact-about.d.ts +1 -1
  9. package/dist/commands/extend.d.ts +2 -0
  10. package/dist/commands/extend.js +8 -1
  11. package/dist/commands/push-wallet.js +6 -0
  12. package/dist/commands/push.d.ts +2 -0
  13. package/dist/commands/put-payer.d.ts +8 -0
  14. package/dist/commands/put-payer.js +41 -0
  15. package/dist/commands/put-wallet.d.ts +9 -3
  16. package/dist/commands/put-wallet.js +105 -185
  17. package/dist/commands/put.d.ts +5 -7
  18. package/dist/commands/put.js +3 -26
  19. package/dist/commands/wallet-donate.d.ts +3 -0
  20. package/dist/commands/wallet-donate.js +6 -2
  21. package/dist/commands/wallet-list.d.ts +18 -0
  22. package/dist/commands/wallet-list.js +116 -0
  23. package/dist/commands/wallet-send.d.ts +4 -0
  24. package/dist/commands/wallet-send.js +6 -2
  25. package/dist/commands/wallet-use.d.ts +7 -0
  26. package/dist/commands/wallet-use.js +55 -0
  27. package/dist/commands/wallet.d.ts +4 -0
  28. package/dist/commands/wallet.js +20 -8
  29. package/dist/extend-plan.d.ts +2 -0
  30. package/dist/help.js +3 -1
  31. package/dist/index.d.ts +10 -1
  32. package/dist/index.js +14 -1
  33. package/dist/main.js +4 -4
  34. package/dist/product.d.ts +1 -1
  35. package/dist/product.js +1 -1
  36. package/dist/risk.d.ts +4 -0
  37. package/dist/risk.js +6 -0
  38. package/dist/shared/lib/drive/manifest-ops.d.ts +8 -30
  39. package/dist/shared/lib/drive/manifest-ops.js +7 -39
  40. package/dist/shared/lib/drive/manifest-settings-patch.d.ts +52 -0
  41. package/dist/shared/lib/drive/manifest-settings-patch.js +110 -0
  42. package/dist/shared/lib/drive/manifest-settings.d.ts +55 -4
  43. package/dist/shared/lib/drive/manifest-settings.js +66 -28
  44. package/dist/shared/lib/wallet/discover.d.ts +54 -0
  45. package/dist/shared/lib/wallet/discover.js +66 -0
  46. package/dist/standing-tip.d.ts +3 -0
  47. package/dist/standing-tip.js +1 -0
  48. package/dist/upload-wallet-put.d.ts +118 -0
  49. package/dist/upload-wallet-put.js +221 -0
  50. package/dist/upload-wallet.d.ts +3 -0
  51. package/dist/upload-wallet.js +2 -0
  52. package/dist/wallet-list-chain.d.ts +4 -0
  53. package/dist/wallet-list-chain.js +21 -0
  54. package/dist/wallet-pay-index.d.ts +21 -0
  55. package/dist/wallet-pay-index.js +68 -0
  56. package/dist/wallet-sign-seams.d.ts +57 -0
  57. package/dist/wallet-sign-seams.js +16 -0
  58. package/dist/wallet-sign.d.ts +3 -53
  59. package/dist/wallet-sign.js +22 -15
  60. package/dist/wallet.d.ts +13 -13
  61. package/dist/wallet.js +15 -15
  62. package/docs/commands/wallet.md +18 -4
  63. package/package.json +1 -1
@@ -51,12 +51,58 @@ export interface AccountSettings {
51
51
  * = never agreed: raising the tip above 0 asks for that agreement once, and later changes do not.
52
52
  */
53
53
  tipConsentAt?: number;
54
+ /**
55
+ * WHICH WALLET PAYS — the index the NMTS key derives it at (NCF-3 §1.3, `walletSeed(N)`). Absent
56
+ * = 0, the wallet every account has had since the beginning.
57
+ *
58
+ * ⛔ HERE, IN THE SEALED LIST, AND NOT BESIDE THE derived/imported SWITCH. That switch is about
59
+ * this DEVICE (which key this browser opens), so it lives in device storage; the number is about
60
+ * the ACCOUNT — it decides which address the storage is paid from, and a phone and a laptop that
61
+ * disagreed about it would spend from two different balances for one person.
62
+ *
63
+ * Whole, 0 to just under 2^31: the format derives a wallet at every index and this is the range
64
+ * an index is written in.
65
+ */
66
+ activeWallet?: number;
67
+ /**
68
+ * HOW MANY WALLETS THIS ACCOUNT HAS MADE — the list the wallets screen draws, 1 to 1000. Absent
69
+ * = 1, the one wallet an account starts with.
70
+ *
71
+ * ⚠ IT IS A COUNT, NOT A SET. Numbers come from the key, so a wallet cannot be deleted and the
72
+ * list is always 0…count-1; "making the next wallet" is this number going up by one, and a scan
73
+ * that finds a funded wallet further out pulls it up to that number + 1.
74
+ */
75
+ walletCount?: number;
54
76
  }
55
77
  /** The sanity bounds a stored text scale must sit in to be USED. One place; codec and UI agree. */
56
78
  export declare const TEXT_SCALE_MIN_PCT = 80;
57
79
  export declare const TEXT_SCALE_MAX_PCT = 160;
58
80
  /** Follow the device. Not written to the wire — absence is the only spelling of it. */
59
81
  export declare const TEXT_SCALE_DEFAULT_PCT = 100;
82
+ /**
83
+ * The index range a wallet number is written in: whole, 0 to just under 2^31.
84
+ *
85
+ * ⛔ THE CEILING IS THE WIRE'S, NOT THE FORMAT'S. `walletSeed(N)` is defined for every N the
86
+ * engine can be handed; what is bounded here is what this build will WRITE and read back, so a
87
+ * number some other build miswrote cannot come back as something no screen can draw.
88
+ */
89
+ export declare const WALLET_INDEX_LIMIT: number;
90
+ /** The wallet an account pays from when nobody chose. Not written to the wire — absence spells it. */
91
+ export declare const ACTIVE_WALLET_DEFAULT = 0;
92
+ /** The most wallets one account's list holds. */
93
+ export declare const WALLET_COUNT_MAX = 1000;
94
+ /** What an account's list holds before anybody made a second one. Absence spells it. */
95
+ export declare const WALLET_COUNT_DEFAULT = 1;
96
+ /** Which wallet this account pays from. Absence is wallet 0, never "unknown". */
97
+ export declare function activeWalletOf(settings: AccountSettings | null | undefined): number;
98
+ /**
99
+ * How many wallets this account's list holds.
100
+ *
101
+ * ⛔ THE INVARIANT LIVES HERE: the paying wallet is always IN the list. A stored count that does
102
+ * not reach the active number is raised to hold it — never the other way round, because lowering
103
+ * it would hide a wallet somebody is paying from, and a wallet cannot be deleted anyway.
104
+ */
105
+ export declare function walletCountOf(settings: AccountSettings | null | undefined): number;
60
106
  export interface WireSettings {
61
107
  /** developerMode. */
62
108
  dm?: 1;
@@ -84,6 +130,15 @@ export interface WireSettings {
84
130
  tp?: number;
85
131
  /** tipConsentAt. */
86
132
  tc?: number;
133
+ /** activeWallet, present only when it is not wallet 0. */
134
+ aw?: number;
135
+ /**
136
+ * walletCount, present only when the account has made more than one.
137
+ *
138
+ * ⚠ A count above the ceiling is DROPPED on both sides, and nothing is lost by that: the read
139
+ * below raises the count to hold `aw` again, so the paying wallet stays in the list either way.
140
+ */
141
+ wc?: number;
87
142
  }
88
143
  /** The most a standing tip can be: the whole payment. Above the dial's 10 % it is typed and confirmed. */
89
144
  export declare const TIP_TENTHS_MAX = 1000;
@@ -107,7 +162,3 @@ export declare function settingsToWire(s: AccountSettings | undefined): WireSett
107
162
  * to the device's own size, which is always readable.
108
163
  */
109
164
  export declare function settingsFromWire(w: unknown): AccountSettings | undefined;
110
- /** Folds a deposit patch into a settings copy: out-of-range is clamped, the full deposit clears. */
111
- export declare function applyDepositPatch(next: AccountSettings, depositDefault?: number): void;
112
- /** Folds a tip patch into a settings copy: 0 clears, above the cap is capped, fractions are rounded. */
113
- export declare function applyTipPatch(next: AccountSettings, tipTenths?: number, tipConsentAt?: number): void;
@@ -14,6 +14,48 @@ export const TEXT_SCALE_MIN_PCT = 80;
14
14
  export const TEXT_SCALE_MAX_PCT = 160;
15
15
  /** Follow the device. Not written to the wire — absence is the only spelling of it. */
16
16
  export const TEXT_SCALE_DEFAULT_PCT = 100;
17
+ /**
18
+ * The index range a wallet number is written in: whole, 0 to just under 2^31.
19
+ *
20
+ * ⛔ THE CEILING IS THE WIRE'S, NOT THE FORMAT'S. `walletSeed(N)` is defined for every N the
21
+ * engine can be handed; what is bounded here is what this build will WRITE and read back, so a
22
+ * number some other build miswrote cannot come back as something no screen can draw.
23
+ */
24
+ export const WALLET_INDEX_LIMIT = 2 ** 31;
25
+ /** The wallet an account pays from when nobody chose. Not written to the wire — absence spells it. */
26
+ export const ACTIVE_WALLET_DEFAULT = 0;
27
+ /** The most wallets one account's list holds. */
28
+ export const WALLET_COUNT_MAX = 1000;
29
+ /** What an account's list holds before anybody made a second one. Absence spells it. */
30
+ export const WALLET_COUNT_DEFAULT = 1;
31
+ /** Is this a wallet index this build writes and reads? */
32
+ function usableWalletIndex(value) {
33
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 && value < WALLET_INDEX_LIMIT;
34
+ }
35
+ /** Is this a wallet count this build writes and reads? */
36
+ function usableWalletCount(value) {
37
+ return (typeof value === "number" &&
38
+ Number.isInteger(value) &&
39
+ value >= WALLET_COUNT_DEFAULT &&
40
+ value <= WALLET_COUNT_MAX);
41
+ }
42
+ /** Which wallet this account pays from. Absence is wallet 0, never "unknown". */
43
+ export function activeWalletOf(settings) {
44
+ const stored = settings?.activeWallet;
45
+ return usableWalletIndex(stored) ? stored : ACTIVE_WALLET_DEFAULT;
46
+ }
47
+ /**
48
+ * How many wallets this account's list holds.
49
+ *
50
+ * ⛔ THE INVARIANT LIVES HERE: the paying wallet is always IN the list. A stored count that does
51
+ * not reach the active number is raised to hold it — never the other way round, because lowering
52
+ * it would hide a wallet somebody is paying from, and a wallet cannot be deleted anyway.
53
+ */
54
+ export function walletCountOf(settings) {
55
+ const stored = settings?.walletCount;
56
+ const held = usableWalletCount(stored) ? stored : WALLET_COUNT_DEFAULT;
57
+ return Math.max(held, activeWalletOf(settings) + 1);
58
+ }
17
59
  /** The most a standing tip can be: the whole payment. Above the dial's 10 % it is typed and confirmed. */
18
60
  export const TIP_TENTHS_MAX = 1000;
19
61
  /**
@@ -59,12 +101,18 @@ export function settingsToWire(s) {
59
101
  if (typeof s.tipConsentAt === "number" && Number.isFinite(s.tipConsentAt) && s.tipConsentAt > 0) {
60
102
  w.tc = Math.round(s.tipConsentAt);
61
103
  }
104
+ if (usableWalletIndex(s.activeWallet) && s.activeWallet !== ACTIVE_WALLET_DEFAULT)
105
+ w.aw = s.activeWallet;
106
+ if (usableWalletCount(s.walletCount) && s.walletCount !== WALLET_COUNT_DEFAULT)
107
+ w.wc = s.walletCount;
62
108
  return w.dm !== undefined ||
63
109
  w.tx !== undefined ||
64
110
  w.pd !== undefined ||
65
111
  w.dd !== undefined ||
66
112
  w.tp !== undefined ||
67
- w.tc !== undefined
113
+ w.tc !== undefined ||
114
+ w.aw !== undefined ||
115
+ w.wc !== undefined
68
116
  ? w
69
117
  : null;
70
118
  }
@@ -84,6 +132,8 @@ export function settingsFromWire(w) {
84
132
  const dd = Reflect.get(w, "dd");
85
133
  const tp = Reflect.get(w, "tp");
86
134
  const tc = Reflect.get(w, "tc");
135
+ const aw = Reflect.get(w, "aw");
136
+ const wc = Reflect.get(w, "wc");
87
137
  const s = {};
88
138
  if (dm === 1)
89
139
  s.developerMode = true;
@@ -109,38 +159,26 @@ export function settingsFromWire(w) {
109
159
  s.tipTenths = tp;
110
160
  if (typeof tc === "number" && Number.isFinite(tc) && tc > 0)
111
161
  s.tipConsentAt = Math.round(tc);
162
+ // A wallet number outside the range is DROPPED, not clamped: paying from a wallet some other
163
+ // build miswrote would spend from an address this person has never seen, and wallet 0 is the one
164
+ // every account already has.
165
+ if (usableWalletIndex(aw) && aw !== ACTIVE_WALLET_DEFAULT)
166
+ s.activeWallet = aw;
167
+ if (usableWalletCount(wc) && wc !== WALLET_COUNT_DEFAULT)
168
+ s.walletCount = wc;
169
+ // ⛔ AND THE INVARIANT IS RESTORED HERE, UPWARDS ONLY (`walletCountOf`). A list whose count does
170
+ // not reach the paying wallet would draw a screen the paying wallet is missing from.
171
+ const counted = walletCountOf(s);
172
+ if (counted !== WALLET_COUNT_DEFAULT)
173
+ s.walletCount = counted;
112
174
  return s.developerMode !== undefined ||
113
175
  s.textScalePct !== undefined ||
114
176
  s.paddingMode !== undefined ||
115
177
  s.depositDefault !== undefined ||
116
178
  s.tipTenths !== undefined ||
117
- s.tipConsentAt !== undefined
179
+ s.tipConsentAt !== undefined ||
180
+ s.activeWallet !== undefined ||
181
+ s.walletCount !== undefined
118
182
  ? s
119
183
  : undefined;
120
184
  }
121
- /** Folds a deposit patch into a settings copy: out-of-range is clamped, the full deposit clears. */
122
- export function applyDepositPatch(next, depositDefault) {
123
- if (depositDefault === undefined || !Number.isFinite(depositDefault))
124
- return;
125
- const credits = Math.round(Math.min(DEPOSIT_MAX_CREDITS, Math.max(0, depositDefault)));
126
- if (credits === DEPOSIT_DEFAULT_CREDITS)
127
- delete next.depositDefault;
128
- else
129
- next.depositDefault = credits;
130
- }
131
- /** Folds a tip patch into a settings copy: 0 clears, above the cap is capped, fractions are rounded. */
132
- export function applyTipPatch(next, tipTenths, tipConsentAt) {
133
- if (tipTenths !== undefined && Number.isFinite(tipTenths)) {
134
- const t = Math.round(Math.min(TIP_TENTHS_MAX, Math.max(0, tipTenths)));
135
- if (t === 0)
136
- delete next.tipTenths;
137
- else
138
- next.tipTenths = t;
139
- }
140
- if (tipConsentAt !== undefined && Number.isFinite(tipConsentAt)) {
141
- if (tipConsentAt <= 0)
142
- delete next.tipConsentAt;
143
- else
144
- next.tipConsentAt = Math.round(tipConsentAt);
145
- }
146
- }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * How many unused wallets in a row end the walk.
3
+ *
4
+ * ⛔ TWENTY, THE SAME NUMBER EVERY OTHER WALLET USES. Somebody who has funded a wallet in another
5
+ * tool and comes looking for it here has been taught what a scan finds by that tool; a different
6
+ * number here would make the same key look like it holds different wallets in two programs.
7
+ */
8
+ export declare const WALLET_SCAN_GAP = 20;
9
+ /** What a walk found: the wallets that are in use, lowest first, and how many were asked about. */
10
+ export interface WalletScan {
11
+ /** Every index the probe said was in use, ascending. */
12
+ readonly used: readonly number[];
13
+ /** How many times the probe was called — what the walk cost, in questions. */
14
+ readonly scanned: number;
15
+ }
16
+ /**
17
+ * Is this wallet IN USE? Answered by the caller, because what counts as "in use" is a question
18
+ * about a chain and this file has none: a balance in either coin, or any transaction the address
19
+ * has ever been in.
20
+ *
21
+ * ⚠ A PROBE THAT FAILS SHOULD THROW, not answer `false`. "Nobody could ask" and "nothing is there"
22
+ * are opposite facts, and answering the second for the first would quietly end the walk early and
23
+ * report a wallet with money in it as one that does not exist.
24
+ */
25
+ export type WalletProbe = (index: number) => Promise<boolean>;
26
+ /**
27
+ * Walk the wallets of one NMTS key from 0 upwards, and stop when the walk has learned enough.
28
+ *
29
+ * TWO RULES, AND THEY ARE DIFFERENT RULES:
30
+ * · every wallet the account has already MADE is asked about, whatever the answers are. Those
31
+ * wallets are on the person's screen; a walk that skipped one because its neighbour was empty
32
+ * would be reporting about a wallet it never asked about.
33
+ * · past those, the walk keeps going as long as it keeps finding wallets in use, and ends after
34
+ * `gap` unused ones in a row.
35
+ *
36
+ * ⚠ THE RUN CARRIES OVER from the made wallets into the walk past them. A list of thirty wallets
37
+ * nobody ever funded is thirty unused answers, and starting the count again at the end of it
38
+ * would ask for twenty more that nobody has any reason to expect anything from.
39
+ */
40
+ export declare function discoverWallets(probe: WalletProbe, { count, gap }: {
41
+ count: number;
42
+ gap?: number;
43
+ }): Promise<WalletScan>;
44
+ /**
45
+ * How far a walk can reach as it stands — the number a progress line counts towards.
46
+ *
47
+ * ⛔ IT MOVES, because the walk's end moves: finding a wallet in use buys another `gap` questions
48
+ * past it. A progress line drawn against the number the walk STARTED with would sit at "20/20"
49
+ * while the walk kept going, which reads as a screen that has stopped answering.
50
+ */
51
+ export declare function walletScanReach({ count, gap }: {
52
+ count: number;
53
+ gap?: number;
54
+ }, highestUsed: number | null): number;
@@ -0,0 +1,66 @@
1
+ // Finding the wallets an NMTS key has already used — the scan every HD wallet does, and where it
2
+ // stops. ⚠ PUBLISHED — copied byte-for-byte into the `nmts` command-line package; keep comments
3
+ // self-contained English.
4
+ //
5
+ // ⛔ PURE, AND THAT IS THE WHOLE POINT. One NMTS key derives a wallet at every index (NCF-3 §1.3),
6
+ // so "which of them exist" is not a question anybody can answer by looking: nothing is
7
+ // registered anywhere, and an unused wallet and a wallet nobody has funded yet are the same
8
+ // thing. The answer is a WALK — ask about one number, then the next — and the only real decision
9
+ // in it is when to stop. That decision is arithmetic, so it is written here, once, with no
10
+ // chain, no fetch and no clock in it; the browser and the `nmts` command each bring their own
11
+ // `probe` and get the same answer out.
12
+ //
13
+ // ⛔ WHY IT STOPS AT ALL. The index space is unbounded, so a walk that asked for proof of absence
14
+ // would never end. Stopping after a run of unused wallets is what every HD wallet does (the
15
+ // convention BIP-44 §Address gap limit wrote down), and the cost of the rule is stated rather
16
+ // than hidden: a wallet funded further out than the gap is NOT found by a scan. It is not lost —
17
+ // numbers come from the key, so asking for that number directly still opens it.
18
+ /**
19
+ * How many unused wallets in a row end the walk.
20
+ *
21
+ * ⛔ TWENTY, THE SAME NUMBER EVERY OTHER WALLET USES. Somebody who has funded a wallet in another
22
+ * tool and comes looking for it here has been taught what a scan finds by that tool; a different
23
+ * number here would make the same key look like it holds different wallets in two programs.
24
+ */
25
+ export const WALLET_SCAN_GAP = 20;
26
+ /**
27
+ * Walk the wallets of one NMTS key from 0 upwards, and stop when the walk has learned enough.
28
+ *
29
+ * TWO RULES, AND THEY ARE DIFFERENT RULES:
30
+ * · every wallet the account has already MADE is asked about, whatever the answers are. Those
31
+ * wallets are on the person's screen; a walk that skipped one because its neighbour was empty
32
+ * would be reporting about a wallet it never asked about.
33
+ * · past those, the walk keeps going as long as it keeps finding wallets in use, and ends after
34
+ * `gap` unused ones in a row.
35
+ *
36
+ * ⚠ THE RUN CARRIES OVER from the made wallets into the walk past them. A list of thirty wallets
37
+ * nobody ever funded is thirty unused answers, and starting the count again at the end of it
38
+ * would ask for twenty more that nobody has any reason to expect anything from.
39
+ */
40
+ export async function discoverWallets(probe, { count, gap = WALLET_SCAN_GAP }) {
41
+ const used = [];
42
+ let scanned = 0;
43
+ let unusedInARow = 0;
44
+ for (let index = 0; index < count || unusedInARow < gap; index += 1) {
45
+ const inUse = await probe(index);
46
+ scanned += 1;
47
+ if (inUse) {
48
+ used.push(index);
49
+ unusedInARow = 0;
50
+ }
51
+ else {
52
+ unusedInARow += 1;
53
+ }
54
+ }
55
+ return { used, scanned };
56
+ }
57
+ /**
58
+ * How far a walk can reach as it stands — the number a progress line counts towards.
59
+ *
60
+ * ⛔ IT MOVES, because the walk's end moves: finding a wallet in use buys another `gap` questions
61
+ * past it. A progress line drawn against the number the walk STARTED with would sit at "20/20"
62
+ * while the walk kept going, which reads as a screen that has stopped answering.
63
+ */
64
+ export function walletScanReach({ count, gap = WALLET_SCAN_GAP }, highestUsed) {
65
+ return Math.max(count, highestUsed === null ? 0 : highestUsed + 1) + gap;
66
+ }
@@ -15,6 +15,9 @@ export interface StandingTipInput {
15
15
  } | undefined;
16
16
  /** What was just paid for storage, in WAL base units. */
17
17
  paidWalFrost: bigint;
18
+ /** ⛔ The wallet the storage was just paid from — a gift from a different one would come out of
19
+ * a balance nobody was looking at, and the payment above named this one. */
20
+ wallet: number;
18
21
  say: (line: string) => void;
19
22
  /** Seams for tests. */
20
23
  readDonation?: (server: string) => Promise<DonationConfig>;
@@ -37,6 +37,7 @@ export async function standingTipAfter(input) {
37
37
  const digest = await sign({
38
38
  network: input.network,
39
39
  code: input.code,
40
+ wallet: input.wallet,
40
41
  shape: { coin: "WAL", amountBaseUnits: amount, destination: config.devAddress, walType: walCoinType(input.network) },
41
42
  });
42
43
  input.say(` Your standing ${percentText(tenths)} % gift — ${coinAmount(amount)} WAL — went to the developer. Transaction ${digest}`);
@@ -0,0 +1,118 @@
1
+ import type { OnCollision } from "./collision.ts";
2
+ import { type CryptoGlue } from "./crypto.ts";
3
+ import type { Network } from "./network.ts";
4
+ import type { PaddingRule } from "./shared/lib/crypto/size-padding.ts";
5
+ import { type FileUploadStep, type PlaintextSource } from "./upload-file.ts";
6
+ import { type StorageChoice, type UploadBudget, type WalletUploadReads } from "./upload-wallet-plan.ts";
7
+ import type { BlobProtocol, UploadApi } from "./upload-wire.ts";
8
+ import type { Spend } from "./wallet-grant.ts";
9
+ import type { SignBlobCertify, SignBlobRegister } from "./wallet-sign.ts";
10
+ /** The account this upload belongs to, and how it seals. */
11
+ export interface WalletPutContext {
12
+ /** ⛔ The NMTS key. It seals the file AND derives the wallet that pays. It is never written down. */
13
+ code: string;
14
+ apiKey: string;
15
+ server: string;
16
+ network: Network;
17
+ accountId: string;
18
+ crypt: CryptoGlue;
19
+ /** How much of the file goes into one part. */
20
+ partSize: number;
21
+ /** The rounding rule from the account's sealed list — it changes the stored size, so the price. */
22
+ rule: PaddingRule;
23
+ /** What to do about a name already in use. Absent = the machine's setting, as `addEntry` reads it. */
24
+ onCollision?: OnCollision | undefined;
25
+ /**
26
+ * Which of this key's wallets pays, by index (0 = the first one).
27
+ *
28
+ * ⛔ THE CALLER RESOLVES IT, BEFORE ANYTHING IS PRICED. It is the account's own number, kept in
29
+ * the sealed file list (`wallet-pay-index.ts`), and a library that guessed at it here would
30
+ * price one address and sign with another.
31
+ */
32
+ wallet: number;
33
+ }
34
+ /** One file and where it goes — already resolved, because a library resolves nothing by asking. */
35
+ export interface WalletPutFile {
36
+ source: PlaintextSource;
37
+ name: string;
38
+ parentId: string | null;
39
+ /** The destination AS TYPED — part of the reservation key, so both runs of a resume agree. */
40
+ destination: string;
41
+ }
42
+ /** The chain, the signatures, the wire and the four things a caller may be told. */
43
+ export interface WalletPutSeams {
44
+ /** How many of the storage network's epochs to buy. Default `DEFAULT_UPLOAD_EPOCHS`. */
45
+ epochs?: string | number | undefined;
46
+ /** `fit`, `whole`, or a held resource's object id. Absent = buy new storage. */
47
+ storage?: string | undefined;
48
+ /** Stop at the review. Nothing is sealed, signed or sent, and no signer is even loaded. */
49
+ dryRun?: boolean | undefined;
50
+ /** ⚠ A SEAM, NOT AN OPTION — what the chain is asked. */
51
+ readChain?: ((network: Network, relayUrl: string) => WalletUploadReads | Promise<WalletUploadReads>) | undefined;
52
+ /** ⛔ SEPARATE FROM THE READS so a caller can prove the review stops before this. */
53
+ sign?: {
54
+ register: SignBlobRegister;
55
+ certify: SignBlobCertify;
56
+ } | undefined;
57
+ /** ⚠ SEAMS, NOT OPTIONS — the storage-network protocol and the server calls. */
58
+ protocol?: ((network: Network, bodyBytes: number, onSent: (sent: number, total: number) => void) => BlobProtocol & {
59
+ relayUrl: string;
60
+ }) | undefined;
61
+ api?: UploadApi | undefined;
62
+ /** Told as sealed bytes leave for the relay. */
63
+ onProgress?: ((sent: number, total: number) => void) | undefined;
64
+ /** Told about each step as it starts. */
65
+ onStep?: ((step: FileUploadStep) => void) | undefined;
66
+ /** Told the numbers the moment they are known — before the dry run returns and before a refusal. */
67
+ onReview?: ((review: WalletPutReview) => void) | undefined;
68
+ /** ⛔ THE GATE. Throwing here stops the upload with nothing signed. See the module header. */
69
+ agree?: ((review: WalletPutReview) => void) | undefined;
70
+ /** Told what left the wallet, after each signature. */
71
+ onSpend?: ((spend: Spend) => void) | undefined;
72
+ }
73
+ /** What this upload would buy, in the order somebody deciding needs it. Base units throughout. */
74
+ export interface WalletPutReview {
75
+ name: string;
76
+ /** Plaintext bytes. */
77
+ bytes: number;
78
+ /** Bytes the storage network holds, padding and sealing included. */
79
+ sealedBytes: number;
80
+ parts: number;
81
+ epochs: number;
82
+ /** The term in days, read from the network's own clock. */
83
+ days: string;
84
+ /** The epoch the storage runs to. */
85
+ endEpoch: number;
86
+ /** The relay's tip over every part, in MIST. Paid inside the register signatures. */
87
+ tipMist: bigint;
88
+ /** Where the storage comes from. */
89
+ storage: StorageChoice;
90
+ /** Free storage resources the wallet holds when none was asked for; null when unreadable. */
91
+ heldResources: number | null;
92
+ /** The wallet against the price: what is needed, what is held, and the shortfall if there is one. */
93
+ budget: UploadBudget;
94
+ }
95
+ /** A dry run's answer, or a finished upload's. The review is in both, so both report one price. */
96
+ export type WalletPutOutcome = {
97
+ kind: "review";
98
+ review: WalletPutReview;
99
+ } | {
100
+ kind: "uploaded";
101
+ review: WalletPutReview;
102
+ itemId: string;
103
+ /** The name it got — numbered if the one asked for was taken. */
104
+ savedAs: string;
105
+ /** The id of the file this one displaced into the trash, when the machine overwrites. */
106
+ replaced: string | null;
107
+ /** The file-list version this write produced. */
108
+ fileListVersion: number;
109
+ /** True when every part was already signed for by an earlier run: this call spent nothing. */
110
+ resumed: boolean;
111
+ };
112
+ /**
113
+ * Price, agree, sign, upload and record ONE file, paid from the wallet the NMTS key derives.
114
+ *
115
+ * ⛔ THE SIGNING MODULE IS LOADED ONLY AFTER `agree` HAS RETURNED. A dry run and a refusal never
116
+ * bring the code that can spend into memory.
117
+ */
118
+ export declare function walletPut(ctx: WalletPutContext, file: WalletPutFile, seams?: WalletPutSeams): Promise<WalletPutOutcome>;