@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.
- package/index.js +176 -85
- package/package.json +9 -2
- package/CHANGELOG.md +0 -9
package/index.js
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
const { EventEmitter } = require('events');
|
|
2
|
-
const
|
|
3
|
-
const
|
|
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 =
|
|
18
|
-
|
|
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.
|
|
37
|
+
this.wallets = [];
|
|
34
38
|
this.deserialize(opts);
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
return
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
68
|
+
addAccounts(n = 1) {
|
|
51
69
|
const newWallets = [];
|
|
52
70
|
for (let i = 0; i < n; i++) {
|
|
53
71
|
const privateKey = generateKey();
|
|
54
|
-
|
|
55
|
-
|
|
72
|
+
newWallets.push({
|
|
73
|
+
privateKey,
|
|
74
|
+
publicKey: ethUtil.privateToPublic(privateKey),
|
|
75
|
+
});
|
|
56
76
|
}
|
|
57
|
-
this.
|
|
77
|
+
this.wallets = this.wallets.concat(newWallets);
|
|
58
78
|
const hexWallets = newWallets.map(({ publicKey }) =>
|
|
59
|
-
ethUtil.
|
|
79
|
+
add0x(ethUtil.bytesToHex(ethUtil.publicToAddress(publicKey))),
|
|
60
80
|
);
|
|
61
|
-
return hexWallets;
|
|
81
|
+
return Promise.resolve(hexWallets);
|
|
62
82
|
}
|
|
63
83
|
|
|
64
|
-
|
|
65
|
-
return
|
|
66
|
-
|
|
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
|
-
|
|
72
|
-
const privKey = this.
|
|
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
|
-
|
|
101
|
+
signMessage(address, data, opts = {}) {
|
|
80
102
|
const message = ethUtil.stripHexPrefix(data);
|
|
81
|
-
const privKey = this.
|
|
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
|
-
|
|
89
|
-
const privKey =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
132
|
+
decryptMessage(withAccount, encryptedData) {
|
|
97
133
|
const wallet = this._getWalletForAccount(withAccount);
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
:
|
|
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
|
-
|
|
115
|
-
|
|
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
|
-
|
|
120
|
-
const privKey =
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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
|
-
|
|
235
|
+
exportAccount(address, opts = {}) {
|
|
149
236
|
const wallet = this._getWalletForAccount(address, opts);
|
|
150
|
-
return wallet.privateKey
|
|
237
|
+
return Promise.resolve(ethUtil.bytesToHex(wallet.privateKey));
|
|
151
238
|
}
|
|
152
239
|
|
|
153
240
|
removeAccount(address) {
|
|
154
241
|
if (
|
|
155
|
-
!this.
|
|
242
|
+
!this.wallets
|
|
156
243
|
.map(({ publicKey }) =>
|
|
157
|
-
|
|
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.
|
|
253
|
+
this.wallets = this.wallets.filter(
|
|
165
254
|
({ publicKey }) =>
|
|
166
|
-
|
|
167
|
-
.
|
|
168
|
-
|
|
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.
|
|
265
|
+
const address = sigUtil.normalize(account);
|
|
266
|
+
let wallet = this.wallets.find(
|
|
178
267
|
({ publicKey }) =>
|
|
179
|
-
|
|
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 =
|
|
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": "
|
|
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
|
-
"@
|
|
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/
|