@needmoretruth/nmts-cli 0.34.2 → 0.34.4
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 +16 -0
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/dist/api-advice.js +4 -0
- package/dist/artifact-about.d.ts +1 -1
- package/dist/product.d.ts +1 -1
- package/dist/product.js +1 -1
- package/dist/wallet-swap-chain.d.ts +16 -2
- package/dist/wallet-swap-chain.js +30 -37
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
Each version's entry is what changed for the person or the program using `nmts`. The product's own
|
|
4
4
|
update history, which covers the site and the server too, is at https://nmts.me/updates.
|
|
5
5
|
|
|
6
|
+
## 0.34.4 — 2026-09-07
|
|
7
|
+
|
|
8
|
+
- The advice for `SPONSORED_IDEM_MISMATCH`: the server now refuses a credit reservation repeated
|
|
9
|
+
under the same key with a different blob (the piece was re-encrypted after it was reserved).
|
|
10
|
+
Repeating the call cannot work; the advice says to start the upload again so new keys are
|
|
11
|
+
derived, and that nothing was charged for the refused call.
|
|
12
|
+
|
|
13
|
+
## 0.34.3 — 2026-09-07
|
|
14
|
+
|
|
15
|
+
- Security. `nmts wallet swap` on Bluefin now calls only the contract package pinned in this
|
|
16
|
+
release. Before, it read the "current" package from a public Sui node and preferred that answer,
|
|
17
|
+
so a node giving a false answer could have sent the swap into someone else's code with your coin
|
|
18
|
+
as the argument. The pinned package is still checked against the chain before a quote is shown;
|
|
19
|
+
if that check refuses it, the Bluefin venue is unavailable until a release bumps the pin. DeepBook
|
|
20
|
+
was never affected. No such node was observed and no loss is known.
|
|
21
|
+
|
|
6
22
|
## 0.34.2 — 2026-09-07
|
|
7
23
|
|
|
8
24
|
- The README and the agent document open with what the name stands for — NMTS is
|
package/README.ko.md
CHANGED
|
@@ -51,7 +51,7 @@ nmts --help
|
|
|
51
51
|
|
|
52
52
|
```sh
|
|
53
53
|
npm install -g github:needmoretruth/nmts-cli # 기본 브랜치
|
|
54
|
-
npm install -g github:needmoretruth/nmts-cli#v0.34.
|
|
54
|
+
npm install -g github:needmoretruth/nmts-cli#v0.34.4 # 버전을 고정할 때
|
|
55
55
|
npm install -g https://github.com/needmoretruth/nmts-cli/releases/latest/download/nmts.tgz
|
|
56
56
|
```
|
|
57
57
|
|
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ default branch, from a pinned version, or from the tarball attached to the
|
|
|
53
53
|
|
|
54
54
|
```sh
|
|
55
55
|
npm install -g github:needmoretruth/nmts-cli # the default branch
|
|
56
|
-
npm install -g github:needmoretruth/nmts-cli#v0.34.
|
|
56
|
+
npm install -g github:needmoretruth/nmts-cli#v0.34.4 # a pinned version
|
|
57
57
|
npm install -g https://github.com/needmoretruth/nmts-cli/releases/latest/download/nmts.tgz
|
|
58
58
|
```
|
|
59
59
|
|
package/dist/api-advice.js
CHANGED
|
@@ -168,6 +168,10 @@ export function adviseFor(code) {
|
|
|
168
168
|
case "RELEASE_NOT_SPONSORED":
|
|
169
169
|
return ("This file's storage was not paid for with credits, so it is not the server's to release. " +
|
|
170
170
|
"Storage bought from a wallet is released by that wallet.");
|
|
171
|
+
case "SPONSORED_IDEM_MISMATCH":
|
|
172
|
+
return ("This reservation key already names a different blob: the piece was re-encrypted after it " +
|
|
173
|
+
"was reserved. Repeating the call under this key cannot work. Start the upload again so it " +
|
|
174
|
+
"derives new keys; nothing was charged for this call.");
|
|
171
175
|
case "SPONSORED_STATE":
|
|
172
176
|
return ("The upload is not at the step that call belongs to — the steps have an order and one was " +
|
|
173
177
|
"skipped or already done. Start the upload again rather than repeating this call.");
|
package/dist/artifact-about.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare const CRYPTO_SPEC_URL = "https://github.com/needmoretruth/nmts-re
|
|
|
11
11
|
* is the field's own contract — what the writer says about itself — and a person holding two
|
|
12
12
|
* copies of one account's artefacts can then tell which program made each.
|
|
13
13
|
*/
|
|
14
|
-
export declare const WRITTEN_BY = "nmts-cli 0.34.
|
|
14
|
+
export declare const WRITTEN_BY = "nmts-cli 0.34.4";
|
|
15
15
|
/** Which of the three artefacts a wrapper is. A reader holding several can sort them. */
|
|
16
16
|
export type ArtifactKind = "recovery-list" | "file-list" | "recovery-kit";
|
|
17
17
|
/**
|
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.34.
|
|
12
|
+
export declare const VERSION = "0.34.4";
|
|
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.34.
|
|
21
|
+
export const VERSION = "0.34.4";
|
|
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. */
|
|
@@ -5,7 +5,7 @@ import { type WalletBalances } from "./wallet.ts";
|
|
|
5
5
|
import { type VenueQuote } from "./wallet-swap-quote.ts";
|
|
6
6
|
/** Where a swap runs: one of the two mainnet venues, or the official testnet facility. */
|
|
7
7
|
export type SwapRail = SwapVenue | "exchange";
|
|
8
|
-
/** Bluefin's addresses
|
|
8
|
+
/** Bluefin's addresses: the package PINNED in this release, confirmed on chain before it is used. */
|
|
9
9
|
export interface BluefinBinding {
|
|
10
10
|
packageId: string;
|
|
11
11
|
globalConfigId: string;
|
|
@@ -40,7 +40,7 @@ export declare function swapTransaction(input: SwapShape & {
|
|
|
40
40
|
/** What `commands/wallet-swap.ts` reads before it prints a review. */
|
|
41
41
|
export interface SwapReads {
|
|
42
42
|
readWallet(address: string): Promise<WalletBalances>;
|
|
43
|
-
/** Bluefin's
|
|
43
|
+
/** Bluefin's pinned package, version-checked. Throws when the chain refuses that package. */
|
|
44
44
|
resolveBluefin(): Promise<BluefinBinding>;
|
|
45
45
|
/** The testnet facility and its rate. Throws off testnet, or when the object cannot be read. */
|
|
46
46
|
readExchange(): Promise<ExchangeFacility>;
|
|
@@ -49,4 +49,18 @@ export interface SwapReads {
|
|
|
49
49
|
/** The fee in MIST measured by dry-running this exact swap, or null when it could not be. */
|
|
50
50
|
estimateFee(shape: SwapShape, sender: string): Promise<bigint | null>;
|
|
51
51
|
}
|
|
52
|
+
/** All this path uses of an RPC: one devInspect and its `error`. Narrow on purpose — the type itself
|
|
53
|
+
* has no place for a chain-supplied ADDRESS to enter, and a test can hand it a fake. */
|
|
54
|
+
export interface BluefinVersionRpc {
|
|
55
|
+
devInspectTransactionBlock(input: {
|
|
56
|
+
sender: string;
|
|
57
|
+
transactionBlock: Transaction;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
error?: string | null;
|
|
60
|
+
}>;
|
|
61
|
+
}
|
|
62
|
+
/** The pinned addresses, returned only once the chain confirms the pinned package still verifies.
|
|
63
|
+
* A refusal ends it: this function has no other address to return. Exported so the test can hold an
|
|
64
|
+
* RPC that names a different package and watch it change nothing. */
|
|
65
|
+
export declare function checkedBluefinBinding(pinned: BluefinBinding, rpc: BluefinVersionRpc): Promise<BluefinBinding>;
|
|
52
66
|
export declare function swapReads(network: Network): SwapReads;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// The swap transaction and its live reads: the wallet's balances, Bluefin's
|
|
2
|
-
// testnet facility's rate, and the fee a dry run measures.
|
|
3
|
-
//
|
|
1
|
+
// The swap transaction and its live reads: the wallet's balances, the version check on Bluefin's
|
|
2
|
+
// PINNED package, the testnet facility's rate, and the fee a dry run measures. Bluefin's package id
|
|
3
|
+
// itself is NOT a live read — see the block above `checkedBluefinBinding`. The quotes live next door
|
|
4
|
+
// in `wallet-swap-quote.ts`; both files read, and only `wallet-sign.ts` signs.
|
|
4
5
|
//
|
|
5
6
|
// ⛔ ONE BUILDER FOR THE FEE AND THE SIGNATURE. `estimateFee` and `wallet-sign.ts` both call
|
|
6
7
|
// `swapTransaction`, so the fee printed is the fee of the transaction that is then signed.
|
|
@@ -20,7 +21,7 @@ import { coinWithBalance, Transaction } from "@mysten/sui/transactions";
|
|
|
20
21
|
import { TESTNET_WALRUS_PACKAGE_CONFIG } from "@mysten/walrus";
|
|
21
22
|
import { walrusClient, netGasFee } from "./extend-chain.js";
|
|
22
23
|
import { isRecord } from "./guards.js";
|
|
23
|
-
import { BLUEFIN_GLOBAL_CONFIG_IDS, BLUEFIN_MAX_SQRT_PRICE, BLUEFIN_MIN_SQRT_PRICE, BLUEFIN_PACKAGE_IDS,
|
|
24
|
+
import { BLUEFIN_GLOBAL_CONFIG_IDS, BLUEFIN_MAX_SQRT_PRICE, BLUEFIN_MIN_SQRT_PRICE, BLUEFIN_PACKAGE_IDS, BLUEFIN_WAL_SUI_POOLS, DEEP_COIN_TYPES, DEEPBOOK_PACKAGE_IDS, DEEPBOOK_WAL_SUI_POOLS, } from "./shared/lib/wallet/venue-ids.js";
|
|
24
25
|
import { readBalances, SUI_COIN_TYPE, walCoinType } from "./wallet.js";
|
|
25
26
|
import { chainReader } from "./wallet-chain.js";
|
|
26
27
|
import { quoteVenue, QUOTE_SENDER } from "./wallet-swap-quote.js";
|
|
@@ -86,38 +87,36 @@ export function swapTransaction(input) {
|
|
|
86
87
|
tx.setGasBudget(input.gasBudgetMist);
|
|
87
88
|
return tx;
|
|
88
89
|
}
|
|
89
|
-
const FULL_ADDRESS = /^0x[0-9a-f]{64}$/;
|
|
90
90
|
/** `fields.<name>` of a Move object's content, as a string, or null when the shape differs. */
|
|
91
91
|
function fieldOf(content, name) {
|
|
92
92
|
const fields = isRecord(content) ? content["fields"] : undefined;
|
|
93
93
|
return isRecord(fields) ? fields[name] : undefined;
|
|
94
94
|
}
|
|
95
|
+
/** Does the chain still accept this package? `config::verify_version` under devInspect: no signature,
|
|
96
|
+
* no gas. False when the chain refuses AND when the RPC cannot answer — both mean "no Bluefin". */
|
|
97
|
+
async function versionPasses(rpc, packageId, globalConfigId) {
|
|
98
|
+
try {
|
|
99
|
+
const tx = new Transaction();
|
|
100
|
+
tx.moveCall({ target: `${packageId}::config::verify_version`, arguments: [tx.object(globalConfigId)] });
|
|
101
|
+
const res = await rpc.devInspectTransactionBlock({ sender: QUOTE_SENDER, transactionBlock: tx });
|
|
102
|
+
return !res.error;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** The pinned addresses, returned only once the chain confirms the pinned package still verifies.
|
|
109
|
+
* A refusal ends it: this function has no other address to return. Exported so the test can hold an
|
|
110
|
+
* RPC that names a different package and watch it change nothing. */
|
|
111
|
+
export async function checkedBluefinBinding(pinned, rpc) {
|
|
112
|
+
if (!(await versionPasses(rpc, pinned.packageId, pinned.globalConfigId))) {
|
|
113
|
+
throw new Error("Bluefin's on-chain version check refused the package address this tool pins.");
|
|
114
|
+
}
|
|
115
|
+
return pinned;
|
|
116
|
+
}
|
|
95
117
|
export function swapReads(network) {
|
|
96
118
|
const client = walrusClient(network);
|
|
97
119
|
let bluefin = null;
|
|
98
|
-
async function livePackage(upgradeCapId) {
|
|
99
|
-
try {
|
|
100
|
-
const obj = await client.getObject({ id: upgradeCapId, options: { showContent: true, showType: true } });
|
|
101
|
-
if (obj.data?.type !== "0x2::package::UpgradeCap")
|
|
102
|
-
return null;
|
|
103
|
-
const pkg = fieldOf(obj.data.content, "package");
|
|
104
|
-
return typeof pkg === "string" && FULL_ADDRESS.test(pkg) ? pkg : null;
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
async function versionPasses(packageId, globalConfigId) {
|
|
111
|
-
try {
|
|
112
|
-
const tx = new Transaction();
|
|
113
|
-
tx.moveCall({ target: `${packageId}::config::verify_version`, arguments: [tx.object(globalConfigId)] });
|
|
114
|
-
const res = await client.devInspectTransactionBlock({ sender: QUOTE_SENDER, transactionBlock: tx });
|
|
115
|
-
return !res.error;
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
120
|
return {
|
|
122
121
|
async readWallet(address) {
|
|
123
122
|
return readBalances(chainReader(network, address), walCoinType(network));
|
|
@@ -127,19 +126,13 @@ export function swapReads(network) {
|
|
|
127
126
|
if (bluefin !== null)
|
|
128
127
|
return bluefin;
|
|
129
128
|
const pending = (async () => {
|
|
130
|
-
const
|
|
131
|
-
const capId = BLUEFIN_UPGRADE_CAP_IDS[network];
|
|
129
|
+
const packageId = BLUEFIN_PACKAGE_IDS[network];
|
|
132
130
|
const globalConfigId = BLUEFIN_GLOBAL_CONFIG_IDS[network];
|
|
133
131
|
const poolId = BLUEFIN_WAL_SUI_POOLS[network];
|
|
134
|
-
if (
|
|
132
|
+
if (packageId === null || globalConfigId === null || poolId === null) {
|
|
135
133
|
throw new Error(`This tool knows no Bluefin WAL/SUI pool on ${network}.`);
|
|
136
134
|
}
|
|
137
|
-
|
|
138
|
-
for (const packageId of live === null ? [pinned] : [live, pinned]) {
|
|
139
|
-
if (await versionPasses(packageId, globalConfigId))
|
|
140
|
-
return { packageId, globalConfigId, poolId };
|
|
141
|
-
}
|
|
142
|
-
throw new Error("Bluefin's on-chain version check refused every package address this tool knows.");
|
|
135
|
+
return checkedBluefinBinding({ packageId, globalConfigId, poolId }, client);
|
|
143
136
|
})();
|
|
144
137
|
bluefin = pending;
|
|
145
138
|
pending.catch(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needmoretruth/nmts-cli",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.4",
|
|
4
4
|
"description": "Command-line and MCP access to NMTS (NeedMoreTruthStorage, https://nmts.me): open-source, end-to-end encrypted storage on the Walrus network. No fee to NMTS; you pay the network from your own wallet. For people and for their agents.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|