@dfinity/hardware-wallet-cli 0.1.1 → 0.2.2
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/.github/CODEOWNERS +1 -1
- package/README.md +13 -0
- package/build/index.js +32 -25
- package/build/src/ledger/identity.js +5 -1
- package/build/src/ledger/secp256k1.js +16 -0
- package/index.ts +43 -26
- package/package.json +8 -4
- package/src/ledger/secp256k1.ts +21 -0
package/.github/CODEOWNERS
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# The codebase is owned by the Governance & Identity Experience team at DFINITY
|
|
2
|
-
|
|
2
|
+
# For questions, reach out to: <gix@dfinity.org>
|
package/README.md
CHANGED
|
@@ -16,3 +16,16 @@ Live desktop app running on your computer. If you are facing connection issues
|
|
|
16
16
|
when doing so, Ledger provides platform-specific
|
|
17
17
|
[troubleshooting instructions](https://support.ledger.com/hc/en-us/articles/115005165269-Fix-USB-connection-issues-with-Ledger-Live?support=true)
|
|
18
18
|
on their support site.
|
|
19
|
+
|
|
20
|
+
## Development
|
|
21
|
+
|
|
22
|
+
Clone the repository.
|
|
23
|
+
|
|
24
|
+
Install dependencies with `npm install`.
|
|
25
|
+
|
|
26
|
+
To execute a command, you can use `npm run execute -- <args>`.
|
|
27
|
+
|
|
28
|
+
For example
|
|
29
|
+
|
|
30
|
+
* The command `ic-hardware-wallet --network https://nnsdapp.dfinity.network icp balance`.
|
|
31
|
+
* Would be `npm run execute -- --network https://nnsdapp.dfinity.network icp balance` for development.
|
package/build/index.js
CHANGED
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
const commander_1 = require("commander");
|
|
11
11
|
const identity_1 = require("./src/ledger/identity");
|
|
12
12
|
const nns_1 = require("@dfinity/nns");
|
|
13
|
+
const principal_1 = require("@dfinity/principal");
|
|
13
14
|
const agent_1 = require("@dfinity/agent");
|
|
14
15
|
const chalk_1 = __importDefault(require("chalk"));
|
|
15
16
|
// Add polyfill for `window` for `TransportWebHID` checks to work.
|
|
@@ -17,7 +18,6 @@ require("node-window-polyfill/register");
|
|
|
17
18
|
// Add polyfill for `window.fetch` for agent-js to work.
|
|
18
19
|
// @ts-ignore (no types are available)
|
|
19
20
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
20
|
-
const principal_1 = require("@dfinity/principal");
|
|
21
21
|
global.fetch = node_fetch_1.default;
|
|
22
22
|
window.fetch = node_fetch_1.default;
|
|
23
23
|
const program = new commander_1.Command();
|
|
@@ -39,16 +39,24 @@ async function getAgent(identity) {
|
|
|
39
39
|
}
|
|
40
40
|
return agent;
|
|
41
41
|
}
|
|
42
|
+
async function getLedgerIdentity() {
|
|
43
|
+
const principalPath = tryParseInt(program.opts().principal);
|
|
44
|
+
if (principalPath < 0 || principalPath > 255) {
|
|
45
|
+
throw new commander_1.InvalidArgumentError("Principal path must be between 0 and 255 inclusive.");
|
|
46
|
+
}
|
|
47
|
+
return identity_1.LedgerIdentity.create(`m/44'/223'/0'/0/${principalPath}`);
|
|
48
|
+
}
|
|
42
49
|
/**
|
|
43
50
|
* Fetches the balance of the main account on the wallet.
|
|
44
51
|
*/
|
|
45
52
|
async function getBalance() {
|
|
46
|
-
const identity = await
|
|
53
|
+
const identity = await getLedgerIdentity();
|
|
47
54
|
const accountIdentifier = nns_1.AccountIdentifier.fromPrincipal({
|
|
48
55
|
principal: identity.getPrincipal(),
|
|
49
56
|
});
|
|
50
57
|
const ledger = nns_1.LedgerCanister.create({
|
|
51
58
|
agent: await getAgent(new agent_1.AnonymousIdentity()),
|
|
59
|
+
hardwareWallet: true,
|
|
52
60
|
});
|
|
53
61
|
const balance = await ledger.accountBalance({
|
|
54
62
|
accountIdentifier: accountIdentifier,
|
|
@@ -62,9 +70,10 @@ async function getBalance() {
|
|
|
62
70
|
* @param amount Amount to send in e8s.
|
|
63
71
|
*/
|
|
64
72
|
async function sendICP(to, amount) {
|
|
65
|
-
const identity = await
|
|
73
|
+
const identity = await getLedgerIdentity();
|
|
66
74
|
const ledger = nns_1.LedgerCanister.create({
|
|
67
75
|
agent: await getAgent(identity),
|
|
76
|
+
hardwareWallet: true,
|
|
68
77
|
});
|
|
69
78
|
const blockHeight = await ledger.transfer({
|
|
70
79
|
to: to,
|
|
@@ -77,12 +86,14 @@ async function sendICP(to, amount) {
|
|
|
77
86
|
* Shows the principal and account idenifier on the terminal and on the wallet's screen.
|
|
78
87
|
*/
|
|
79
88
|
async function showInfo(showOnDevice) {
|
|
80
|
-
const identity = await
|
|
89
|
+
const identity = await getLedgerIdentity();
|
|
81
90
|
const accountIdentifier = nns_1.AccountIdentifier.fromPrincipal({
|
|
82
91
|
principal: identity.getPrincipal(),
|
|
83
92
|
});
|
|
93
|
+
const publicKey = identity.getPublicKey();
|
|
84
94
|
log(chalk_1.default.bold(`Principal: `) + identity.getPrincipal());
|
|
85
95
|
log(chalk_1.default.bold(`Address (${identity.derivePath}): `) + accountIdentifier.toHex());
|
|
96
|
+
log(chalk_1.default.bold('Public key: ') + publicKey.toHex());
|
|
86
97
|
if (showOnDevice) {
|
|
87
98
|
log("Displaying the principal and the address on the device...");
|
|
88
99
|
await identity.showAddressAndPubKeyOnDevice();
|
|
@@ -94,7 +105,7 @@ async function showInfo(showOnDevice) {
|
|
|
94
105
|
* @param amount Amount to stake in e8s.
|
|
95
106
|
*/
|
|
96
107
|
async function stakeNeuron(stake) {
|
|
97
|
-
const identity = await
|
|
108
|
+
const identity = await getLedgerIdentity();
|
|
98
109
|
const ledger = nns_1.LedgerCanister.create({
|
|
99
110
|
agent: await getAgent(identity),
|
|
100
111
|
});
|
|
@@ -126,7 +137,7 @@ async function stakeNeuron(stake) {
|
|
|
126
137
|
}
|
|
127
138
|
}
|
|
128
139
|
async function increaseDissolveDelay(neuronId, years, days, minutes, seconds) {
|
|
129
|
-
const identity = await
|
|
140
|
+
const identity = await getLedgerIdentity();
|
|
130
141
|
const governance = nns_1.GovernanceCanister.create({
|
|
131
142
|
agent: await getAgent(identity),
|
|
132
143
|
hardwareWallet: true,
|
|
@@ -142,7 +153,7 @@ async function increaseDissolveDelay(neuronId, years, days, minutes, seconds) {
|
|
|
142
153
|
ok();
|
|
143
154
|
}
|
|
144
155
|
async function disburseNeuron(neuronId, to, amount) {
|
|
145
|
-
const identity = await
|
|
156
|
+
const identity = await getLedgerIdentity();
|
|
146
157
|
const governance = nns_1.GovernanceCanister.create({
|
|
147
158
|
agent: await getAgent(identity),
|
|
148
159
|
hardwareWallet: true,
|
|
@@ -155,7 +166,7 @@ async function disburseNeuron(neuronId, to, amount) {
|
|
|
155
166
|
ok();
|
|
156
167
|
}
|
|
157
168
|
async function spawnNeuron(neuronId, controller) {
|
|
158
|
-
const identity = await
|
|
169
|
+
const identity = await getLedgerIdentity();
|
|
159
170
|
const governance = nns_1.GovernanceCanister.create({
|
|
160
171
|
agent: await getAgent(identity),
|
|
161
172
|
hardwareWallet: true,
|
|
@@ -167,7 +178,7 @@ async function spawnNeuron(neuronId, controller) {
|
|
|
167
178
|
ok(`Spawned neuron with ID ${spawnedNeuronId}`);
|
|
168
179
|
}
|
|
169
180
|
async function startDissolving(neuronId) {
|
|
170
|
-
const identity = await
|
|
181
|
+
const identity = await getLedgerIdentity();
|
|
171
182
|
const governance = nns_1.GovernanceCanister.create({
|
|
172
183
|
agent: await getAgent(identity),
|
|
173
184
|
hardwareWallet: true,
|
|
@@ -176,7 +187,7 @@ async function startDissolving(neuronId) {
|
|
|
176
187
|
ok();
|
|
177
188
|
}
|
|
178
189
|
async function stopDissolving(neuronId) {
|
|
179
|
-
const identity = await
|
|
190
|
+
const identity = await getLedgerIdentity();
|
|
180
191
|
const governance = nns_1.GovernanceCanister.create({
|
|
181
192
|
agent: await getAgent(identity),
|
|
182
193
|
hardwareWallet: true,
|
|
@@ -185,7 +196,7 @@ async function stopDissolving(neuronId) {
|
|
|
185
196
|
ok();
|
|
186
197
|
}
|
|
187
198
|
async function addHotkey(neuronId, principal) {
|
|
188
|
-
const identity = await
|
|
199
|
+
const identity = await getLedgerIdentity();
|
|
189
200
|
const governance = nns_1.GovernanceCanister.create({
|
|
190
201
|
agent: await getAgent(identity),
|
|
191
202
|
hardwareWallet: true,
|
|
@@ -197,7 +208,7 @@ async function addHotkey(neuronId, principal) {
|
|
|
197
208
|
ok();
|
|
198
209
|
}
|
|
199
210
|
async function removeHotkey(neuronId, principal) {
|
|
200
|
-
const identity = await
|
|
211
|
+
const identity = await getLedgerIdentity();
|
|
201
212
|
const governance = nns_1.GovernanceCanister.create({
|
|
202
213
|
agent: await getAgent(identity),
|
|
203
214
|
hardwareWallet: true,
|
|
@@ -209,7 +220,7 @@ async function removeHotkey(neuronId, principal) {
|
|
|
209
220
|
ok();
|
|
210
221
|
}
|
|
211
222
|
async function listNeurons() {
|
|
212
|
-
const identity = await
|
|
223
|
+
const identity = await getLedgerIdentity();
|
|
213
224
|
const governance = nns_1.GovernanceCanister.create({
|
|
214
225
|
agent: await getAgent(identity),
|
|
215
226
|
hardwareWallet: true,
|
|
@@ -230,20 +241,15 @@ async function listNeurons() {
|
|
|
230
241
|
/**
|
|
231
242
|
* Fetches the balance of the main account on the wallet.
|
|
232
243
|
*/
|
|
233
|
-
async function claimNeurons(
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
if (hexPubKey.length < 130 || hexPubKey.length > 150) {
|
|
239
|
-
throw new Error(`The key must be >= 130 characters and <= 150 characters.`);
|
|
240
|
-
}
|
|
241
|
-
const identity = await identity_1.LedgerIdentity.create();
|
|
244
|
+
async function claimNeurons() {
|
|
245
|
+
const identity = await getLedgerIdentity();
|
|
246
|
+
const publicKey = identity.getPublicKey();
|
|
247
|
+
const hexPubKey = publicKey.toHex();
|
|
242
248
|
const governance = await nns_1.GenesisTokenCanister.create({
|
|
243
249
|
agent: await getAgent(identity),
|
|
244
250
|
});
|
|
245
251
|
const claimedNeuronIds = await governance.claimNeurons({
|
|
246
|
-
hexPubKey
|
|
252
|
+
hexPubKey,
|
|
247
253
|
});
|
|
248
254
|
ok(`Successfully claimed the following neurons: ${claimedNeuronIds}`);
|
|
249
255
|
}
|
|
@@ -360,8 +366,8 @@ async function main() {
|
|
|
360
366
|
.requiredOption("--principal <principal>", "Principal", tryParsePrincipal)
|
|
361
367
|
.action((args) => run(() => removeHotkey(args.neuronId, args.principal))))
|
|
362
368
|
.addCommand(new commander_1.Command("claim")
|
|
363
|
-
.
|
|
364
|
-
.action((args) => run(() => claimNeurons(
|
|
369
|
+
.description("Claim the caller's GTC neurons.")
|
|
370
|
+
.action((args) => run(() => claimNeurons())));
|
|
365
371
|
const icp = new commander_1.Command("icp")
|
|
366
372
|
.description("Commands for managing ICP.")
|
|
367
373
|
.showSuggestionAfterError()
|
|
@@ -381,6 +387,7 @@ async function main() {
|
|
|
381
387
|
.addOption(new commander_1.Option("--network <network>", "The IC network to talk to.")
|
|
382
388
|
.default("https://ic0.app")
|
|
383
389
|
.env("IC_NETWORK"))
|
|
390
|
+
.addOption(new commander_1.Option("--principal <principal>", "The derivation path to use for the principal.\n(e.g. --principal 123 will result in a derivation path of m/44'/223'/0'/0/123)\nMust be >= 0 && <= 255").default(0))
|
|
384
391
|
.addCommand(new commander_1.Command("info")
|
|
385
392
|
.option("-n --no-show-on-device")
|
|
386
393
|
.description("Show the wallet's principal, address, and balance.")
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -14,6 +14,11 @@ function equals(b1, b2) {
|
|
|
14
14
|
}
|
|
15
15
|
return true;
|
|
16
16
|
}
|
|
17
|
+
const buf2hex = (buffer) => {
|
|
18
|
+
return [...new Uint8Array(buffer)]
|
|
19
|
+
.map((x) => x.toString(16).padStart(2, "0"))
|
|
20
|
+
.join("");
|
|
21
|
+
};
|
|
17
22
|
// This implementation is adjusted from the Ed25519PublicKey.
|
|
18
23
|
// The RAW_KEY_LENGTH and DER_PREFIX are modified accordingly
|
|
19
24
|
class Secp256k1PublicKey {
|
|
@@ -58,6 +63,17 @@ class Secp256k1PublicKey {
|
|
|
58
63
|
toRaw() {
|
|
59
64
|
return this.rawKey;
|
|
60
65
|
}
|
|
66
|
+
toHex() {
|
|
67
|
+
const hexPubKey = buf2hex(this.toRaw());
|
|
68
|
+
const isHex = hexPubKey.match("^[0-9a-fA-F]+$");
|
|
69
|
+
if (!isHex) {
|
|
70
|
+
throw new Error(`${hexPubKey} is not a hex string.`);
|
|
71
|
+
}
|
|
72
|
+
if (hexPubKey.length < 130 || hexPubKey.length > 150) {
|
|
73
|
+
throw new Error(`The key must be >= 130 characters and <= 150 characters.`);
|
|
74
|
+
}
|
|
75
|
+
return hexPubKey;
|
|
76
|
+
}
|
|
61
77
|
}
|
|
62
78
|
exports.Secp256k1PublicKey = Secp256k1PublicKey;
|
|
63
79
|
// The length of secp256k1 public keys is always 65 bytes.
|
package/index.ts
CHANGED
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
InsufficientAmountError,
|
|
16
16
|
InsufficientFundsError,
|
|
17
17
|
} from "@dfinity/nns";
|
|
18
|
+
import { Principal } from "@dfinity/principal";
|
|
19
|
+
import type { Secp256k1PublicKey } from "src/ledger/secp256k1";
|
|
18
20
|
import { Agent, AnonymousIdentity, HttpAgent, Identity } from "@dfinity/agent";
|
|
19
21
|
import chalk from "chalk";
|
|
20
22
|
|
|
@@ -24,7 +26,7 @@ import "node-window-polyfill/register";
|
|
|
24
26
|
// Add polyfill for `window.fetch` for agent-js to work.
|
|
25
27
|
// @ts-ignore (no types are available)
|
|
26
28
|
import fetch from "node-fetch";
|
|
27
|
-
|
|
29
|
+
|
|
28
30
|
global.fetch = fetch;
|
|
29
31
|
window.fetch = fetch;
|
|
30
32
|
|
|
@@ -54,17 +56,28 @@ async function getAgent(identity: Identity): Promise<Agent> {
|
|
|
54
56
|
return agent;
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
async function getLedgerIdentity(): Promise<LedgerIdentity> {
|
|
60
|
+
const principalPath = tryParseInt(program.opts().principal);
|
|
61
|
+
if (principalPath < 0 || principalPath > 255) {
|
|
62
|
+
throw new InvalidArgumentError(
|
|
63
|
+
"Principal path must be between 0 and 255 inclusive."
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return LedgerIdentity.create(`m/44'/223'/0'/0/${principalPath}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
/**
|
|
58
70
|
* Fetches the balance of the main account on the wallet.
|
|
59
71
|
*/
|
|
60
72
|
async function getBalance() {
|
|
61
|
-
const identity = await
|
|
73
|
+
const identity = await getLedgerIdentity();
|
|
62
74
|
const accountIdentifier = AccountIdentifier.fromPrincipal({
|
|
63
75
|
principal: identity.getPrincipal(),
|
|
64
76
|
});
|
|
65
77
|
|
|
66
78
|
const ledger = LedgerCanister.create({
|
|
67
79
|
agent: await getAgent(new AnonymousIdentity()),
|
|
80
|
+
hardwareWallet: true,
|
|
68
81
|
});
|
|
69
82
|
|
|
70
83
|
const balance = await ledger.accountBalance({
|
|
@@ -81,9 +94,10 @@ async function getBalance() {
|
|
|
81
94
|
* @param amount Amount to send in e8s.
|
|
82
95
|
*/
|
|
83
96
|
async function sendICP(to: AccountIdentifier, amount: ICP) {
|
|
84
|
-
const identity = await
|
|
97
|
+
const identity = await getLedgerIdentity();
|
|
85
98
|
const ledger = LedgerCanister.create({
|
|
86
99
|
agent: await getAgent(identity),
|
|
100
|
+
hardwareWallet: true,
|
|
87
101
|
});
|
|
88
102
|
|
|
89
103
|
const blockHeight = await ledger.transfer({
|
|
@@ -99,15 +113,19 @@ async function sendICP(to: AccountIdentifier, amount: ICP) {
|
|
|
99
113
|
* Shows the principal and account idenifier on the terminal and on the wallet's screen.
|
|
100
114
|
*/
|
|
101
115
|
async function showInfo(showOnDevice?: boolean) {
|
|
102
|
-
const identity = await
|
|
116
|
+
const identity = await getLedgerIdentity();
|
|
103
117
|
const accountIdentifier = AccountIdentifier.fromPrincipal({
|
|
104
118
|
principal: identity.getPrincipal(),
|
|
105
119
|
});
|
|
120
|
+
const publicKey = identity.getPublicKey() as Secp256k1PublicKey;
|
|
106
121
|
|
|
107
122
|
log(chalk.bold(`Principal: `) + identity.getPrincipal());
|
|
108
123
|
log(
|
|
109
124
|
chalk.bold(`Address (${identity.derivePath}): `) + accountIdentifier.toHex()
|
|
110
125
|
);
|
|
126
|
+
log(
|
|
127
|
+
chalk.bold('Public key: ') + publicKey.toHex()
|
|
128
|
+
)
|
|
111
129
|
|
|
112
130
|
if (showOnDevice) {
|
|
113
131
|
log("Displaying the principal and the address on the device...");
|
|
@@ -121,7 +139,7 @@ async function showInfo(showOnDevice?: boolean) {
|
|
|
121
139
|
* @param amount Amount to stake in e8s.
|
|
122
140
|
*/
|
|
123
141
|
async function stakeNeuron(stake: ICP) {
|
|
124
|
-
const identity = await
|
|
142
|
+
const identity = await getLedgerIdentity();
|
|
125
143
|
const ledger = LedgerCanister.create({
|
|
126
144
|
agent: await getAgent(identity),
|
|
127
145
|
});
|
|
@@ -160,7 +178,7 @@ async function increaseDissolveDelay(
|
|
|
160
178
|
minutes: number,
|
|
161
179
|
seconds: number
|
|
162
180
|
) {
|
|
163
|
-
const identity = await
|
|
181
|
+
const identity = await getLedgerIdentity();
|
|
164
182
|
const governance = GovernanceCanister.create({
|
|
165
183
|
agent: await getAgent(identity),
|
|
166
184
|
hardwareWallet: true,
|
|
@@ -181,7 +199,7 @@ async function increaseDissolveDelay(
|
|
|
181
199
|
}
|
|
182
200
|
|
|
183
201
|
async function disburseNeuron(neuronId: bigint, to?: string, amount?: bigint) {
|
|
184
|
-
const identity = await
|
|
202
|
+
const identity = await getLedgerIdentity();
|
|
185
203
|
const governance = GovernanceCanister.create({
|
|
186
204
|
agent: await getAgent(identity),
|
|
187
205
|
hardwareWallet: true,
|
|
@@ -197,7 +215,7 @@ async function disburseNeuron(neuronId: bigint, to?: string, amount?: bigint) {
|
|
|
197
215
|
}
|
|
198
216
|
|
|
199
217
|
async function spawnNeuron(neuronId: string, controller?: Principal) {
|
|
200
|
-
const identity = await
|
|
218
|
+
const identity = await getLedgerIdentity();
|
|
201
219
|
const governance = GovernanceCanister.create({
|
|
202
220
|
agent: await getAgent(identity),
|
|
203
221
|
hardwareWallet: true,
|
|
@@ -211,7 +229,7 @@ async function spawnNeuron(neuronId: string, controller?: Principal) {
|
|
|
211
229
|
}
|
|
212
230
|
|
|
213
231
|
async function startDissolving(neuronId: bigint) {
|
|
214
|
-
const identity = await
|
|
232
|
+
const identity = await getLedgerIdentity();
|
|
215
233
|
const governance = GovernanceCanister.create({
|
|
216
234
|
agent: await getAgent(identity),
|
|
217
235
|
hardwareWallet: true,
|
|
@@ -223,7 +241,7 @@ async function startDissolving(neuronId: bigint) {
|
|
|
223
241
|
}
|
|
224
242
|
|
|
225
243
|
async function stopDissolving(neuronId: bigint) {
|
|
226
|
-
const identity = await
|
|
244
|
+
const identity = await getLedgerIdentity();
|
|
227
245
|
const governance = GovernanceCanister.create({
|
|
228
246
|
agent: await getAgent(identity),
|
|
229
247
|
hardwareWallet: true,
|
|
@@ -235,7 +253,7 @@ async function stopDissolving(neuronId: bigint) {
|
|
|
235
253
|
}
|
|
236
254
|
|
|
237
255
|
async function addHotkey(neuronId: bigint, principal: Principal) {
|
|
238
|
-
const identity = await
|
|
256
|
+
const identity = await getLedgerIdentity();
|
|
239
257
|
const governance = GovernanceCanister.create({
|
|
240
258
|
agent: await getAgent(identity),
|
|
241
259
|
hardwareWallet: true,
|
|
@@ -250,7 +268,7 @@ async function addHotkey(neuronId: bigint, principal: Principal) {
|
|
|
250
268
|
}
|
|
251
269
|
|
|
252
270
|
async function removeHotkey(neuronId: bigint, principal: Principal) {
|
|
253
|
-
const identity = await
|
|
271
|
+
const identity = await getLedgerIdentity();
|
|
254
272
|
const governance = GovernanceCanister.create({
|
|
255
273
|
agent: await getAgent(identity),
|
|
256
274
|
hardwareWallet: true,
|
|
@@ -265,7 +283,7 @@ async function removeHotkey(neuronId: bigint, principal: Principal) {
|
|
|
265
283
|
}
|
|
266
284
|
|
|
267
285
|
async function listNeurons() {
|
|
268
|
-
const identity = await
|
|
286
|
+
const identity = await getLedgerIdentity();
|
|
269
287
|
const governance = GovernanceCanister.create({
|
|
270
288
|
agent: await getAgent(identity),
|
|
271
289
|
hardwareWallet: true,
|
|
@@ -288,23 +306,18 @@ async function listNeurons() {
|
|
|
288
306
|
/**
|
|
289
307
|
* Fetches the balance of the main account on the wallet.
|
|
290
308
|
*/
|
|
291
|
-
async function claimNeurons(
|
|
292
|
-
const
|
|
293
|
-
if (!isHex) {
|
|
294
|
-
throw new Error(`${hexPubKey} is not a hex string.`);
|
|
295
|
-
}
|
|
309
|
+
async function claimNeurons() {
|
|
310
|
+
const identity = await getLedgerIdentity();
|
|
296
311
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
312
|
+
const publicKey = identity.getPublicKey() as Secp256k1PublicKey;
|
|
313
|
+
const hexPubKey = publicKey.toHex();
|
|
300
314
|
|
|
301
|
-
const identity = await LedgerIdentity.create();
|
|
302
315
|
const governance = await GenesisTokenCanister.create({
|
|
303
316
|
agent: await getAgent(identity),
|
|
304
317
|
});
|
|
305
318
|
|
|
306
319
|
const claimedNeuronIds = await governance.claimNeurons({
|
|
307
|
-
hexPubKey
|
|
320
|
+
hexPubKey,
|
|
308
321
|
});
|
|
309
322
|
|
|
310
323
|
ok(`Successfully claimed the following neurons: ${claimedNeuronIds}`);
|
|
@@ -477,11 +490,10 @@ async function main() {
|
|
|
477
490
|
)
|
|
478
491
|
.addCommand(
|
|
479
492
|
new Command("claim")
|
|
480
|
-
.
|
|
481
|
-
"--hex-public-key <public-key>",
|
|
493
|
+
.description(
|
|
482
494
|
"Claim the caller's GTC neurons."
|
|
483
495
|
)
|
|
484
|
-
.action((args) => run(() => claimNeurons(
|
|
496
|
+
.action((args) => run(() => claimNeurons()))
|
|
485
497
|
);
|
|
486
498
|
|
|
487
499
|
const icp = new Command("icp")
|
|
@@ -518,6 +530,11 @@ async function main() {
|
|
|
518
530
|
.default("https://ic0.app")
|
|
519
531
|
.env("IC_NETWORK")
|
|
520
532
|
)
|
|
533
|
+
.addOption(
|
|
534
|
+
new Option("--principal <principal>", "The derivation path to use for the principal.\n(e.g. --principal 123 will result in a derivation path of m/44'/223'/0'/0/123)\nMust be >= 0 && <= 255").default(
|
|
535
|
+
0
|
|
536
|
+
)
|
|
537
|
+
)
|
|
521
538
|
.addCommand(
|
|
522
539
|
new Command("info")
|
|
523
540
|
.option("-n --no-show-on-device")
|
package/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dfinity/hardware-wallet-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A CLI to interact with the Internet Computer App on Ledger Nano S/X devices.",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"format": "prettier --write .",
|
|
8
8
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
9
|
"build": "tsc --build",
|
|
10
|
+
"prepack": "npm run build",
|
|
10
11
|
"clean": "tsc --build --clean",
|
|
11
|
-
"refresh": "rm -rf ./node_modules ./package-lock.json && npm install"
|
|
12
|
+
"refresh": "rm -rf ./node_modules ./package-lock.json && npm install",
|
|
13
|
+
"execute": "ts-node ./index.ts",
|
|
14
|
+
"update:nns": "npm update @dfinity/nns"
|
|
12
15
|
},
|
|
13
16
|
"repository": {
|
|
14
17
|
"type": "git",
|
|
@@ -22,7 +25,7 @@
|
|
|
22
25
|
"homepage": "https://github.com/dfinity/hardware-wallet-cli#readme",
|
|
23
26
|
"dependencies": {
|
|
24
27
|
"@dfinity/agent": "^0.11.2",
|
|
25
|
-
"@dfinity/nns": "
|
|
28
|
+
"@dfinity/nns": "nightly",
|
|
26
29
|
"@ledgerhq/hw-transport-node-hid-noevents": "^6.3.0",
|
|
27
30
|
"@ledgerhq/hw-transport-webhid": "^6.27.1",
|
|
28
31
|
"@zondax/ledger-icp": "^0.6.0",
|
|
@@ -35,7 +38,8 @@
|
|
|
35
38
|
"@types/google-protobuf": "^3.15.6",
|
|
36
39
|
"@types/node": "^17.0.16",
|
|
37
40
|
"@types/node-hid": "^1.3.1",
|
|
38
|
-
"prettier": "^2.6.2"
|
|
41
|
+
"prettier": "^2.6.2",
|
|
42
|
+
"ts-node": "^10.8.0"
|
|
39
43
|
},
|
|
40
44
|
"bin": {
|
|
41
45
|
"ic-hardware-wallet": "./build/index.js"
|
package/src/ledger/secp256k1.ts
CHANGED
|
@@ -15,6 +15,12 @@ function equals(b1: ArrayBuffer, b2: ArrayBuffer): boolean {
|
|
|
15
15
|
return true;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
const buf2hex = (buffer: ArrayBuffer): string => {
|
|
19
|
+
return [...new Uint8Array(buffer)]
|
|
20
|
+
.map((x) => x.toString(16).padStart(2, "0"))
|
|
21
|
+
.join("");
|
|
22
|
+
};
|
|
23
|
+
|
|
18
24
|
// This implementation is adjusted from the Ed25519PublicKey.
|
|
19
25
|
// The RAW_KEY_LENGTH and DER_PREFIX are modified accordingly
|
|
20
26
|
export class Secp256k1PublicKey implements PublicKey {
|
|
@@ -93,4 +99,19 @@ export class Secp256k1PublicKey implements PublicKey {
|
|
|
93
99
|
public toRaw(): ArrayBuffer {
|
|
94
100
|
return this.rawKey;
|
|
95
101
|
}
|
|
102
|
+
|
|
103
|
+
public toHex(): string {
|
|
104
|
+
const hexPubKey = buf2hex(this.toRaw());
|
|
105
|
+
|
|
106
|
+
const isHex = hexPubKey.match("^[0-9a-fA-F]+$");
|
|
107
|
+
if (!isHex) {
|
|
108
|
+
throw new Error(`${hexPubKey} is not a hex string.`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (hexPubKey.length < 130 || hexPubKey.length > 150) {
|
|
112
|
+
throw new Error(`The key must be >= 130 characters and <= 150 characters.`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return hexPubKey;
|
|
116
|
+
}
|
|
96
117
|
}
|