@metamask-previews/eth-hd-keyring 7.0.4-d5a036e → 8.0.0-22cd5fe
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/CHANGELOG.md +9 -1
- package/package.json +24 -8
- package/index.js +0 -312
- package/jest.config.js +0 -32
- package/test/index.js +0 -960
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [8.0.0]
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **BREAKING**: Bump `@metamask/eth-sig-util` dependency from `^7.0.3` to `^8.0.0` ([#79](https://github.com/MetaMask/accounts/pull/79))
|
|
15
|
+
- `signTypedData` no longer support `number` for addresses, see [here](https://github.com/MetaMask/eth-sig-util/blob/main/CHANGELOG.md#800).
|
|
16
|
+
|
|
10
17
|
## [7.0.4]
|
|
11
18
|
|
|
12
19
|
### Changed
|
|
@@ -130,7 +137,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
130
137
|
- Deserialize method (and `HdKeyring` constructor by extension) can no longer be passed an options object containing a value for `numberOfAccounts` if it is not also containing a value for `mnemonic`.
|
|
131
138
|
- Package name changed from `eth-hd-keyring` to `@metamask/eth-hd-keyring`.
|
|
132
139
|
|
|
133
|
-
[Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/eth-hd-keyring@
|
|
140
|
+
[Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/eth-hd-keyring@8.0.0...HEAD
|
|
141
|
+
[8.0.0]: https://github.com/MetaMask/accounts/compare/@metamask/eth-hd-keyring@7.0.4...@metamask/eth-hd-keyring@8.0.0
|
|
134
142
|
[7.0.4]: https://github.com/MetaMask/accounts/compare/@metamask/eth-hd-keyring@7.0.3...@metamask/eth-hd-keyring@7.0.4
|
|
135
143
|
[7.0.3]: https://github.com/MetaMask/accounts/compare/@metamask/eth-hd-keyring@7.0.2...@metamask/eth-hd-keyring@7.0.3
|
|
136
144
|
[7.0.2]: https://github.com/MetaMask/accounts/compare/@metamask/eth-hd-keyring@7.0.1...@metamask/eth-hd-keyring@7.0.2
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/eth-hd-keyring",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0-22cd5fe",
|
|
4
4
|
"description": "A simple standard interface for a seed phrase generated set of Ethereum accounts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ethereum",
|
|
@@ -16,21 +16,36 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "ISC",
|
|
18
18
|
"author": "Dan Finlay",
|
|
19
|
-
"
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"default": "./dist/index.mjs"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/index.d.cts",
|
|
27
|
+
"default": "./dist/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"types": "./dist/index.d.cts",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/"
|
|
35
|
+
],
|
|
20
36
|
"scripts": {
|
|
21
|
-
"build": "",
|
|
22
|
-
"build:clean": "",
|
|
23
|
-
"build:
|
|
37
|
+
"build": "ts-bridge --project tsconfig.build.json --no-references",
|
|
38
|
+
"build:clean": "yarn build --clean",
|
|
39
|
+
"build:docs": "typedoc",
|
|
24
40
|
"changelog:update": "../../scripts/update-changelog.sh @metamask/eth-hd-keyring",
|
|
25
41
|
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/eth-hd-keyring",
|
|
26
42
|
"publish:preview": "yarn npm publish --tag preview",
|
|
27
43
|
"test": "jest",
|
|
28
|
-
"test:clean": "jest --clearCache"
|
|
29
|
-
"test:verbose": "jest --verbose"
|
|
44
|
+
"test:clean": "jest --clearCache"
|
|
30
45
|
},
|
|
31
46
|
"dependencies": {
|
|
32
47
|
"@ethereumjs/util": "^8.1.0",
|
|
33
|
-
"@metamask/eth-sig-util": "^
|
|
48
|
+
"@metamask/eth-sig-util": "^8.0.0",
|
|
34
49
|
"@metamask/scure-bip39": "^2.1.1",
|
|
35
50
|
"@metamask/utils": "^9.2.1",
|
|
36
51
|
"ethereum-cryptography": "^2.1.2"
|
|
@@ -42,6 +57,7 @@
|
|
|
42
57
|
"@metamask/auto-changelog": "^3.4.4",
|
|
43
58
|
"@metamask/bip39": "^4.0.0",
|
|
44
59
|
"@metamask/eth-hd-keyring": "4.0.1",
|
|
60
|
+
"@ts-bridge/cli": "^0.6.0",
|
|
45
61
|
"@types/jest": "^29.5.12",
|
|
46
62
|
"deepmerge": "^4.2.2",
|
|
47
63
|
"jest": "^29.5.0"
|
package/index.js
DELETED
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
privateToPublic,
|
|
3
|
-
publicToAddress,
|
|
4
|
-
ecsign,
|
|
5
|
-
arrToBufArr,
|
|
6
|
-
bufferToHex,
|
|
7
|
-
} = require('@ethereumjs/util');
|
|
8
|
-
const {
|
|
9
|
-
concatSig,
|
|
10
|
-
decrypt,
|
|
11
|
-
getEncryptionPublicKey,
|
|
12
|
-
normalize,
|
|
13
|
-
personalSign,
|
|
14
|
-
signTypedData,
|
|
15
|
-
SignTypedDataVersion,
|
|
16
|
-
} = require('@metamask/eth-sig-util');
|
|
17
|
-
const bip39 = require('@metamask/scure-bip39');
|
|
18
|
-
const { wordlist } = require('@metamask/scure-bip39/dist/wordlists/english');
|
|
19
|
-
const { assertIsHexString, remove0x } = require('@metamask/utils');
|
|
20
|
-
const { HDKey } = require('ethereum-cryptography/hdkey');
|
|
21
|
-
const { keccak256 } = require('ethereum-cryptography/keccak');
|
|
22
|
-
const { bytesToHex } = require('ethereum-cryptography/utils');
|
|
23
|
-
|
|
24
|
-
// Options:
|
|
25
|
-
const hdPathString = `m/44'/60'/0'/0`;
|
|
26
|
-
const type = 'HD Key Tree';
|
|
27
|
-
|
|
28
|
-
class HdKeyring {
|
|
29
|
-
/* PUBLIC METHODS */
|
|
30
|
-
constructor(opts = {}) {
|
|
31
|
-
this.type = type;
|
|
32
|
-
this._wallets = [];
|
|
33
|
-
this.deserialize(opts);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
generateRandomMnemonic() {
|
|
37
|
-
this._initFromMnemonic(bip39.generateMnemonic(wordlist));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
_uint8ArrayToString(mnemonic) {
|
|
41
|
-
const recoveredIndices = Array.from(
|
|
42
|
-
new Uint16Array(new Uint8Array(mnemonic).buffer),
|
|
43
|
-
);
|
|
44
|
-
return recoveredIndices.map((i) => wordlist[i]).join(' ');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
_stringToUint8Array(mnemonic) {
|
|
48
|
-
const indices = mnemonic.split(' ').map((word) => wordlist.indexOf(word));
|
|
49
|
-
return new Uint8Array(new Uint16Array(indices).buffer);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
_mnemonicToUint8Array(mnemonic) {
|
|
53
|
-
let mnemonicData = mnemonic;
|
|
54
|
-
// when encrypted/decrypted, buffers get cast into js object with a property type set to buffer
|
|
55
|
-
if (mnemonic && mnemonic.type && mnemonic.type === 'Buffer') {
|
|
56
|
-
mnemonicData = mnemonic.data;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
// this block is for backwards compatibility with vaults that were previously stored as buffers, number arrays or plain text strings
|
|
61
|
-
typeof mnemonicData === 'string' ||
|
|
62
|
-
Buffer.isBuffer(mnemonicData) ||
|
|
63
|
-
Array.isArray(mnemonicData)
|
|
64
|
-
) {
|
|
65
|
-
let mnemonicAsString = mnemonicData;
|
|
66
|
-
if (Array.isArray(mnemonicData)) {
|
|
67
|
-
mnemonicAsString = Buffer.from(mnemonicData).toString();
|
|
68
|
-
} else if (Buffer.isBuffer(mnemonicData)) {
|
|
69
|
-
mnemonicAsString = mnemonicData.toString();
|
|
70
|
-
}
|
|
71
|
-
return this._stringToUint8Array(mnemonicAsString);
|
|
72
|
-
} else if (
|
|
73
|
-
mnemonicData instanceof Object &&
|
|
74
|
-
!(mnemonicData instanceof Uint8Array)
|
|
75
|
-
) {
|
|
76
|
-
// when encrypted/decrypted the Uint8Array becomes a js object we need to cast back to a Uint8Array
|
|
77
|
-
return Uint8Array.from(Object.values(mnemonicData));
|
|
78
|
-
}
|
|
79
|
-
return mnemonicData;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
serialize() {
|
|
83
|
-
const mnemonicAsString = this._uint8ArrayToString(this.mnemonic);
|
|
84
|
-
const uint8ArrayMnemonic = new TextEncoder('utf-8').encode(
|
|
85
|
-
mnemonicAsString,
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
return Promise.resolve({
|
|
89
|
-
mnemonic: Array.from(uint8ArrayMnemonic),
|
|
90
|
-
numberOfAccounts: this._wallets.length,
|
|
91
|
-
hdPath: this.hdPath,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
deserialize(opts = {}) {
|
|
96
|
-
if (opts.numberOfAccounts && !opts.mnemonic) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
'Eth-Hd-Keyring: Deserialize method cannot be called with an opts value for numberOfAccounts and no menmonic',
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (this.root) {
|
|
103
|
-
throw new Error(
|
|
104
|
-
'Eth-Hd-Keyring: Secret recovery phrase already provided',
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
this.opts = opts;
|
|
108
|
-
this._wallets = [];
|
|
109
|
-
this.mnemonic = null;
|
|
110
|
-
this.root = null;
|
|
111
|
-
this.hdPath = opts.hdPath || hdPathString;
|
|
112
|
-
|
|
113
|
-
if (opts.mnemonic) {
|
|
114
|
-
this._initFromMnemonic(opts.mnemonic);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (opts.numberOfAccounts) {
|
|
118
|
-
return this.addAccounts(opts.numberOfAccounts);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return Promise.resolve([]);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
addAccounts(numberOfAccounts = 1) {
|
|
125
|
-
if (!this.root) {
|
|
126
|
-
throw new Error('Eth-Hd-Keyring: No secret recovery phrase provided');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const oldLen = this._wallets.length;
|
|
130
|
-
const newWallets = [];
|
|
131
|
-
for (let i = oldLen; i < numberOfAccounts + oldLen; i++) {
|
|
132
|
-
const wallet = this.root.deriveChild(i);
|
|
133
|
-
newWallets.push(wallet);
|
|
134
|
-
this._wallets.push(wallet);
|
|
135
|
-
}
|
|
136
|
-
const hexWallets = newWallets.map((w) => {
|
|
137
|
-
return this._addressfromPublicKey(w.publicKey);
|
|
138
|
-
});
|
|
139
|
-
return Promise.resolve(hexWallets);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
getAccounts() {
|
|
143
|
-
return this._wallets.map((w) => this._addressfromPublicKey(w.publicKey));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/* BASE KEYRING METHODS */
|
|
147
|
-
|
|
148
|
-
// returns an address specific to an app
|
|
149
|
-
async getAppKeyAddress(address, origin) {
|
|
150
|
-
if (!origin || typeof origin !== 'string') {
|
|
151
|
-
throw new Error(`'origin' must be a non-empty string`);
|
|
152
|
-
}
|
|
153
|
-
const wallet = this._getWalletForAccount(address, {
|
|
154
|
-
withAppKeyOrigin: origin,
|
|
155
|
-
});
|
|
156
|
-
const appKeyAddress = normalize(
|
|
157
|
-
publicToAddress(wallet.publicKey).toString('hex'),
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
return appKeyAddress;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// exportAccount should return a hex-encoded private key:
|
|
164
|
-
async exportAccount(address, opts = {}) {
|
|
165
|
-
const wallet = this._getWalletForAccount(address, opts);
|
|
166
|
-
return bytesToHex(wallet.privateKey);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// tx is an instance of the ethereumjs-transaction class.
|
|
170
|
-
async signTransaction(address, tx, opts = {}) {
|
|
171
|
-
const privKey = this._getPrivateKeyFor(address, opts);
|
|
172
|
-
const signedTx = tx.sign(privKey);
|
|
173
|
-
// Newer versions of Ethereumjs-tx are immutable and return a new tx object
|
|
174
|
-
return signedTx === undefined ? tx : signedTx;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// For eth_sign, we need to sign arbitrary data:
|
|
178
|
-
async signMessage(address, data, opts = {}) {
|
|
179
|
-
assertIsHexString(data);
|
|
180
|
-
const message = remove0x(data);
|
|
181
|
-
const privKey = this._getPrivateKeyFor(address, opts);
|
|
182
|
-
const msgSig = ecsign(Buffer.from(message, 'hex'), privKey);
|
|
183
|
-
const rawMsgSig = concatSig(msgSig.v, msgSig.r, msgSig.s);
|
|
184
|
-
return rawMsgSig;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// For personal_sign, we need to prefix the message:
|
|
188
|
-
async signPersonalMessage(address, msgHex, opts = {}) {
|
|
189
|
-
const privKey = this._getPrivateKeyFor(address, opts);
|
|
190
|
-
const privateKey = Buffer.from(privKey, 'hex');
|
|
191
|
-
const sig = personalSign({ privateKey, data: msgHex });
|
|
192
|
-
return sig;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// For eth_decryptMessage:
|
|
196
|
-
async decryptMessage(withAccount, encryptedData) {
|
|
197
|
-
const wallet = this._getWalletForAccount(withAccount);
|
|
198
|
-
const { privateKey: privateKeyAsUint8Array } = wallet;
|
|
199
|
-
const privateKeyAsHex = Buffer.from(privateKeyAsUint8Array).toString('hex');
|
|
200
|
-
const sig = decrypt({ privateKey: privateKeyAsHex, encryptedData });
|
|
201
|
-
return sig;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// personal_signTypedData, signs data along with the schema
|
|
205
|
-
async signTypedData(
|
|
206
|
-
withAccount,
|
|
207
|
-
typedData,
|
|
208
|
-
opts = { version: SignTypedDataVersion.V1 },
|
|
209
|
-
) {
|
|
210
|
-
// Treat invalid versions as "V1"
|
|
211
|
-
const version = Object.keys(SignTypedDataVersion).includes(opts.version)
|
|
212
|
-
? opts.version
|
|
213
|
-
: SignTypedDataVersion.V1;
|
|
214
|
-
|
|
215
|
-
const privateKey = this._getPrivateKeyFor(withAccount, opts);
|
|
216
|
-
return signTypedData({ privateKey, data: typedData, version });
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
removeAccount(account) {
|
|
220
|
-
const address = normalize(account);
|
|
221
|
-
if (
|
|
222
|
-
!this._wallets
|
|
223
|
-
.map(({ publicKey }) => this._addressfromPublicKey(publicKey))
|
|
224
|
-
.includes(address)
|
|
225
|
-
) {
|
|
226
|
-
throw new Error(`Address ${address} not found in this keyring`);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
this._wallets = this._wallets.filter(
|
|
230
|
-
({ publicKey }) => this._addressfromPublicKey(publicKey) !== address,
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// get public key for nacl
|
|
235
|
-
async getEncryptionPublicKey(withAccount, opts = {}) {
|
|
236
|
-
const privKey = this._getPrivateKeyFor(withAccount, opts);
|
|
237
|
-
const publicKey = getEncryptionPublicKey(privKey);
|
|
238
|
-
return publicKey;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
_getPrivateKeyFor(address, opts = {}) {
|
|
242
|
-
if (!address) {
|
|
243
|
-
throw new Error('Must specify address.');
|
|
244
|
-
}
|
|
245
|
-
const wallet = this._getWalletForAccount(address, opts);
|
|
246
|
-
return wallet.privateKey;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
_getWalletForAccount(account, opts = {}) {
|
|
250
|
-
const address = normalize(account);
|
|
251
|
-
let wallet = this._wallets.find(({ publicKey }) => {
|
|
252
|
-
return this._addressfromPublicKey(publicKey) === address;
|
|
253
|
-
});
|
|
254
|
-
if (!wallet) {
|
|
255
|
-
throw new Error('HD Keyring - Unable to find matching address.');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (opts.withAppKeyOrigin) {
|
|
259
|
-
const { privateKey } = wallet;
|
|
260
|
-
const appKeyOriginBuffer = Buffer.from(opts.withAppKeyOrigin, 'utf8');
|
|
261
|
-
const appKeyBuffer = Buffer.concat([privateKey, appKeyOriginBuffer]);
|
|
262
|
-
const appKeyPrivateKey = arrToBufArr(keccak256(appKeyBuffer, 256));
|
|
263
|
-
const appKeyPublicKey = privateToPublic(appKeyPrivateKey);
|
|
264
|
-
wallet = { privateKey: appKeyPrivateKey, publicKey: appKeyPublicKey };
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return wallet;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/* PRIVATE / UTILITY METHODS */
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Sets appropriate properties for the keyring based on the given
|
|
274
|
-
* BIP39-compliant mnemonic.
|
|
275
|
-
*
|
|
276
|
-
* @param {string|Array<number>|Buffer} mnemonic - A seed phrase represented
|
|
277
|
-
* as a string, an array of UTF-8 bytes, or a Buffer. Mnemonic input
|
|
278
|
-
* passed as type buffer or array of UTF-8 bytes must be NFKD normalized.
|
|
279
|
-
*/
|
|
280
|
-
_initFromMnemonic(mnemonic) {
|
|
281
|
-
if (this.root) {
|
|
282
|
-
throw new Error(
|
|
283
|
-
'Eth-Hd-Keyring: Secret recovery phrase already provided',
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
this.mnemonic = this._mnemonicToUint8Array(mnemonic);
|
|
288
|
-
|
|
289
|
-
// validate before initializing
|
|
290
|
-
const isValid = bip39.validateMnemonic(this.mnemonic, wordlist);
|
|
291
|
-
if (!isValid) {
|
|
292
|
-
throw new Error(
|
|
293
|
-
'Eth-Hd-Keyring: Invalid secret recovery phrase provided',
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// eslint-disable-next-line n/no-sync
|
|
298
|
-
const seed = bip39.mnemonicToSeedSync(this.mnemonic, wordlist);
|
|
299
|
-
this.hdWallet = HDKey.fromMasterSeed(seed);
|
|
300
|
-
this.root = this.hdWallet.derive(this.hdPath);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// small helper function to convert publicKey in Uint8Array form to a publicAddress as a hex
|
|
304
|
-
_addressfromPublicKey(publicKey) {
|
|
305
|
-
return bufferToHex(
|
|
306
|
-
publicToAddress(Buffer.from(publicKey), true),
|
|
307
|
-
).toLowerCase();
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
HdKeyring.type = type;
|
|
312
|
-
module.exports = HdKeyring;
|
package/jest.config.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* For a detailed explanation regarding each configuration property and type check, visit:
|
|
3
|
-
* https://jestjs.io/docs/configuration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const merge = require('deepmerge');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
|
|
9
|
-
const baseConfig = require('../../jest.config.packages');
|
|
10
|
-
|
|
11
|
-
const displayName = path.basename(__dirname);
|
|
12
|
-
|
|
13
|
-
module.exports = merge(baseConfig, {
|
|
14
|
-
// The display name when running multiple projects
|
|
15
|
-
displayName,
|
|
16
|
-
|
|
17
|
-
// An array of regexp pattern strings used to skip coverage collection
|
|
18
|
-
coveragePathIgnorePatterns: ['./test'],
|
|
19
|
-
|
|
20
|
-
// The glob patterns Jest uses to detect test files
|
|
21
|
-
testMatch: ['**/test/**/*.[jt]s?(x)'],
|
|
22
|
-
|
|
23
|
-
// An object that configures minimum threshold enforcement for coverage results
|
|
24
|
-
coverageThreshold: {
|
|
25
|
-
global: {
|
|
26
|
-
branches: 84,
|
|
27
|
-
functions: 100,
|
|
28
|
-
lines: 95,
|
|
29
|
-
statements: 95,
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
});
|
package/test/index.js
DELETED
|
@@ -1,960 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
TransactionFactory,
|
|
3
|
-
Transaction: EthereumTx,
|
|
4
|
-
} = require('@ethereumjs/tx');
|
|
5
|
-
const {
|
|
6
|
-
isValidAddress,
|
|
7
|
-
bufferToHex,
|
|
8
|
-
toBuffer,
|
|
9
|
-
ecrecover,
|
|
10
|
-
pubToAddress,
|
|
11
|
-
} = require('@ethereumjs/util');
|
|
12
|
-
const oldMMForkBIP39 = require('@metamask/bip39');
|
|
13
|
-
const OldHdKeyring = require('@metamask/eth-hd-keyring');
|
|
14
|
-
const {
|
|
15
|
-
normalize,
|
|
16
|
-
personalSign,
|
|
17
|
-
recoverPersonalSignature,
|
|
18
|
-
recoverTypedSignature,
|
|
19
|
-
signTypedData,
|
|
20
|
-
SignTypedDataVersion,
|
|
21
|
-
encrypt,
|
|
22
|
-
} = require('@metamask/eth-sig-util');
|
|
23
|
-
const { wordlist } = require('@metamask/scure-bip39/dist/wordlists/english');
|
|
24
|
-
const { keccak256 } = require('ethereum-cryptography/keccak');
|
|
25
|
-
|
|
26
|
-
const HdKeyring = require('..');
|
|
27
|
-
|
|
28
|
-
// Sample account:
|
|
29
|
-
const privKeyHex =
|
|
30
|
-
'b8a9c05beeedb25df85f8d641538cbffedf67216048de9c678ee26260eb91952';
|
|
31
|
-
|
|
32
|
-
const sampleMnemonic =
|
|
33
|
-
'finish oppose decorate face calm tragic certain desk hour urge dinosaur mango';
|
|
34
|
-
const firstAcct = '0x1c96099350f13d558464ec79b9be4445aa0ef579';
|
|
35
|
-
const secondAcct = '0x1b00aed43a693f3a957f9feb5cc08afa031e37a0';
|
|
36
|
-
|
|
37
|
-
const notKeyringAddress = '0xbD20F6F5F1616947a39E11926E78ec94817B3931';
|
|
38
|
-
|
|
39
|
-
describe('hd-keyring', () => {
|
|
40
|
-
describe('compare old bip39 implementation with new', () => {
|
|
41
|
-
it('should derive the same accounts from the same mnemonics', async () => {
|
|
42
|
-
const mnemonics = [];
|
|
43
|
-
for (let i = 0; i < 99; i++) {
|
|
44
|
-
mnemonics.push(oldMMForkBIP39.generateMnemonic());
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
await Promise.all(
|
|
48
|
-
mnemonics.map(async (mnemonic) => {
|
|
49
|
-
const newHDKeyring = new HdKeyring({ mnemonic, numberOfAccounts: 3 });
|
|
50
|
-
const oldHDKeyring = new OldHdKeyring({
|
|
51
|
-
mnemonic,
|
|
52
|
-
numberOfAccounts: 3,
|
|
53
|
-
});
|
|
54
|
-
const newAccounts = await newHDKeyring.getAccounts();
|
|
55
|
-
const oldAccounts = await oldHDKeyring.getAccounts();
|
|
56
|
-
await expect(newAccounts[0]).toStrictEqual(oldAccounts[0]);
|
|
57
|
-
|
|
58
|
-
await expect(newAccounts[1]).toStrictEqual(oldAccounts[1]);
|
|
59
|
-
|
|
60
|
-
await expect(newAccounts[2]).toStrictEqual(oldAccounts[2]);
|
|
61
|
-
}),
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('constructor', () => {
|
|
67
|
-
it('constructs with a typeof string mnemonic', async () => {
|
|
68
|
-
const keyring = new HdKeyring({
|
|
69
|
-
mnemonic: sampleMnemonic,
|
|
70
|
-
numberOfAccounts: 2,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const accounts = await keyring.getAccounts();
|
|
74
|
-
expect(accounts[0]).toStrictEqual(firstAcct);
|
|
75
|
-
expect(accounts[1]).toStrictEqual(secondAcct);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('constructs with a typeof buffer mnemonic', async () => {
|
|
79
|
-
const keyring = new HdKeyring({
|
|
80
|
-
mnemonic: Buffer.from(sampleMnemonic, 'utf8'),
|
|
81
|
-
numberOfAccounts: 2,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const accounts = await keyring.getAccounts();
|
|
85
|
-
expect(accounts[0]).toStrictEqual(firstAcct);
|
|
86
|
-
expect(accounts[1]).toStrictEqual(secondAcct);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('constructs with a typeof Uint8Array mnemonic', async () => {
|
|
90
|
-
const indices = sampleMnemonic
|
|
91
|
-
.split(' ')
|
|
92
|
-
.map((word) => wordlist.indexOf(word));
|
|
93
|
-
const uInt8ArrayOfMnemonic = new Uint8Array(
|
|
94
|
-
new Uint16Array(indices).buffer,
|
|
95
|
-
);
|
|
96
|
-
const keyring = new HdKeyring({
|
|
97
|
-
mnemonic: uInt8ArrayOfMnemonic,
|
|
98
|
-
numberOfAccounts: 2,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const accounts = await keyring.getAccounts();
|
|
102
|
-
expect(accounts[0]).toStrictEqual(firstAcct);
|
|
103
|
-
expect(accounts[1]).toStrictEqual(secondAcct);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('throws on invalid mnemonic', () => {
|
|
107
|
-
expect(
|
|
108
|
-
() =>
|
|
109
|
-
new HdKeyring({
|
|
110
|
-
mnemonic: 'abc xyz',
|
|
111
|
-
numberOfAccounts: 2,
|
|
112
|
-
}),
|
|
113
|
-
).toThrow('Eth-Hd-Keyring: Invalid secret recovery phrase provided');
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('throws when numberOfAccounts is passed with no mnemonic', () => {
|
|
117
|
-
expect(
|
|
118
|
-
() =>
|
|
119
|
-
new HdKeyring({
|
|
120
|
-
numberOfAccounts: 2,
|
|
121
|
-
}),
|
|
122
|
-
).toThrow(
|
|
123
|
-
'Eth-Hd-Keyring: Deserialize method cannot be called with an opts value for numberOfAccounts and no menmonic',
|
|
124
|
-
);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('re-initialization protection', () => {
|
|
129
|
-
const alreadyProvidedError =
|
|
130
|
-
'Eth-Hd-Keyring: Secret recovery phrase already provided';
|
|
131
|
-
it('double generateRandomMnemonic', () => {
|
|
132
|
-
const keyring = new HdKeyring();
|
|
133
|
-
keyring.generateRandomMnemonic();
|
|
134
|
-
expect(() => {
|
|
135
|
-
keyring.generateRandomMnemonic();
|
|
136
|
-
}).toThrow(alreadyProvidedError);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('constructor + generateRandomMnemonic', () => {
|
|
140
|
-
const keyring = new HdKeyring({
|
|
141
|
-
mnemonic: sampleMnemonic,
|
|
142
|
-
numberOfAccounts: 2,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
expect(() => {
|
|
146
|
-
keyring.generateRandomMnemonic();
|
|
147
|
-
}).toThrow(alreadyProvidedError);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('constructor + deserialize', () => {
|
|
151
|
-
const keyring = new HdKeyring({
|
|
152
|
-
mnemonic: sampleMnemonic,
|
|
153
|
-
numberOfAccounts: 2,
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
expect(() => {
|
|
157
|
-
keyring.deserialize({
|
|
158
|
-
mnemonic: sampleMnemonic,
|
|
159
|
-
numberOfAccounts: 1,
|
|
160
|
-
});
|
|
161
|
-
}).toThrow(alreadyProvidedError);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe('Keyring.type', () => {
|
|
166
|
-
it('is a class property that returns the type string.', () => {
|
|
167
|
-
const { type } = HdKeyring;
|
|
168
|
-
expect(typeof type).toBe('string');
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe('#type', () => {
|
|
173
|
-
it('returns the correct value', () => {
|
|
174
|
-
const keyring = new HdKeyring();
|
|
175
|
-
|
|
176
|
-
const { type } = keyring;
|
|
177
|
-
const correct = HdKeyring.type;
|
|
178
|
-
expect(type).toStrictEqual(correct);
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
describe('#serialize mnemonic.', () => {
|
|
183
|
-
it('serializes the mnemonic in the same format as previous version (an array of utf8 encoded bytes)', async () => {
|
|
184
|
-
const keyring = new HdKeyring({
|
|
185
|
-
mnemonic: sampleMnemonic,
|
|
186
|
-
});
|
|
187
|
-
// uses previous version of eth-hd-keyring to ensure backwards compatibility
|
|
188
|
-
const oldHDKeyring = new OldHdKeyring({ mnemonic: sampleMnemonic });
|
|
189
|
-
const { mnemonic: oldKeyringSerializedMnemonic } =
|
|
190
|
-
await oldHDKeyring.serialize();
|
|
191
|
-
|
|
192
|
-
const output = await keyring.serialize();
|
|
193
|
-
expect(output.mnemonic).toStrictEqual(oldKeyringSerializedMnemonic);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('serializes mnemonic passed in as a string to an array of utf8 encoded bytes', async () => {
|
|
197
|
-
const keyring = new HdKeyring({
|
|
198
|
-
mnemonic: sampleMnemonic,
|
|
199
|
-
});
|
|
200
|
-
const output = await keyring.serialize();
|
|
201
|
-
// this Buffer.from(...).toString() is the method of converting from an array of utf8 encoded bytes back to a string
|
|
202
|
-
const mnemonicAsString = Buffer.from(output.mnemonic).toString();
|
|
203
|
-
expect(mnemonicAsString).toStrictEqual(sampleMnemonic);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('serializes mnemonic passed in as a an array of utf8 encoded bytes in the same format', async () => {
|
|
207
|
-
const uint8Array = new TextEncoder('utf-8').encode(sampleMnemonic);
|
|
208
|
-
const mnemonicAsArrayOfUtf8EncodedBytes = Array.from(uint8Array);
|
|
209
|
-
const keyring = new HdKeyring({
|
|
210
|
-
mnemonic: mnemonicAsArrayOfUtf8EncodedBytes,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
const output = await keyring.serialize();
|
|
214
|
-
// this Buffer.from(...).toString() is the method of converting from an array of utf8 encoded bytes back to a string
|
|
215
|
-
const mnemonicAsString = Buffer.from(output.mnemonic).toString();
|
|
216
|
-
expect(mnemonicAsString).toStrictEqual(sampleMnemonic);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
describe('#deserialize a private key', () => {
|
|
221
|
-
it('serializes what it deserializes', async () => {
|
|
222
|
-
const keyring = new HdKeyring();
|
|
223
|
-
await keyring.deserialize({
|
|
224
|
-
mnemonic: sampleMnemonic,
|
|
225
|
-
numberOfAccounts: 1,
|
|
226
|
-
});
|
|
227
|
-
const accountsFirstCheck = await keyring.getAccounts();
|
|
228
|
-
|
|
229
|
-
expect(accountsFirstCheck).toHaveLength(1);
|
|
230
|
-
await keyring.addAccounts(1);
|
|
231
|
-
const accountsSecondCheck = await keyring.getAccounts();
|
|
232
|
-
expect(accountsSecondCheck[0]).toStrictEqual(firstAcct);
|
|
233
|
-
expect(accountsSecondCheck[1]).toStrictEqual(secondAcct);
|
|
234
|
-
expect(accountsSecondCheck).toHaveLength(2);
|
|
235
|
-
const serialized = await keyring.serialize();
|
|
236
|
-
expect(Buffer.from(serialized.mnemonic).toString()).toStrictEqual(
|
|
237
|
-
sampleMnemonic,
|
|
238
|
-
);
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
describe('#addAccounts', () => {
|
|
243
|
-
describe('with no arguments', () => {
|
|
244
|
-
it('creates a single wallet', async () => {
|
|
245
|
-
const keyring = new HdKeyring();
|
|
246
|
-
keyring.generateRandomMnemonic();
|
|
247
|
-
await keyring.addAccounts();
|
|
248
|
-
const accounts = await keyring.getAccounts();
|
|
249
|
-
expect(accounts).toHaveLength(1);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it('throws an error when no SRP has been generated yet', async () => {
|
|
253
|
-
const keyring = new HdKeyring();
|
|
254
|
-
expect(() => keyring.addAccounts()).toThrow(
|
|
255
|
-
'Eth-Hd-Keyring: No secret recovery phrase provided',
|
|
256
|
-
);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe('with a numeric argument', () => {
|
|
261
|
-
it('creates that number of wallets', async () => {
|
|
262
|
-
const keyring = new HdKeyring();
|
|
263
|
-
keyring.generateRandomMnemonic();
|
|
264
|
-
await keyring.addAccounts(3);
|
|
265
|
-
const accounts = await keyring.getAccounts();
|
|
266
|
-
expect(accounts).toHaveLength(3);
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
describe('#signPersonalMessage', () => {
|
|
272
|
-
it('returns the expected value', async () => {
|
|
273
|
-
const keyring = new HdKeyring();
|
|
274
|
-
|
|
275
|
-
const address = firstAcct;
|
|
276
|
-
const message = '0x68656c6c6f20776f726c64';
|
|
277
|
-
|
|
278
|
-
await keyring.deserialize({
|
|
279
|
-
mnemonic: sampleMnemonic,
|
|
280
|
-
numberOfAccounts: 1,
|
|
281
|
-
});
|
|
282
|
-
const signature = await keyring.signPersonalMessage(address, message);
|
|
283
|
-
expect(signature).not.toBe(message);
|
|
284
|
-
|
|
285
|
-
const restored = recoverPersonalSignature({
|
|
286
|
-
data: message,
|
|
287
|
-
signature,
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
expect(restored).toStrictEqual(normalize(address));
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
describe('#signTypedData', () => {
|
|
295
|
-
it('can recover a basic signature', async () => {
|
|
296
|
-
const keyring = new HdKeyring();
|
|
297
|
-
Buffer.from(privKeyHex, 'hex');
|
|
298
|
-
const typedData = [
|
|
299
|
-
{
|
|
300
|
-
type: 'string',
|
|
301
|
-
name: 'message',
|
|
302
|
-
value: 'Hi, Alice!',
|
|
303
|
-
},
|
|
304
|
-
];
|
|
305
|
-
keyring.generateRandomMnemonic();
|
|
306
|
-
await keyring.addAccounts(1);
|
|
307
|
-
const addresses = await keyring.getAccounts();
|
|
308
|
-
const address = addresses[0];
|
|
309
|
-
const signature = await keyring.signTypedData(address, typedData);
|
|
310
|
-
const restored = recoverTypedSignature({
|
|
311
|
-
data: typedData,
|
|
312
|
-
signature,
|
|
313
|
-
version: SignTypedDataVersion.V1,
|
|
314
|
-
});
|
|
315
|
-
expect(restored).toStrictEqual(address);
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
describe('#signTypedData_v1', () => {
|
|
320
|
-
const typedData = [
|
|
321
|
-
{
|
|
322
|
-
type: 'string',
|
|
323
|
-
name: 'message',
|
|
324
|
-
value: 'Hi, Alice!',
|
|
325
|
-
},
|
|
326
|
-
];
|
|
327
|
-
|
|
328
|
-
it('signs in a compliant and recoverable way', async () => {
|
|
329
|
-
const keyring = new HdKeyring();
|
|
330
|
-
keyring.generateRandomMnemonic();
|
|
331
|
-
await keyring.addAccounts(1);
|
|
332
|
-
const addresses = await keyring.getAccounts();
|
|
333
|
-
const address = addresses[0];
|
|
334
|
-
const signature = await keyring.signTypedData(address, typedData, {
|
|
335
|
-
version: SignTypedDataVersion.V1,
|
|
336
|
-
});
|
|
337
|
-
const restored = recoverTypedSignature({
|
|
338
|
-
data: typedData,
|
|
339
|
-
signature,
|
|
340
|
-
version: SignTypedDataVersion.V1,
|
|
341
|
-
});
|
|
342
|
-
expect(restored).toStrictEqual(address);
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
describe('#signTypedData_v3', () => {
|
|
347
|
-
it('signs in a compliant and recoverable way', async () => {
|
|
348
|
-
const keyring = new HdKeyring();
|
|
349
|
-
const typedData = {
|
|
350
|
-
types: {
|
|
351
|
-
EIP712Domain: [],
|
|
352
|
-
},
|
|
353
|
-
domain: {},
|
|
354
|
-
primaryType: 'EIP712Domain',
|
|
355
|
-
message: {},
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
await keyring.deserialize({
|
|
359
|
-
mnemonic: sampleMnemonic,
|
|
360
|
-
numberOfAccounts: 1,
|
|
361
|
-
});
|
|
362
|
-
const addresses = await keyring.getAccounts();
|
|
363
|
-
const address = addresses[0];
|
|
364
|
-
const signature = await keyring.signTypedData(address, typedData, {
|
|
365
|
-
version: SignTypedDataVersion.V3,
|
|
366
|
-
});
|
|
367
|
-
const restored = recoverTypedSignature({
|
|
368
|
-
data: typedData,
|
|
369
|
-
signature,
|
|
370
|
-
version: SignTypedDataVersion.V3,
|
|
371
|
-
});
|
|
372
|
-
expect(restored).toStrictEqual(address);
|
|
373
|
-
});
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
describe('#signTypedData_v3 signature verification', () => {
|
|
377
|
-
it('signs in a recoverable way.', async () => {
|
|
378
|
-
const keyring = new HdKeyring();
|
|
379
|
-
const typedData = {
|
|
380
|
-
types: {
|
|
381
|
-
EIP712Domain: [
|
|
382
|
-
{ name: 'name', type: 'string' },
|
|
383
|
-
{ name: 'version', type: 'string' },
|
|
384
|
-
{ name: 'chainId', type: 'uint256' },
|
|
385
|
-
{ name: 'verifyingContract', type: 'address' },
|
|
386
|
-
],
|
|
387
|
-
Person: [
|
|
388
|
-
{ name: 'name', type: 'string' },
|
|
389
|
-
{ name: 'wallet', type: 'address' },
|
|
390
|
-
],
|
|
391
|
-
Mail: [
|
|
392
|
-
{ name: 'from', type: 'Person' },
|
|
393
|
-
{ name: 'to', type: 'Person' },
|
|
394
|
-
{ name: 'contents', type: 'string' },
|
|
395
|
-
],
|
|
396
|
-
},
|
|
397
|
-
primaryType: 'Mail',
|
|
398
|
-
domain: {
|
|
399
|
-
name: 'Ether Mail',
|
|
400
|
-
version: '1',
|
|
401
|
-
chainId: 1,
|
|
402
|
-
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
|
|
403
|
-
},
|
|
404
|
-
message: {
|
|
405
|
-
from: {
|
|
406
|
-
name: 'Cow',
|
|
407
|
-
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
|
|
408
|
-
},
|
|
409
|
-
to: {
|
|
410
|
-
name: 'Bob',
|
|
411
|
-
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
|
|
412
|
-
},
|
|
413
|
-
contents: 'Hello, Bob!',
|
|
414
|
-
},
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
keyring.generateRandomMnemonic();
|
|
418
|
-
await keyring.addAccounts(1);
|
|
419
|
-
const addresses = await keyring.getAccounts();
|
|
420
|
-
const address = addresses[0];
|
|
421
|
-
const signature = await keyring.signTypedData(address, typedData, {
|
|
422
|
-
version: SignTypedDataVersion.V3,
|
|
423
|
-
});
|
|
424
|
-
const restored = recoverTypedSignature({
|
|
425
|
-
data: typedData,
|
|
426
|
-
signature,
|
|
427
|
-
version: SignTypedDataVersion.V3,
|
|
428
|
-
});
|
|
429
|
-
expect(restored).toStrictEqual(address);
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
describe('custom hd paths', () => {
|
|
434
|
-
it('can deserialize with an hdPath param and generate the same accounts.', async () => {
|
|
435
|
-
const keyring = new HdKeyring();
|
|
436
|
-
const hdPathString = `m/44'/60'/0'/0`;
|
|
437
|
-
keyring.deserialize({
|
|
438
|
-
mnemonic: sampleMnemonic,
|
|
439
|
-
numberOfAccounts: 1,
|
|
440
|
-
hdPath: hdPathString,
|
|
441
|
-
});
|
|
442
|
-
const addresses = await keyring.getAccounts();
|
|
443
|
-
expect(addresses[0]).toStrictEqual(firstAcct);
|
|
444
|
-
const serialized = await keyring.serialize();
|
|
445
|
-
expect(serialized.hdPath).toStrictEqual(hdPathString);
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
it('can deserialize with an hdPath param and generate different accounts.', async () => {
|
|
449
|
-
const keyring = new HdKeyring();
|
|
450
|
-
const hdPathString = `m/44'/60'/0'/1`;
|
|
451
|
-
keyring.deserialize({
|
|
452
|
-
mnemonic: sampleMnemonic,
|
|
453
|
-
numberOfAccounts: 1,
|
|
454
|
-
hdPath: hdPathString,
|
|
455
|
-
});
|
|
456
|
-
const addresses = await keyring.getAccounts();
|
|
457
|
-
expect(addresses[0]).not.toBe(firstAcct);
|
|
458
|
-
const serialized = await keyring.serialize();
|
|
459
|
-
expect(serialized.hdPath).toStrictEqual(hdPathString);
|
|
460
|
-
});
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
// eslint-disable-next-line
|
|
464
|
-
/*
|
|
465
|
-
describe('create and restore 1k accounts', function () {
|
|
466
|
-
it('should restore same accounts with no problem', async function () {
|
|
467
|
-
this.timeout(20000)
|
|
468
|
-
|
|
469
|
-
for (let i = 0; i < 1e3; i++) {
|
|
470
|
-
|
|
471
|
-
const keyring = new HdKeyring({
|
|
472
|
-
numberOfAccounts: 1,
|
|
473
|
-
})
|
|
474
|
-
const originalAccounts = await keyring.getAccounts()
|
|
475
|
-
const serialized = await keyring.serialize()
|
|
476
|
-
const mnemonic = serialized.mnemonic
|
|
477
|
-
|
|
478
|
-
const keyring = new HdKeyring({
|
|
479
|
-
numberOfAccounts: 1,
|
|
480
|
-
mnemonic,
|
|
481
|
-
})
|
|
482
|
-
const restoredAccounts = await keyring.getAccounts()
|
|
483
|
-
|
|
484
|
-
const first = originalAccounts[0]
|
|
485
|
-
const restored = restoredAccounts[0]
|
|
486
|
-
const msg = `Should restore same account from mnemonic: "${mnemonic}"`
|
|
487
|
-
assert.equal(restoredAccounts[0], originalAccounts[0], msg)
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
return true
|
|
492
|
-
})
|
|
493
|
-
})
|
|
494
|
-
*/
|
|
495
|
-
|
|
496
|
-
describe('signing methods withAppKeyOrigin option', () => {
|
|
497
|
-
it('should signPersonalMessage with the expected key when passed a withAppKeyOrigin', async () => {
|
|
498
|
-
const keyring = new HdKeyring();
|
|
499
|
-
const address = firstAcct;
|
|
500
|
-
const message = '0x68656c6c6f20776f726c64';
|
|
501
|
-
|
|
502
|
-
const privateKey = Buffer.from(
|
|
503
|
-
'8e82d2d74c50e5c8460f771d38a560ebe1151a9134c65a7e92b28ad0cfae7151',
|
|
504
|
-
'hex',
|
|
505
|
-
);
|
|
506
|
-
const expectedSig = personalSign({ privateKey, data: message });
|
|
507
|
-
|
|
508
|
-
await keyring.deserialize({
|
|
509
|
-
mnemonic: sampleMnemonic,
|
|
510
|
-
numberOfAccounts: 1,
|
|
511
|
-
});
|
|
512
|
-
const sig = await keyring.signPersonalMessage(address, message, {
|
|
513
|
-
withAppKeyOrigin: 'someapp.origin.io',
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
expect(sig).toStrictEqual(expectedSig);
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
it('should signTypedData with the expected key when passed a withAppKeyOrigin', async () => {
|
|
520
|
-
const keyring = new HdKeyring();
|
|
521
|
-
const address = firstAcct;
|
|
522
|
-
const typedData = {
|
|
523
|
-
types: {
|
|
524
|
-
EIP712Domain: [],
|
|
525
|
-
},
|
|
526
|
-
domain: {},
|
|
527
|
-
primaryType: 'EIP712Domain',
|
|
528
|
-
message: {},
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
const privateKey = Buffer.from(
|
|
532
|
-
'8e82d2d74c50e5c8460f771d38a560ebe1151a9134c65a7e92b28ad0cfae7151',
|
|
533
|
-
'hex',
|
|
534
|
-
);
|
|
535
|
-
const expectedSig = signTypedData({
|
|
536
|
-
privateKey,
|
|
537
|
-
data: typedData,
|
|
538
|
-
version: SignTypedDataVersion.V3,
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
await keyring.deserialize({
|
|
542
|
-
mnemonic: sampleMnemonic,
|
|
543
|
-
numberOfAccounts: 1,
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
const sig = await keyring.signTypedData(address, typedData, {
|
|
547
|
-
withAppKeyOrigin: 'someapp.origin.io',
|
|
548
|
-
version: SignTypedDataVersion.V3,
|
|
549
|
-
});
|
|
550
|
-
expect(sig).toStrictEqual(expectedSig);
|
|
551
|
-
});
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
// /
|
|
555
|
-
/* TESTS FOR BASE-KEYRING METHODS */
|
|
556
|
-
// /
|
|
557
|
-
|
|
558
|
-
describe('#signMessage', function () {
|
|
559
|
-
const message =
|
|
560
|
-
'0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0';
|
|
561
|
-
const expectedResult =
|
|
562
|
-
'0xb21867b2221db0172e970b7370825b71c57823ff8714168ce9748f32f450e2c43d0fe396eb5b5f59284b7fd108c8cf61a6180a6756bdd3d4b7b9ccc4ac6d51611b';
|
|
563
|
-
|
|
564
|
-
it('passes the dennis test', async function () {
|
|
565
|
-
const keyring = new HdKeyring();
|
|
566
|
-
await keyring.deserialize({
|
|
567
|
-
mnemonic: sampleMnemonic,
|
|
568
|
-
numberOfAccounts: 1,
|
|
569
|
-
});
|
|
570
|
-
const result = await keyring.signMessage(firstAcct, message);
|
|
571
|
-
expect(result).toBe(expectedResult);
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
it('reliably can decode messages it signs', async function () {
|
|
575
|
-
const keyring = new HdKeyring();
|
|
576
|
-
await keyring.deserialize({
|
|
577
|
-
mnemonic: sampleMnemonic,
|
|
578
|
-
numberOfAccounts: 1,
|
|
579
|
-
});
|
|
580
|
-
const localMessage = 'hello there!';
|
|
581
|
-
const msgHashHex = bufferToHex(
|
|
582
|
-
Buffer.from(keccak256(Buffer.from(localMessage))),
|
|
583
|
-
);
|
|
584
|
-
await keyring.addAccounts(9);
|
|
585
|
-
const addresses = await keyring.getAccounts();
|
|
586
|
-
const signatures = await Promise.all(
|
|
587
|
-
addresses.map(async (accountAddress) => {
|
|
588
|
-
return await keyring.signMessage(accountAddress, msgHashHex);
|
|
589
|
-
}),
|
|
590
|
-
);
|
|
591
|
-
signatures.forEach((sgn, index) => {
|
|
592
|
-
const accountAddress = addresses[index];
|
|
593
|
-
|
|
594
|
-
const r = toBuffer(sgn.slice(0, 66));
|
|
595
|
-
const s = toBuffer(`0x${sgn.slice(66, 130)}`);
|
|
596
|
-
const v = BigInt(`0x${sgn.slice(130, 132)}`);
|
|
597
|
-
const m = toBuffer(msgHashHex);
|
|
598
|
-
const pub = ecrecover(m, v, r, s);
|
|
599
|
-
const adr = `0x${pubToAddress(pub).toString('hex')}`;
|
|
600
|
-
|
|
601
|
-
expect(adr).toBe(accountAddress);
|
|
602
|
-
});
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
it('throw error for invalid message', async function () {
|
|
606
|
-
const keyring = new HdKeyring();
|
|
607
|
-
await keyring.deserialize({
|
|
608
|
-
mnemonic: sampleMnemonic,
|
|
609
|
-
numberOfAccounts: 1,
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
await expect(keyring.signMessage(firstAcct, '')).rejects.toThrow(
|
|
613
|
-
'Value must be a hexadecimal string',
|
|
614
|
-
);
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
it('throw error if empty address is passed', async function () {
|
|
618
|
-
const keyring = new HdKeyring();
|
|
619
|
-
await keyring.deserialize({
|
|
620
|
-
mnemonic: sampleMnemonic,
|
|
621
|
-
numberOfAccounts: 1,
|
|
622
|
-
});
|
|
623
|
-
|
|
624
|
-
await expect(keyring.signMessage('', message)).rejects.toThrow(
|
|
625
|
-
'Must specify address.',
|
|
626
|
-
);
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
it('throw error if address not associated with the current keyring is passed', async function () {
|
|
630
|
-
const keyring = new HdKeyring();
|
|
631
|
-
await keyring.deserialize({
|
|
632
|
-
mnemonic: sampleMnemonic,
|
|
633
|
-
numberOfAccounts: 1,
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
await expect(
|
|
637
|
-
keyring.signMessage(notKeyringAddress, message),
|
|
638
|
-
).rejects.toThrow('HD Keyring - Unable to find matching address.');
|
|
639
|
-
});
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
describe('#removeAccount', function () {
|
|
643
|
-
let keyring;
|
|
644
|
-
beforeEach(() => {
|
|
645
|
-
keyring = new HdKeyring({
|
|
646
|
-
mnemonic: sampleMnemonic,
|
|
647
|
-
numberOfAccounts: 1,
|
|
648
|
-
});
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
describe('if the account exists', function () {
|
|
652
|
-
it('should remove that account', async function () {
|
|
653
|
-
const addresses = await keyring.getAccounts();
|
|
654
|
-
expect(addresses).toHaveLength(1);
|
|
655
|
-
keyring.removeAccount(addresses[0]);
|
|
656
|
-
const addressesAfterRemoval = await keyring.getAccounts();
|
|
657
|
-
expect(addressesAfterRemoval).toHaveLength(0);
|
|
658
|
-
});
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
describe('if the account does not exist', function () {
|
|
662
|
-
it('should throw an error', function () {
|
|
663
|
-
const unexistingAccount = '0x0000000000000000000000000000000000000000';
|
|
664
|
-
expect(() => keyring.removeAccount(unexistingAccount)).toThrow(
|
|
665
|
-
`Address ${unexistingAccount} not found in this keyring`,
|
|
666
|
-
);
|
|
667
|
-
});
|
|
668
|
-
});
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
describe('getAppKeyAddress', function () {
|
|
672
|
-
let keyring;
|
|
673
|
-
beforeEach(() => {
|
|
674
|
-
keyring = new HdKeyring({
|
|
675
|
-
mnemonic: sampleMnemonic,
|
|
676
|
-
numberOfAccounts: 1,
|
|
677
|
-
});
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
it('should return a public address custom to the provided app key origin', async function () {
|
|
681
|
-
const appKeyAddress = await keyring.getAppKeyAddress(
|
|
682
|
-
firstAcct,
|
|
683
|
-
'someapp.origin.io',
|
|
684
|
-
);
|
|
685
|
-
|
|
686
|
-
expect(firstAcct).not.toBe(appKeyAddress);
|
|
687
|
-
expect(isValidAddress(appKeyAddress)).toBe(true);
|
|
688
|
-
});
|
|
689
|
-
|
|
690
|
-
it('should return different addresses when provided different app key origins', async function () {
|
|
691
|
-
const appKeyAddress1 = await keyring.getAppKeyAddress(
|
|
692
|
-
firstAcct,
|
|
693
|
-
'someapp.origin.io',
|
|
694
|
-
);
|
|
695
|
-
|
|
696
|
-
expect(isValidAddress(appKeyAddress1)).toBe(true);
|
|
697
|
-
|
|
698
|
-
const appKeyAddress2 = await keyring.getAppKeyAddress(
|
|
699
|
-
firstAcct,
|
|
700
|
-
'anotherapp.origin.io',
|
|
701
|
-
);
|
|
702
|
-
|
|
703
|
-
expect(isValidAddress(appKeyAddress2)).toBe(true);
|
|
704
|
-
expect(appKeyAddress1).not.toBe(appKeyAddress2);
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
it('should return the same address when called multiple times with the same params', async function () {
|
|
708
|
-
const appKeyAddress1 = await keyring.getAppKeyAddress(
|
|
709
|
-
firstAcct,
|
|
710
|
-
'someapp.origin.io',
|
|
711
|
-
);
|
|
712
|
-
|
|
713
|
-
expect(isValidAddress(appKeyAddress1)).toBe(true);
|
|
714
|
-
|
|
715
|
-
const appKeyAddress2 = await keyring.getAppKeyAddress(
|
|
716
|
-
firstAcct,
|
|
717
|
-
'someapp.origin.io',
|
|
718
|
-
);
|
|
719
|
-
|
|
720
|
-
expect(isValidAddress(appKeyAddress2)).toBe(true);
|
|
721
|
-
expect(appKeyAddress1).toBe(appKeyAddress2);
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
it('should throw error if the provided origin is not a string', async function () {
|
|
725
|
-
await expect(keyring.getAppKeyAddress(firstAcct, [])).rejects.toThrow(
|
|
726
|
-
`'origin' must be a non-empty string`,
|
|
727
|
-
);
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
it('should throw error if the provided origin is an empty string', async function () {
|
|
731
|
-
await expect(keyring.getAppKeyAddress(firstAcct, '')).rejects.toThrow(
|
|
732
|
-
`'origin' must be a non-empty string`,
|
|
733
|
-
);
|
|
734
|
-
});
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
describe('exportAccount', function () {
|
|
738
|
-
let keyring;
|
|
739
|
-
beforeEach(() => {
|
|
740
|
-
keyring = new HdKeyring({
|
|
741
|
-
mnemonic: sampleMnemonic,
|
|
742
|
-
numberOfAccounts: 1,
|
|
743
|
-
});
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
it('should return a hex-encoded private key', async function () {
|
|
747
|
-
const expectedPrivateKeyResult =
|
|
748
|
-
'0xd3cc16948a02a91b9fcf83735653bf3dfd82c86543fdd1e9a828bd25e8a7b68d';
|
|
749
|
-
const privKeyHexValue = await keyring.exportAccount(firstAcct);
|
|
750
|
-
|
|
751
|
-
expect(expectedPrivateKeyResult).toBe(`0x${privKeyHexValue}`);
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
it('throw error if account is not present', async function () {
|
|
755
|
-
await expect(keyring.exportAccount(notKeyringAddress)).rejects.toThrow(
|
|
756
|
-
'HD Keyring - Unable to find matching address.',
|
|
757
|
-
);
|
|
758
|
-
});
|
|
759
|
-
});
|
|
760
|
-
|
|
761
|
-
describe('#encryptionPublicKey', function () {
|
|
762
|
-
const publicKey = 'LV7lWhd0mUDcvxkMU2o6uKXftu25zq4bMYdmMqppXic=';
|
|
763
|
-
let keyring;
|
|
764
|
-
beforeEach(() => {
|
|
765
|
-
keyring = new HdKeyring({
|
|
766
|
-
mnemonic: sampleMnemonic,
|
|
767
|
-
numberOfAccounts: 1,
|
|
768
|
-
});
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
it('returns the expected value', async function () {
|
|
772
|
-
const encryptionPublicKey = await keyring.getEncryptionPublicKey(
|
|
773
|
-
firstAcct,
|
|
774
|
-
);
|
|
775
|
-
expect(publicKey).toBe(encryptionPublicKey);
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
it('throw error if address is blank', async function () {
|
|
779
|
-
await expect(keyring.getEncryptionPublicKey('')).rejects.toThrow(
|
|
780
|
-
'Must specify address.',
|
|
781
|
-
);
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
it('throw error if address is not present in the keyring', async function () {
|
|
785
|
-
await expect(
|
|
786
|
-
keyring.getEncryptionPublicKey(notKeyringAddress),
|
|
787
|
-
).rejects.toThrow('HD Keyring - Unable to find matching address.');
|
|
788
|
-
});
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
describe('#signTypedData V4 signature verification', function () {
|
|
792
|
-
let keyring;
|
|
793
|
-
beforeEach(() => {
|
|
794
|
-
keyring = new HdKeyring({
|
|
795
|
-
mnemonic: sampleMnemonic,
|
|
796
|
-
numberOfAccounts: 1,
|
|
797
|
-
});
|
|
798
|
-
});
|
|
799
|
-
|
|
800
|
-
const expectedSignature =
|
|
801
|
-
'0x220917664ef676d592bd709a5bffedaf69c5f6c72f13c6c4547a41d211f0923c3180893b1dec023433f11b664fabda22b74b57d21094f7798fc85b7650f8edbb1b';
|
|
802
|
-
|
|
803
|
-
it('returns the expected value', async function () {
|
|
804
|
-
const typedData = {
|
|
805
|
-
types: {
|
|
806
|
-
EIP712Domain: [
|
|
807
|
-
{ name: 'name', type: 'string' },
|
|
808
|
-
{ name: 'version', type: 'string' },
|
|
809
|
-
{ name: 'chainId', type: 'uint256' },
|
|
810
|
-
{ name: 'verifyingContract', type: 'address' },
|
|
811
|
-
],
|
|
812
|
-
Person: [
|
|
813
|
-
{ name: 'name', type: 'string' },
|
|
814
|
-
{ name: 'wallets', type: 'address[]' },
|
|
815
|
-
],
|
|
816
|
-
Mail: [
|
|
817
|
-
{ name: 'from', type: 'Person' },
|
|
818
|
-
{ name: 'to', type: 'Person[]' },
|
|
819
|
-
{ name: 'contents', type: 'string' },
|
|
820
|
-
],
|
|
821
|
-
Group: [
|
|
822
|
-
{ name: 'name', type: 'string' },
|
|
823
|
-
{ name: 'members', type: 'Person[]' },
|
|
824
|
-
],
|
|
825
|
-
},
|
|
826
|
-
domain: {
|
|
827
|
-
name: 'Ether Mail',
|
|
828
|
-
version: '1',
|
|
829
|
-
chainId: 1,
|
|
830
|
-
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
|
|
831
|
-
},
|
|
832
|
-
primaryType: 'Mail',
|
|
833
|
-
message: {
|
|
834
|
-
from: {
|
|
835
|
-
name: 'Cow',
|
|
836
|
-
wallets: [
|
|
837
|
-
'0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
|
|
838
|
-
'0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF',
|
|
839
|
-
],
|
|
840
|
-
},
|
|
841
|
-
to: [
|
|
842
|
-
{
|
|
843
|
-
name: 'Bob',
|
|
844
|
-
wallets: [
|
|
845
|
-
'0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
|
|
846
|
-
'0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
|
|
847
|
-
'0xB0B0b0b0b0b0B000000000000000000000000000',
|
|
848
|
-
],
|
|
849
|
-
},
|
|
850
|
-
],
|
|
851
|
-
contents: 'Hello, Bob!',
|
|
852
|
-
},
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
const addresses = await keyring.getAccounts();
|
|
856
|
-
const [address] = addresses;
|
|
857
|
-
|
|
858
|
-
const signature = await keyring.signTypedData(address, typedData, {
|
|
859
|
-
version: 'V4',
|
|
860
|
-
});
|
|
861
|
-
expect(signature).toBe(expectedSignature);
|
|
862
|
-
const restored = recoverTypedSignature({
|
|
863
|
-
data: typedData,
|
|
864
|
-
signature,
|
|
865
|
-
version: SignTypedDataVersion.V4,
|
|
866
|
-
});
|
|
867
|
-
expect(restored).toBe(address);
|
|
868
|
-
});
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
describe('#decryptMessage', function () {
|
|
872
|
-
const message = 'Hello world!';
|
|
873
|
-
let encryptedMessage, keyring;
|
|
874
|
-
|
|
875
|
-
beforeEach(async () => {
|
|
876
|
-
keyring = new HdKeyring({
|
|
877
|
-
mnemonic: sampleMnemonic,
|
|
878
|
-
numberOfAccounts: 1,
|
|
879
|
-
});
|
|
880
|
-
|
|
881
|
-
const encryptionPublicKey = await keyring.getEncryptionPublicKey(
|
|
882
|
-
firstAcct,
|
|
883
|
-
);
|
|
884
|
-
encryptedMessage = encrypt({
|
|
885
|
-
publicKey: encryptionPublicKey,
|
|
886
|
-
data: message,
|
|
887
|
-
version: 'x25519-xsalsa20-poly1305',
|
|
888
|
-
});
|
|
889
|
-
});
|
|
890
|
-
|
|
891
|
-
it('returns the expected value', async function () {
|
|
892
|
-
const decryptedMessage = await keyring.decryptMessage(
|
|
893
|
-
firstAcct,
|
|
894
|
-
encryptedMessage,
|
|
895
|
-
);
|
|
896
|
-
expect(message).toBe(decryptedMessage);
|
|
897
|
-
});
|
|
898
|
-
|
|
899
|
-
it('throw error if address passed is not present in the keyring', async function () {
|
|
900
|
-
await expect(
|
|
901
|
-
keyring.decryptMessage(notKeyringAddress, encryptedMessage),
|
|
902
|
-
).rejects.toThrow('HD Keyring - Unable to find matching address.');
|
|
903
|
-
});
|
|
904
|
-
|
|
905
|
-
it('throw error if wrong encrypted data object is passed', async function () {
|
|
906
|
-
await expect(keyring.decryptMessage(firstAcct, {})).rejects.toThrow(
|
|
907
|
-
'Encryption type/version not supported.',
|
|
908
|
-
);
|
|
909
|
-
});
|
|
910
|
-
});
|
|
911
|
-
|
|
912
|
-
describe('#signTransaction', function () {
|
|
913
|
-
let keyring;
|
|
914
|
-
beforeEach(() => {
|
|
915
|
-
keyring = new HdKeyring({
|
|
916
|
-
mnemonic: sampleMnemonic,
|
|
917
|
-
numberOfAccounts: 1,
|
|
918
|
-
});
|
|
919
|
-
});
|
|
920
|
-
|
|
921
|
-
const txParams = {
|
|
922
|
-
from: firstAcct,
|
|
923
|
-
nonce: '0x00',
|
|
924
|
-
gasPrice: '0x09184e72a000',
|
|
925
|
-
gasLimit: '0x2710',
|
|
926
|
-
to: firstAcct,
|
|
927
|
-
value: '0x1000',
|
|
928
|
-
};
|
|
929
|
-
|
|
930
|
-
it('returns a signed legacy tx object', async function () {
|
|
931
|
-
const tx = new EthereumTx(txParams);
|
|
932
|
-
expect(tx.isSigned()).toBe(false);
|
|
933
|
-
|
|
934
|
-
const signed = await keyring.signTransaction(firstAcct, tx);
|
|
935
|
-
expect(signed.isSigned()).toBe(true);
|
|
936
|
-
});
|
|
937
|
-
|
|
938
|
-
it('returns a signed tx object', async function () {
|
|
939
|
-
const tx = TransactionFactory.fromTxData(txParams);
|
|
940
|
-
expect(tx.isSigned()).toBe(false);
|
|
941
|
-
|
|
942
|
-
const signed = await keyring.signTransaction(firstAcct, tx);
|
|
943
|
-
expect(signed.isSigned()).toBe(true);
|
|
944
|
-
});
|
|
945
|
-
|
|
946
|
-
it('returns rejected promise if empty address is passed', async function () {
|
|
947
|
-
const tx = TransactionFactory.fromTxData(txParams);
|
|
948
|
-
await expect(keyring.signTransaction('', tx)).rejects.toThrow(
|
|
949
|
-
'Must specify address.',
|
|
950
|
-
);
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
it('throw error if wrong address is passed', async function () {
|
|
954
|
-
const tx = TransactionFactory.fromTxData(txParams);
|
|
955
|
-
await expect(
|
|
956
|
-
keyring.signTransaction(notKeyringAddress, tx),
|
|
957
|
-
).rejects.toThrow('HD Keyring - Unable to find matching address.');
|
|
958
|
-
});
|
|
959
|
-
});
|
|
960
|
-
});
|