@ldgr/cli 0.2.1 → 0.3.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/README.md +3 -2
- package/dist/cli.js +26 -48
- package/dist/transfer.d.ts +1 -14
- package/dist/wallet-file.d.ts +3 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ldgr/cli
|
|
2
2
|
|
|
3
|
-
LDGR's self-custody terminal wallet. Version 0.
|
|
3
|
+
LDGR's self-custody terminal wallet. Version 0.3.0 creates or imports local wallets, lets you select between them, shows addresses and balances, sends native Ledra or a token, and persistently switches the selected wallet between mainnet and testnet.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install -g @ldgr/cli
|
|
@@ -13,7 +13,8 @@ ldgr address
|
|
|
13
13
|
ldgr balance
|
|
14
14
|
ldgr send ldgr1… 2.5
|
|
15
15
|
ldgr send-token t1abcdef01234567 ldgr1… 10
|
|
16
|
+
ldgr network show
|
|
16
17
|
ldgr network set testnet
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
The default owner-only wallet store lives at `~/.ldgr/wallet.json`. On first use, an existing `~/.config/ldgr/wallet.json` store is validated, copied to the new owner-only location, and removed from its former location. At creation/import you can choose passphrase protection or an **unprotected** wallet, which follows the extension model and relies only on the local file's permissions. `wallet list` shows one-based indexes and marks the active wallet; `wallet select <index>` changes it without exposing a secret. `wallet export <path>` writes
|
|
20
|
+
The default owner-only wallet store lives at `~/.ldgr/wallet.json`. Pass `--wallet <path>` to any command to use a different store. On first use, an existing `~/.config/ldgr/wallet.json` store is validated, copied to the new owner-only location, and removed from its former location; an explicit `--wallet` path is never moved. On Windows, the CLI uses the native `USERPROFILE`/`APPDATA` locations and path separators, and relies on the file's native ACLs instead of POSIX mode bits. At creation/import you can choose passphrase protection or an **unprotected** wallet, which follows the extension model and relies only on the local file's permissions. `wallet list` shows one-based indexes and marks the active wallet; `wallet select <index>` changes it without exposing a secret. `wallet export <path>` writes an owner-only `ldgr-recovery-key.txt` file containing the raw recovery-key value accepted by the extension; `wallet import <path>` reads the same file. `--testnet` is available only when creating or importing a wallet; use `network set testnet` for an existing wallet. Testnet prints a warning on every wallet command and only uses `https://tst.ldgr.ltd`.
|
package/dist/cli.js
CHANGED
|
@@ -11,48 +11,14 @@ import {
|
|
|
11
11
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
12
12
|
|
|
13
13
|
// src/transfer.ts
|
|
14
|
-
import {
|
|
15
|
-
canonicalizeLedgerAddress,
|
|
16
|
-
formatAtomicAmount,
|
|
17
|
-
parseDecimalAmount
|
|
18
|
-
} from "@ldgr/protocol";
|
|
19
|
-
async function buildTransferSubmission(input) {
|
|
20
|
-
const recipient = canonicalizeLedgerAddress(input.to, input.wallet.ledgerId);
|
|
21
|
-
const amount = parseDecimalAmount(input.amount.trim(), input.assetDefinition);
|
|
22
|
-
if (amount <= 0n)
|
|
23
|
-
throw new RangeError("Amount must be greater than zero");
|
|
24
|
-
if (!/^(0|[1-9][0-9]*)$/.test(input.nextNonce))
|
|
25
|
-
throw new TypeError("Ledger returned an invalid next nonce");
|
|
26
|
-
const payload = {
|
|
27
|
-
version: 1,
|
|
28
|
-
action: "transfer",
|
|
29
|
-
chain: "ldgr-v1",
|
|
30
|
-
ledgerId: input.wallet.ledgerId,
|
|
31
|
-
asset: input.asset,
|
|
32
|
-
from: input.wallet.address,
|
|
33
|
-
to: recipient,
|
|
34
|
-
amount,
|
|
35
|
-
nonce: BigInt(input.nextNonce),
|
|
36
|
-
timestamp: BigInt((input.now ?? Date.now)()),
|
|
37
|
-
publicKey: input.wallet.publicKey
|
|
38
|
-
};
|
|
39
|
-
const signature = await input.wallet.sign(payload);
|
|
40
|
-
return {
|
|
41
|
-
payload: {
|
|
42
|
-
...payload,
|
|
43
|
-
amount: formatAtomicAmount(amount, input.assetDefinition),
|
|
44
|
-
nonce: payload.nonce.toString(),
|
|
45
|
-
timestamp: payload.timestamp.toString()
|
|
46
|
-
},
|
|
47
|
-
signature
|
|
48
|
-
};
|
|
49
|
-
}
|
|
14
|
+
import { buildTransferSubmission } from "@ldgr/wallet-core";
|
|
50
15
|
|
|
51
16
|
// src/wallet-file.ts
|
|
52
17
|
import { generateLocalWallet, loadLocalWallet } from "@ldgr/client";
|
|
53
|
-
import { canonicalizeLedgerAddress
|
|
18
|
+
import { canonicalizeLedgerAddress, decodeEd25519RecoveryKey } from "@ldgr/protocol";
|
|
54
19
|
import { lstat, mkdir, open, readFile, rename, unlink } from "node:fs/promises";
|
|
55
|
-
import { dirname, join } from "node:path";
|
|
20
|
+
import { dirname, join, win32 } from "node:path";
|
|
21
|
+
import { homedir } from "node:os";
|
|
56
22
|
var KDF_ITERATIONS = 310000;
|
|
57
23
|
var AES_GCM = "AES-GCM";
|
|
58
24
|
function arrayBuffer(bytes) {
|
|
@@ -87,7 +53,7 @@ function parseWalletEntry(value) {
|
|
|
87
53
|
throw new TypeError("Unsupported wallet file format");
|
|
88
54
|
if (file.network !== networkForLedger(file.ledgerId))
|
|
89
55
|
throw new TypeError("Wallet file network does not match ledgerId");
|
|
90
|
-
|
|
56
|
+
canonicalizeLedgerAddress(file.address, file.ledgerId);
|
|
91
57
|
if (file.protection === "none") {
|
|
92
58
|
if (typeof file.recoveryKey !== "string")
|
|
93
59
|
throw new TypeError("Unsupported wallet file format");
|
|
@@ -125,18 +91,31 @@ function serializeWalletStore(store) {
|
|
|
125
91
|
return `${JSON.stringify(store, null, 2)}
|
|
126
92
|
`;
|
|
127
93
|
}
|
|
94
|
+
function pathJoiner(platform) {
|
|
95
|
+
return platform === "win32" ? win32.join : join;
|
|
96
|
+
}
|
|
97
|
+
function homeDirectory(input, platform) {
|
|
98
|
+
if (input.home !== undefined)
|
|
99
|
+
return input.home;
|
|
100
|
+
if (platform === "win32")
|
|
101
|
+
return process.env.USERPROFILE?.trim() || process.env.HOME?.trim() || homedir();
|
|
102
|
+
return process.env.HOME?.trim() || homedir();
|
|
103
|
+
}
|
|
128
104
|
function defaultWalletPath(input = {}) {
|
|
129
|
-
const
|
|
105
|
+
const platform = input.platform ?? process.platform;
|
|
106
|
+
const home = homeDirectory(input, platform);
|
|
130
107
|
if (!home)
|
|
131
108
|
throw new Error("Could not determine your home directory; use --wallet <path>");
|
|
132
|
-
return
|
|
109
|
+
return pathJoiner(platform)(home, ".ldgr", "wallet.json");
|
|
133
110
|
}
|
|
134
111
|
function legacyDefaultWalletPath(input = {}) {
|
|
135
|
-
const
|
|
112
|
+
const platform = input.platform ?? process.platform;
|
|
113
|
+
const joinPath = pathJoiner(platform);
|
|
114
|
+
const home = homeDirectory(input, platform);
|
|
136
115
|
if (!home)
|
|
137
116
|
throw new Error("Could not determine your home directory; use --wallet <path>");
|
|
138
|
-
const configHome = input.configHome ?? (process.env.XDG_CONFIG_HOME?.trim() ||
|
|
139
|
-
return
|
|
117
|
+
const configHome = input.configHome ?? (platform === "win32" ? process.env.APPDATA?.trim() || joinPath(home, "AppData", "Roaming") : process.env.XDG_CONFIG_HOME?.trim() || joinPath(home, ".config"));
|
|
118
|
+
return joinPath(configHome, "ldgr", "wallet.json");
|
|
140
119
|
}
|
|
141
120
|
async function createEncryptedWalletFile(input) {
|
|
142
121
|
const wallet = await generateLocalWallet({ ledgerId: input.ledgerId });
|
|
@@ -204,7 +183,7 @@ async function readWalletStore(path) {
|
|
|
204
183
|
});
|
|
205
184
|
if (!details.isFile() || details.isSymbolicLink() || details.nlink !== 1)
|
|
206
185
|
throw new Error("Wallet file must be a regular file with no links");
|
|
207
|
-
if ((details.mode & 63) !== 0)
|
|
186
|
+
if (process.platform !== "win32" && (details.mode & 63) !== 0)
|
|
208
187
|
throw new Error("Wallet file must be owner-only (chmod 600)");
|
|
209
188
|
let parsed;
|
|
210
189
|
try {
|
|
@@ -362,7 +341,7 @@ async function walletFileMetadata(path) {
|
|
|
362
341
|
|
|
363
342
|
// src/cli.ts
|
|
364
343
|
var HELP = `
|
|
365
|
-
LDGR CLI 0.
|
|
344
|
+
LDGR CLI 0.3.0 — self-custody wallet
|
|
366
345
|
|
|
367
346
|
Usage:
|
|
368
347
|
ldgr wallet create [--testnet] [--wallet <path>]
|
|
@@ -377,8 +356,7 @@ Usage:
|
|
|
377
356
|
ldgr network show [--wallet <path>]
|
|
378
357
|
ldgr network set <mainnet|testnet> [--wallet <path>]
|
|
379
358
|
|
|
380
|
-
The default wallet store is ~/.ldgr/wallet.json.
|
|
381
|
-
~/.config/ldgr/wallet.json move there safely on first use. A wallet can be
|
|
359
|
+
The default wallet store is ~/.ldgr/wallet.json. A wallet can be
|
|
382
360
|
passphrase-protected or unprotected (local-file permissions only). The CLI
|
|
383
361
|
never accepts recovery-key values or passphrases as arguments or environment
|
|
384
362
|
variables. Use \`ldgr wallet export <path>\` for an extension-compatible
|
package/dist/transfer.d.ts
CHANGED
|
@@ -1,14 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { type AssetDefinition } from "@ldgr/protocol";
|
|
3
|
-
export declare function buildTransferSubmission(input: {
|
|
4
|
-
readonly wallet: LdgrSigner;
|
|
5
|
-
readonly asset: string;
|
|
6
|
-
readonly assetDefinition: AssetDefinition;
|
|
7
|
-
readonly amount: string;
|
|
8
|
-
readonly to: string;
|
|
9
|
-
readonly nextNonce: string;
|
|
10
|
-
readonly now?: () => number;
|
|
11
|
-
}): Promise<{
|
|
12
|
-
readonly payload: Record<string, unknown>;
|
|
13
|
-
readonly signature: string;
|
|
14
|
-
}>;
|
|
1
|
+
export { buildTransferSubmission } from "@ldgr/wallet-core";
|
package/dist/wallet-file.d.ts
CHANGED
|
@@ -40,8 +40,10 @@ export type WalletFileMetadata = LocalWalletFile & {
|
|
|
40
40
|
};
|
|
41
41
|
export type DefaultWalletPathInput = {
|
|
42
42
|
readonly home?: string | undefined;
|
|
43
|
-
/** Test seam for the former
|
|
43
|
+
/** Test seam for the former platform-specific config default. */
|
|
44
44
|
readonly configHome?: string | undefined;
|
|
45
|
+
/** Test seam for Windows path and environment behavior. */
|
|
46
|
+
readonly platform?: NodeJS.Platform | undefined;
|
|
45
47
|
};
|
|
46
48
|
export type CreatedEncryptedWallet = {
|
|
47
49
|
readonly file: LocalWalletFile;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ldgr/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ldgr": "dist/cli.js"
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
"node": ">=20.19.0"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@ldgr/client": "^0.
|
|
20
|
-
"@ldgr/protocol": "^0.
|
|
19
|
+
"@ldgr/client": "^0.3.0",
|
|
20
|
+
"@ldgr/protocol": "^0.3.0",
|
|
21
|
+
"@ldgr/wallet-core": "^0.1.0",
|
|
21
22
|
"@clack/prompts": "^0.11.0"
|
|
22
23
|
},
|
|
23
24
|
"scripts": {
|
|
24
|
-
"build": "
|
|
25
|
+
"build": "bun scripts/build.mjs",
|
|
25
26
|
"prepack": "bun run build",
|
|
26
27
|
"start": "bun src/cli.ts",
|
|
27
28
|
"test": "bun test",
|
|
@@ -29,6 +30,6 @@
|
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@types/node": "^22.20.1",
|
|
32
|
-
"typescript": "^
|
|
33
|
+
"typescript": "^7.0.2"
|
|
33
34
|
}
|
|
34
35
|
}
|