@rabby-wallet/eth-simple-keyring 4.2.0 → 5.0.0-alpha

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 (3) hide show
  1. package/index.js +176 -85
  2. package/package.json +9 -2
  3. package/CHANGELOG.md +0 -9
package/index.js CHANGED
@@ -1,23 +1,15 @@
1
1
  const { EventEmitter } = require('events');
2
- const ethUtil = require('ethereumjs-util');
3
- const randomBytes = require('randombytes');
2
+ // const Wallet = require('ethereumjs-wallet').default;
3
+ const ethUtil = require('@ethereumjs/util');
4
+ const sigUtil = require('@metamask/eth-sig-util');
5
+ const { keccak256 } = require('ethereum-cryptography/keccak');
6
+ const { getRandomBytesSync } = require('ethereum-cryptography/random');
4
7
 
5
8
  const type = 'Simple Key Pair';
6
- const {
7
- concatSig,
8
- decrypt,
9
- getEncryptionPublicKey,
10
- normalize,
11
- personalSign,
12
- signTypedData,
13
- SignTypedDataVersion,
14
- } = require('@metamask/eth-sig-util');
15
9
 
16
10
  function generateKey() {
17
- const privateKey = randomBytes(32);
18
- // I don't think this is possible, but this validation was here previously,
19
- // so it has been preserved just in case.
20
- // istanbul ignore next
11
+ const privateKey = getRandomBytesSync(32);
12
+
21
13
  if (!ethUtil.isValidPrivate(privateKey)) {
22
14
  throw new Error(
23
15
  'Private key does not satisfy the curve requirements (ie. it is invalid)',
@@ -26,103 +18,192 @@ function generateKey() {
26
18
  return privateKey;
27
19
  }
28
20
 
21
+ function add0x(hexadecimal) {
22
+ if (hexadecimal.startsWith('0x')) {
23
+ return hexadecimal;
24
+ }
25
+
26
+ if (hexadecimal.startsWith('0X')) {
27
+ return `0x${hexadecimal.substring(2)}`;
28
+ }
29
+
30
+ return `0x${hexadecimal}`;
31
+ }
32
+
29
33
  class SimpleKeyring extends EventEmitter {
30
34
  constructor(opts) {
31
35
  super();
32
36
  this.type = type;
33
- this._wallets = [];
37
+ this.wallets = [];
34
38
  this.deserialize(opts);
35
39
  }
36
40
 
37
- async serialize() {
38
- return this._wallets.map(({ privateKey }) => privateKey.toString('hex'));
41
+ serialize() {
42
+ return Promise.resolve(
43
+ this.wallets.map((w) =>
44
+ ethUtil.stripHexPrefix(ethUtil.bytesToHex(w.privateKey)),
45
+ ),
46
+ );
39
47
  }
40
48
 
41
- async deserialize(privateKeys = []) {
42
- this._wallets = privateKeys.map((hexPrivateKey) => {
43
- const strippedHexPrivateKey = ethUtil.stripHexPrefix(hexPrivateKey);
44
- const privateKey = Buffer.from(strippedHexPrivateKey, 'hex');
45
- const publicKey = ethUtil.privateToPublic(privateKey);
46
- return { privateKey, publicKey };
49
+ deserialize(privateKeys = []) {
50
+ return new Promise((resolve, reject) => {
51
+ try {
52
+ this.wallets = privateKeys.map((hexPrivateKey) => {
53
+ let privk = hexPrivateKey;
54
+ if (!privk.startsWith('0x')) {
55
+ privk = ethUtil.addHexPrefix(privk);
56
+ }
57
+ const privateKey = ethUtil.hexToBytes(privk);
58
+ const publicKey = ethUtil.privateToPublic(privateKey);
59
+ return { privateKey, publicKey };
60
+ });
61
+ } catch (e) {
62
+ reject(e);
63
+ }
64
+ resolve();
47
65
  });
48
66
  }
49
67
 
50
- async addAccounts(n = 1) {
68
+ addAccounts(n = 1) {
51
69
  const newWallets = [];
52
70
  for (let i = 0; i < n; i++) {
53
71
  const privateKey = generateKey();
54
- const publicKey = ethUtil.privateToPublic(privateKey);
55
- newWallets.push({ privateKey, publicKey });
72
+ newWallets.push({
73
+ privateKey,
74
+ publicKey: ethUtil.privateToPublic(privateKey),
75
+ });
56
76
  }
57
- this._wallets = this._wallets.concat(newWallets);
77
+ this.wallets = this.wallets.concat(newWallets);
58
78
  const hexWallets = newWallets.map(({ publicKey }) =>
59
- ethUtil.bufferToHex(ethUtil.publicToAddress(publicKey)),
79
+ add0x(ethUtil.bytesToHex(ethUtil.publicToAddress(publicKey))),
60
80
  );
61
- return hexWallets;
81
+ return Promise.resolve(hexWallets);
62
82
  }
63
83
 
64
- async getAccounts() {
65
- return this._wallets.map(({ publicKey }) =>
66
- ethUtil.bufferToHex(ethUtil.publicToAddress(publicKey)),
84
+ getAccounts() {
85
+ return Promise.resolve(
86
+ this.wallets.map(({ publicKey }) => {
87
+ return add0x(ethUtil.bytesToHex(ethUtil.publicToAddress(publicKey)));
88
+ }),
67
89
  );
68
90
  }
69
91
 
70
92
  // tx is an instance of the ethereumjs-transaction class.
71
- async signTransaction(address, tx, opts = {}) {
72
- const privKey = this._getPrivateKeyFor(address, opts);
73
- const signedTx = tx.sign(privKey);
93
+ signTransaction(address, tx, opts = {}) {
94
+ const privKey = ethUtil.addHexPrefix(this.getPrivateKeyFor(address, opts));
95
+ const signedTx = tx.sign(Buffer.from(privKey, 'hex'));
74
96
  // Newer versions of Ethereumjs-tx are immutable and return a new tx object
75
- return signedTx === undefined ? tx : signedTx;
97
+ return Promise.resolve(signedTx === undefined ? tx : signedTx);
76
98
  }
77
99
 
78
100
  // For eth_sign, we need to sign arbitrary data:
79
- async signMessage(address, data, opts = {}) {
101
+ signMessage(address, data, opts = {}) {
80
102
  const message = ethUtil.stripHexPrefix(data);
81
- const privKey = this._getPrivateKeyFor(address, opts);
103
+ const privKey = this.getPrivateKeyFor(address, opts);
82
104
  const msgSig = ethUtil.ecsign(Buffer.from(message, 'hex'), privKey);
83
- const rawMsgSig = concatSig(msgSig.v, msgSig.r, msgSig.s);
84
- return rawMsgSig;
105
+ const rawMsgSig = sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s);
106
+ return Promise.resolve(rawMsgSig);
107
+ }
108
+
109
+ // For eth_sign, we need to sign transactions:
110
+ newGethSignMessage(withAccount, msgHex, opts = {}) {
111
+ const privKey = this.getPrivateKeyFor(withAccount, opts);
112
+ const msgBuffer = ethUtil.toBuffer(msgHex);
113
+ const msgHash = ethUtil.hashPersonalMessage(msgBuffer);
114
+ const msgSig = ethUtil.ecsign(msgHash, privKey);
115
+ const rawMsgSig = sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s);
116
+ return Promise.resolve(rawMsgSig);
85
117
  }
86
118
 
87
119
  // For personal_sign, we need to prefix the message:
88
- async signPersonalMessage(address, msgHex, opts = {}) {
89
- const privKey = this._getPrivateKeyFor(address, opts);
90
- const privateKey = Buffer.from(privKey, 'hex');
91
- const sig = personalSign({ privateKey, data: msgHex });
92
- return sig;
120
+ signPersonalMessage(address, msgHex, opts = {}) {
121
+ const privKey = ethUtil.stripHexPrefix(
122
+ ethUtil.bytesToHex(this.getPrivateKeyFor(address, opts)),
123
+ );
124
+ const sig = sigUtil.personalSign({
125
+ privateKey: privKey,
126
+ data: msgHex,
127
+ });
128
+ return Promise.resolve(sig);
93
129
  }
94
130
 
95
131
  // For eth_decryptMessage:
96
- async decryptMessage(withAccount, encryptedData) {
132
+ decryptMessage(withAccount, encryptedData) {
97
133
  const wallet = this._getWalletForAccount(withAccount);
98
- const privateKey = ethUtil.stripHexPrefix(wallet.privateKey);
99
- const sig = decrypt({ privateKey, encryptedData });
100
- return sig;
134
+ const privKey = ethUtil.stripHexPrefix(
135
+ ethUtil.bytesToHex(wallet.privateKey),
136
+ );
137
+ const sig = sigUtil.decrypt({
138
+ encryptedData,
139
+ privateKey: privKey,
140
+ });
141
+ return Promise.resolve(sig);
101
142
  }
102
143
 
103
144
  // personal_signTypedData, signs data along with the schema
104
- async signTypedData(
105
- withAccount,
106
- typedData,
107
- opts = { version: SignTypedDataVersion.V1 },
108
- ) {
109
- // Treat invalid versions as "V1"
110
- const version = Object.keys(SignTypedDataVersion).includes(opts.version)
111
- ? opts.version
112
- : SignTypedDataVersion.V1;
145
+ signTypedData(withAccount, typedData, opts = { version: 'V1' }) {
146
+ switch (opts.version) {
147
+ case 'V1':
148
+ return this.signTypedData_v1(withAccount, typedData, opts);
149
+ case 'V3':
150
+ return this.signTypedData_v3(withAccount, typedData, opts);
151
+ case 'V4':
152
+ return this.signTypedData_v4(withAccount, typedData, opts);
153
+ default:
154
+ return this.signTypedData_v1(withAccount, typedData, opts);
155
+ }
156
+ }
113
157
 
114
- const privateKey = this._getPrivateKeyFor(withAccount, opts);
115
- return signTypedData({ privateKey, data: typedData, version });
158
+ // personal_signTypedData, signs data along with the schema
159
+ signTypedData_v1(withAccount, typedData, opts = {}) {
160
+ const privKey = ethUtil.stripHexPrefix(
161
+ ethUtil.bytesToHex(this.getPrivateKeyFor(withAccount, opts)),
162
+ );
163
+ const sig = sigUtil.signTypedData({
164
+ privateKey: Buffer.from(privKey, 'hex'),
165
+ data: typedData,
166
+ version: sigUtil.SignTypedDataVersion.V1,
167
+ });
168
+ return Promise.resolve(sig);
169
+ }
170
+
171
+ // personal_signTypedData, signs data along with the schema
172
+ signTypedData_v3(withAccount, typedData, opts = {}) {
173
+ const privKey = ethUtil.stripHexPrefix(
174
+ ethUtil.bytesToHex(this.getPrivateKeyFor(withAccount, opts)),
175
+ );
176
+ const sig = sigUtil.signTypedData({
177
+ privateKey: Buffer.from(privKey, 'hex'),
178
+ data: typedData,
179
+ version: sigUtil.SignTypedDataVersion.V3,
180
+ });
181
+ return Promise.resolve(sig);
182
+ }
183
+
184
+ // personal_signTypedData, signs data along with the schema
185
+ signTypedData_v4(withAccount, typedData, opts = {}) {
186
+ const privKey = ethUtil.stripHexPrefix(
187
+ ethUtil.bytesToHex(this.getPrivateKeyFor(withAccount, opts)),
188
+ );
189
+ const sig = sigUtil.signTypedData({
190
+ privateKey: Buffer.from(privKey, 'hex'),
191
+ data: typedData,
192
+ version: sigUtil.SignTypedDataVersion.V4,
193
+ });
194
+ return Promise.resolve(sig);
116
195
  }
117
196
 
118
197
  // get public key for nacl
119
- async getEncryptionPublicKey(withAccount, opts = {}) {
120
- const privKey = this._getPrivateKeyFor(withAccount, opts);
121
- const publicKey = getEncryptionPublicKey(privKey);
122
- return publicKey;
198
+ getEncryptionPublicKey(withAccount, opts = {}) {
199
+ const privKey = ethUtil.stripHexPrefix(
200
+ ethUtil.bytesToHex(this.getPrivateKeyFor(withAccount, opts)),
201
+ );
202
+ const publicKey = sigUtil.getEncryptionPublicKey(privKey);
203
+ return Promise.resolve(publicKey);
123
204
  }
124
205
 
125
- _getPrivateKeyFor(address, opts = {}) {
206
+ getPrivateKeyFor(address, opts = {}) {
126
207
  if (!address) {
127
208
  throw new Error('Must specify address.');
128
209
  }
@@ -131,41 +212,49 @@ class SimpleKeyring extends EventEmitter {
131
212
  }
132
213
 
133
214
  // returns an address specific to an app
134
- async getAppKeyAddress(address, origin) {
215
+ getAppKeyAddress(address, origin) {
135
216
  if (!origin || typeof origin !== 'string') {
136
217
  throw new Error(`'origin' must be a non-empty string`);
137
218
  }
138
- const wallet = this._getWalletForAccount(address, {
139
- withAppKeyOrigin: origin,
219
+ return new Promise((resolve, reject) => {
220
+ try {
221
+ const wallet = this._getWalletForAccount(address, {
222
+ withAppKeyOrigin: origin,
223
+ });
224
+ const appKeyAddress = sigUtil.normalize(
225
+ ethUtil.bytesToHex(ethUtil.publicToAddress(wallet.publicKey)),
226
+ );
227
+ return resolve(appKeyAddress);
228
+ } catch (e) {
229
+ return reject(e);
230
+ }
140
231
  });
141
- const appKeyAddress = normalize(
142
- ethUtil.publicToAddress(wallet.publicKey).toString('hex'),
143
- );
144
- return appKeyAddress;
145
232
  }
146
233
 
147
234
  // exportAccount should return a hex-encoded private key:
148
- async exportAccount(address, opts = {}) {
235
+ exportAccount(address, opts = {}) {
149
236
  const wallet = this._getWalletForAccount(address, opts);
150
- return wallet.privateKey.toString('hex');
237
+ return Promise.resolve(ethUtil.bytesToHex(wallet.privateKey));
151
238
  }
152
239
 
153
240
  removeAccount(address) {
154
241
  if (
155
- !this._wallets
242
+ !this.wallets
156
243
  .map(({ publicKey }) =>
157
- ethUtil.bufferToHex(ethUtil.publicToAddress(publicKey)).toLowerCase(),
244
+ add0x(
245
+ ethUtil.bytesToHex(ethUtil.publicToAddress(publicKey)),
246
+ ).toLowerCase(),
158
247
  )
159
248
  .includes(address.toLowerCase())
160
249
  ) {
161
250
  throw new Error(`Address ${address} not found in this keyring`);
162
251
  }
163
252
 
164
- this._wallets = this._wallets.filter(
253
+ this.wallets = this.wallets.filter(
165
254
  ({ publicKey }) =>
166
- ethUtil
167
- .bufferToHex(ethUtil.publicToAddress(publicKey))
168
- .toLowerCase() !== address.toLowerCase(),
255
+ add0x(
256
+ ethUtil.bytesToHex(ethUtil.publicToAddress(publicKey)),
257
+ ).toLowerCase() !== address.toLowerCase(),
169
258
  );
170
259
  }
171
260
 
@@ -173,10 +262,12 @@ class SimpleKeyring extends EventEmitter {
173
262
  * @private
174
263
  */
175
264
  _getWalletForAccount(account, opts = {}) {
176
- const address = normalize(account);
177
- let wallet = this._wallets.find(
265
+ const address = sigUtil.normalize(account);
266
+ let wallet = this.wallets.find(
178
267
  ({ publicKey }) =>
179
- ethUtil.bufferToHex(ethUtil.publicToAddress(publicKey)) === address,
268
+ add0x(
269
+ ethUtil.bytesToHex(ethUtil.publicToAddress(publicKey)),
270
+ ).toLowerCase() === address.toLowerCase(),
180
271
  );
181
272
  if (!wallet) {
182
273
  throw new Error('Simple Keyring - Unable to find matching address.');
@@ -186,7 +277,7 @@ class SimpleKeyring extends EventEmitter {
186
277
  const { privateKey } = wallet;
187
278
  const appKeyOriginBuffer = Buffer.from(opts.withAppKeyOrigin, 'utf8');
188
279
  const appKeyBuffer = Buffer.concat([privateKey, appKeyOriginBuffer]);
189
- const appKeyPrivateKey = ethUtil.keccak(appKeyBuffer, 256);
280
+ const appKeyPrivateKey = keccak256(appKeyBuffer);
190
281
  const appKeyPublicKey = ethUtil.privateToPublic(appKeyPrivateKey);
191
282
  wallet = { privateKey: appKeyPrivateKey, publicKey: appKeyPublicKey };
192
283
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rabby-wallet/eth-simple-keyring",
3
- "version": "4.2.0",
3
+ "version": "5.0.0-alpha",
4
4
  "description": "A simple standard interface for a series of Ethereum private keys.",
5
5
  "keywords": [
6
6
  "ethereum",
@@ -30,8 +30,15 @@
30
30
  "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write"
31
31
  },
32
32
  "dependencies": {
33
- "@metamask/eth-sig-util": "^4.0.0",
33
+ "@ethereumjs/util": "^9.0.0",
34
+ "@metamask/eth-sig-util": "^7.0.0",
35
+ "@metamask/utils": "^8.1.0",
36
+ "chai": "^4.3.4",
37
+ "eth-sig-util": "^3.0.1",
38
+ "ethereum-cryptography": "^2.1.2",
39
+ "ethereumjs-tx": "^2.1.2",
34
40
  "ethereumjs-util": "^7.0.9",
41
+ "ethereumjs-wallet": "^1.0.2",
35
42
  "randombytes": "^2.1.0"
36
43
  },
37
44
  "devDependencies": {
package/CHANGELOG.md DELETED
@@ -1,9 +0,0 @@
1
- # Changelog
2
- All notable changes to this project will be documented in this file.
3
-
4
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
- ## [Unreleased]
8
-
9
- [Unreleased]: https://github.com/MetaMask/eth-simple-keyring/