@dfinity/hardware-wallet-cli 0.2.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.
@@ -1,2 +1,2 @@
1
1
  # The codebase is owned by the Governance & Identity Experience team at DFINITY
2
- * @dfinity/gix
2
+ # For questions, reach out to: <gix@dfinity.org>
package/build/index.js CHANGED
@@ -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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
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 identity_1.LedgerIdentity.create();
223
+ const identity = await getLedgerIdentity();
213
224
  const governance = nns_1.GovernanceCanister.create({
214
225
  agent: await getAgent(identity),
215
226
  hardwareWallet: true,
@@ -227,30 +238,18 @@ async function listNeurons() {
227
238
  ok("No neurons found.");
228
239
  }
229
240
  }
230
- const buf2hex = (buffer) => {
231
- return [...new Uint8Array(buffer)]
232
- .map((x) => x.toString(16).padStart(2, "0"))
233
- .join("");
234
- };
235
241
  /**
236
242
  * Fetches the balance of the main account on the wallet.
237
243
  */
238
244
  async function claimNeurons() {
239
- const identity = await identity_1.LedgerIdentity.create();
240
- const bufferKey = identity.getPublicKey();
241
- const hexPubKey = buf2hex(bufferKey.toRaw());
242
- const isHex = hexPubKey.match("^[0-9a-fA-F]+$");
243
- if (!isHex) {
244
- throw new Error(`${hexPubKey} is not a hex string.`);
245
- }
246
- if (hexPubKey.length < 130 || hexPubKey.length > 150) {
247
- throw new Error(`The key must be >= 130 characters and <= 150 characters.`);
248
- }
245
+ const identity = await getLedgerIdentity();
246
+ const publicKey = identity.getPublicKey();
247
+ const hexPubKey = publicKey.toHex();
249
248
  const governance = await nns_1.GenesisTokenCanister.create({
250
249
  agent: await getAgent(identity),
251
250
  });
252
251
  const claimedNeuronIds = await governance.claimNeurons({
253
- hexPubKey: hexPubKey,
252
+ hexPubKey,
254
253
  });
255
254
  ok(`Successfully claimed the following neurons: ${claimedNeuronIds}`);
256
255
  }
@@ -388,6 +387,7 @@ async function main() {
388
387
  .addOption(new commander_1.Option("--network <network>", "The IC network to talk to.")
389
388
  .default("https://ic0.app")
390
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))
391
391
  .addCommand(new commander_1.Command("info")
392
392
  .option("-n --no-show-on-device")
393
393
  .description("Show the wallet's principal, address, and balance.")
@@ -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
@@ -56,17 +56,28 @@ async function getAgent(identity: Identity): Promise<Agent> {
56
56
  return agent;
57
57
  }
58
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
+
59
69
  /**
60
70
  * Fetches the balance of the main account on the wallet.
61
71
  */
62
72
  async function getBalance() {
63
- const identity = await LedgerIdentity.create();
73
+ const identity = await getLedgerIdentity();
64
74
  const accountIdentifier = AccountIdentifier.fromPrincipal({
65
75
  principal: identity.getPrincipal(),
66
76
  });
67
77
 
68
78
  const ledger = LedgerCanister.create({
69
79
  agent: await getAgent(new AnonymousIdentity()),
80
+ hardwareWallet: true,
70
81
  });
71
82
 
72
83
  const balance = await ledger.accountBalance({
@@ -83,9 +94,10 @@ async function getBalance() {
83
94
  * @param amount Amount to send in e8s.
84
95
  */
85
96
  async function sendICP(to: AccountIdentifier, amount: ICP) {
86
- const identity = await LedgerIdentity.create();
97
+ const identity = await getLedgerIdentity();
87
98
  const ledger = LedgerCanister.create({
88
99
  agent: await getAgent(identity),
100
+ hardwareWallet: true,
89
101
  });
90
102
 
91
103
  const blockHeight = await ledger.transfer({
@@ -101,15 +113,19 @@ async function sendICP(to: AccountIdentifier, amount: ICP) {
101
113
  * Shows the principal and account idenifier on the terminal and on the wallet's screen.
102
114
  */
103
115
  async function showInfo(showOnDevice?: boolean) {
104
- const identity = await LedgerIdentity.create();
116
+ const identity = await getLedgerIdentity();
105
117
  const accountIdentifier = AccountIdentifier.fromPrincipal({
106
118
  principal: identity.getPrincipal(),
107
119
  });
120
+ const publicKey = identity.getPublicKey() as Secp256k1PublicKey;
108
121
 
109
122
  log(chalk.bold(`Principal: `) + identity.getPrincipal());
110
123
  log(
111
124
  chalk.bold(`Address (${identity.derivePath}): `) + accountIdentifier.toHex()
112
125
  );
126
+ log(
127
+ chalk.bold('Public key: ') + publicKey.toHex()
128
+ )
113
129
 
114
130
  if (showOnDevice) {
115
131
  log("Displaying the principal and the address on the device...");
@@ -123,7 +139,7 @@ async function showInfo(showOnDevice?: boolean) {
123
139
  * @param amount Amount to stake in e8s.
124
140
  */
125
141
  async function stakeNeuron(stake: ICP) {
126
- const identity = await LedgerIdentity.create();
142
+ const identity = await getLedgerIdentity();
127
143
  const ledger = LedgerCanister.create({
128
144
  agent: await getAgent(identity),
129
145
  });
@@ -162,7 +178,7 @@ async function increaseDissolveDelay(
162
178
  minutes: number,
163
179
  seconds: number
164
180
  ) {
165
- const identity = await LedgerIdentity.create();
181
+ const identity = await getLedgerIdentity();
166
182
  const governance = GovernanceCanister.create({
167
183
  agent: await getAgent(identity),
168
184
  hardwareWallet: true,
@@ -183,7 +199,7 @@ async function increaseDissolveDelay(
183
199
  }
184
200
 
185
201
  async function disburseNeuron(neuronId: bigint, to?: string, amount?: bigint) {
186
- const identity = await LedgerIdentity.create();
202
+ const identity = await getLedgerIdentity();
187
203
  const governance = GovernanceCanister.create({
188
204
  agent: await getAgent(identity),
189
205
  hardwareWallet: true,
@@ -199,7 +215,7 @@ async function disburseNeuron(neuronId: bigint, to?: string, amount?: bigint) {
199
215
  }
200
216
 
201
217
  async function spawnNeuron(neuronId: string, controller?: Principal) {
202
- const identity = await LedgerIdentity.create();
218
+ const identity = await getLedgerIdentity();
203
219
  const governance = GovernanceCanister.create({
204
220
  agent: await getAgent(identity),
205
221
  hardwareWallet: true,
@@ -213,7 +229,7 @@ async function spawnNeuron(neuronId: string, controller?: Principal) {
213
229
  }
214
230
 
215
231
  async function startDissolving(neuronId: bigint) {
216
- const identity = await LedgerIdentity.create();
232
+ const identity = await getLedgerIdentity();
217
233
  const governance = GovernanceCanister.create({
218
234
  agent: await getAgent(identity),
219
235
  hardwareWallet: true,
@@ -225,7 +241,7 @@ async function startDissolving(neuronId: bigint) {
225
241
  }
226
242
 
227
243
  async function stopDissolving(neuronId: bigint) {
228
- const identity = await LedgerIdentity.create();
244
+ const identity = await getLedgerIdentity();
229
245
  const governance = GovernanceCanister.create({
230
246
  agent: await getAgent(identity),
231
247
  hardwareWallet: true,
@@ -237,7 +253,7 @@ async function stopDissolving(neuronId: bigint) {
237
253
  }
238
254
 
239
255
  async function addHotkey(neuronId: bigint, principal: Principal) {
240
- const identity = await LedgerIdentity.create();
256
+ const identity = await getLedgerIdentity();
241
257
  const governance = GovernanceCanister.create({
242
258
  agent: await getAgent(identity),
243
259
  hardwareWallet: true,
@@ -252,7 +268,7 @@ async function addHotkey(neuronId: bigint, principal: Principal) {
252
268
  }
253
269
 
254
270
  async function removeHotkey(neuronId: bigint, principal: Principal) {
255
- const identity = await LedgerIdentity.create();
271
+ const identity = await getLedgerIdentity();
256
272
  const governance = GovernanceCanister.create({
257
273
  agent: await getAgent(identity),
258
274
  hardwareWallet: true,
@@ -267,7 +283,7 @@ async function removeHotkey(neuronId: bigint, principal: Principal) {
267
283
  }
268
284
 
269
285
  async function listNeurons() {
270
- const identity = await LedgerIdentity.create();
286
+ const identity = await getLedgerIdentity();
271
287
  const governance = GovernanceCanister.create({
272
288
  agent: await getAgent(identity),
273
289
  hardwareWallet: true,
@@ -287,35 +303,21 @@ async function listNeurons() {
287
303
  }
288
304
  }
289
305
 
290
- const buf2hex = (buffer: ArrayBuffer): string => {
291
- return [...new Uint8Array(buffer)]
292
- .map((x) => x.toString(16).padStart(2, "0"))
293
- .join("");
294
- };
295
-
296
306
  /**
297
307
  * Fetches the balance of the main account on the wallet.
298
308
  */
299
309
  async function claimNeurons() {
300
- const identity = await LedgerIdentity.create();
301
-
302
- const bufferKey = identity.getPublicKey() as Secp256k1PublicKey;
303
- const hexPubKey = buf2hex(bufferKey.toRaw());
304
- const isHex = hexPubKey.match("^[0-9a-fA-F]+$");
305
- if (!isHex) {
306
- throw new Error(`${hexPubKey} is not a hex string.`);
307
- }
310
+ const identity = await getLedgerIdentity();
308
311
 
309
- if (hexPubKey.length < 130 || hexPubKey.length > 150) {
310
- throw new Error(`The key must be >= 130 characters and <= 150 characters.`);
311
- }
312
+ const publicKey = identity.getPublicKey() as Secp256k1PublicKey;
313
+ const hexPubKey = publicKey.toHex();
312
314
 
313
315
  const governance = await GenesisTokenCanister.create({
314
316
  agent: await getAgent(identity),
315
317
  });
316
318
 
317
319
  const claimedNeuronIds = await governance.claimNeurons({
318
- hexPubKey: hexPubKey,
320
+ hexPubKey,
319
321
  });
320
322
 
321
323
  ok(`Successfully claimed the following neurons: ${claimedNeuronIds}`);
@@ -528,6 +530,11 @@ async function main() {
528
530
  .default("https://ic0.app")
529
531
  .env("IC_NETWORK")
530
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
+ )
531
538
  .addCommand(
532
539
  new Command("info")
533
540
  .option("-n --no-show-on-device")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dfinity/hardware-wallet-cli",
3
- "version": "0.2.1",
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": {
@@ -10,7 +10,8 @@
10
10
  "prepack": "npm run build",
11
11
  "clean": "tsc --build --clean",
12
12
  "refresh": "rm -rf ./node_modules ./package-lock.json && npm install",
13
- "execute": "ts-node ./index.ts"
13
+ "execute": "ts-node ./index.ts",
14
+ "update:nns": "npm update @dfinity/nns"
14
15
  },
15
16
  "repository": {
16
17
  "type": "git",
@@ -24,7 +25,7 @@
24
25
  "homepage": "https://github.com/dfinity/hardware-wallet-cli#readme",
25
26
  "dependencies": {
26
27
  "@dfinity/agent": "^0.11.2",
27
- "@dfinity/nns": "^0.4.2",
28
+ "@dfinity/nns": "nightly",
28
29
  "@ledgerhq/hw-transport-node-hid-noevents": "^6.3.0",
29
30
  "@ledgerhq/hw-transport-webhid": "^6.27.1",
30
31
  "@zondax/ledger-icp": "^0.6.0",
@@ -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
  }