Skip to content

Commit 9d35cc6

Browse files
authored
Merge pull request #9 from SpringRole/RS-5896
Rs 5896 Refactor SpringWallet
2 parents e6fc25b + 5a8891a commit 9d35cc6

File tree

9 files changed

+814
-1717
lines changed

9 files changed

+814
-1717
lines changed

.eslintrc.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
{
2-
"parser": "babel-eslint",
32
"env": {
43
"browser": true,
5-
"commonjs": true,
64
"es6": true,
75
"node": true
86
},
9-
"extends": ["standard", "prettier", "prettier/standard"],
7+
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
108
"parserOptions": {
11-
"ecmaVersion": 2017,
9+
"ecmaVersion": 2018,
1210
"sourceType": "module"
1311
},
14-
"plugins": ["prettier", "standard"],
12+
"plugins": ["prettier"],
1513
"rules": {
1614
"no-useless-constructor": 0,
1715
"no-new": 0,

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
src/
33
.babelrc
44
.editorconfig
5+
.eslintcache
56
.eslintignore
67
.eslintrc.json
78
.prettierrc

.prettierrc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
2-
"printWidth": 120,
3-
"tabWidth": 4,
4-
"useTabs": false,
5-
"semi": true,
6-
"singleQuote": true,
7-
"trailingComma": "none",
8-
"bracketSpacing": false,
9-
"arrowParens": "always",
10-
"proseWrap": "never"
2+
"printWidth": 120,
3+
"tabWidth": 4,
4+
"useTabs": false,
5+
"semi": true,
6+
"singleQuote": true,
7+
"trailingComma": "none",
8+
"arrowParens": "always",
9+
"proseWrap": "never",
10+
"endOfLine": "lf"
1111
}

package.json

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,12 @@
22
"name": "@springrole/springwallet",
33
"version": "0.1.7-beta.7",
44
"description": "Wallet for SpringRole users",
5-
"main": "dist/SpringWallet.node.js",
6-
"browser": "dist/SpringWallet.umd.js",
7-
"browserslist": [
8-
"> 1%",
9-
"node 8",
10-
"not dead"
11-
],
12-
"engines": {
13-
"node": ">=8.0.0"
14-
},
5+
"main": "dist/index.js",
156
"scripts": {
16-
"dev": "webpack --mode development",
17-
"build": "npm run build:node && npm run build:webpack",
18-
"build:node": "babel src --out-dir lib",
19-
"build:webpack": "webpack --mode production",
20-
"lint": "eslint --cache --fix .",
21-
"format": "prettier --config ./.prettierrc --write ./src/{*.js,**/*.js}"
7+
"build": "./node_modules/.bin/babel src --out-dir dist",
8+
"lint": "./node_modules/.bin/eslint .",
9+
"lint:fix": "./node_modules/.bin/eslint --fix .",
10+
"format": "./node_modules/.bin/prettier --ignore-path .gitignore --write ./{*.json,**/*.json}"
2211
},
2312
"repository": {
2413
"type": "git",
@@ -50,7 +39,6 @@
5039
"dependencies": {
5140
"bip39": "^3.0.2",
5241
"ethereumjs-wallet": "^0.6.3",
53-
"sweetalert2": "^8.15.2",
5442
"web3-provider-engine": "^15.0.3"
5543
},
5644
"devDependencies": {
@@ -61,18 +49,26 @@
6149
"@babel/plugin-transform-runtime": "^7.6.0",
6250
"@babel/preset-env": "^7.6.0",
6351
"@babel/runtime": "^7.6.3",
64-
"babel-eslint": "^10.0.3",
65-
"babel-loader": "^8.0.6",
6652
"eslint": "^6.3.0",
6753
"eslint-config-prettier": "^6.2.0",
68-
"eslint-config-standard": "^14.1.0",
69-
"eslint-plugin-import": "^2.18.2",
70-
"eslint-plugin-node": "^10.0.0",
7154
"eslint-plugin-prettier": "^3.1.0",
72-
"eslint-plugin-promise": "^4.2.1",
73-
"eslint-plugin-standard": "^4.0.1",
74-
"prettier": "^1.18.2",
75-
"webpack": "^4.39.1",
76-
"webpack-cli": "^3.3.6"
55+
"husky": "^3.1.0",
56+
"lint-staged": "^9.5.0",
57+
"prettier": "^1.18.2"
58+
},
59+
"husky": {
60+
"hooks": {
61+
"pre-commit": "lint-staged"
62+
}
63+
},
64+
"lint-staged": {
65+
"*.js": [
66+
"./node_modules/.bin/eslint --ignore-pattern '!.eslintrc.js --fix",
67+
"git add"
68+
],
69+
"*.{json,css,md}": [
70+
"./node_modules/.bin/prettier --write",
71+
"git add"
72+
]
7773
}
7874
}

src/index.js

Lines changed: 73 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,16 @@
11
import * as bip39 from 'bip39';
22
import crypto from 'crypto';
33
import * as HDKey from 'ethereumjs-wallet/hdkey';
4-
import Swal from 'sweetalert2';
54
import Web3ProviderEngine from 'web3-provider-engine';
65
import FixtureSubprovider from 'web3-provider-engine/subproviders/fixture.js';
76
import RpcSubprovider from 'web3-provider-engine/subproviders/rpc.js';
87
import HookedWalletSubprovider from 'web3-provider-engine/subproviders/hooked-wallet-ethtx.js';
98
import networkConfig from './networkConfig';
10-
import {encryptMnemonic, encryptSecret, decryptMnemonic, decryptSecret} from './utils/encryption';
9+
import { encryptMnemonic, decryptMnemonic } from './utils/encryption';
1110

1211
const STORAGE_SESSION_KEY = 'wallet-session';
1312
const MNEMONIC_PATH = "m/44'/60'/0'/0/0";
1413

15-
/**
16-
* Fetch encrypted mnemonic from browser's local storage
17-
* @method getEncryptedMnemonic
18-
* @returns {String} encryptedMnemonic
19-
*/
20-
function getEncryptedMnemonic() {
21-
const data = localStorage.getItem(STORAGE_SESSION_KEY);
22-
if (!data) {
23-
return Error('User not logged in');
24-
}
25-
26-
const {encryptedMnemonic} = JSON.parse(data);
27-
return encryptedMnemonic;
28-
}
29-
30-
/**
31-
* Prompt for wallet password
32-
* @method promptPassword
33-
* @returns {Promise<String>} password
34-
*/
35-
async function promptPassword() {
36-
const {value: password} = await Swal.fire({
37-
title: 'Enter your password',
38-
input: 'password',
39-
inputPlaceholder: 'Enter your password',
40-
inputAttributes: {
41-
maxlength: 50,
42-
autocapitalize: 'off',
43-
autocorrect: 'off'
44-
}
45-
});
46-
47-
if (!password) {
48-
throw new Error('Password not entered');
49-
}
50-
51-
return password;
52-
}
53-
54-
/**
55-
* Get wallet password from encrypted password
56-
* @method getPassword
57-
* @param address - wallet address
58-
* @returns {Promise<String>} password
59-
*/
60-
async function getPassword() {
61-
const data = localStorage.getItem(STORAGE_SESSION_KEY);
62-
const address = JSON.parse(data).address;
63-
const encryptedPassword = sessionStorage.getItem(STORAGE_SESSION_KEY);
64-
65-
if (!encryptedPassword) {
66-
return promptPassword();
67-
}
68-
69-
return decryptSecret(address, encryptedPassword);
70-
}
71-
7214
/**
7315
* `SpringWallet` class
7416
*/
@@ -82,7 +24,25 @@ export default class SpringWallet {
8224
throw new Error("'network' not defined");
8325
}
8426
this.network = networkConfig(network);
85-
this.provider = this.initProvider(network);
27+
this.walletAddress = this.initWalletAddress();
28+
this.privateKey = this.initPrivateKey();
29+
this.provider = this.initProvider(this.network);
30+
}
31+
32+
initWalletAddress() {
33+
let walletSession = SpringWallet.getWalletSession();
34+
if (!walletSession) {
35+
return null;
36+
}
37+
return walletSession.address;
38+
}
39+
40+
initPrivateKey() {
41+
let privateKey = sessionStorage.getItem(STORAGE_SESSION_KEY);
42+
if (!privateKey) {
43+
return null;
44+
}
45+
return privateKey;
8646
}
8747

8848
initProvider(opts) {
@@ -97,21 +57,30 @@ export default class SpringWallet {
9757
})
9858
);
9959

100-
opts.getPrivateKey = async (address, cb) => {
60+
opts.getPrivateKey = (address, cb) => {
10161
if (address.toLowerCase() === this.walletAddress.toLowerCase()) {
102-
const privKey = this.wallet.getPrivateKey().toString('hex');
103-
cb(null, Buffer.from(privKey, 'hex'));
62+
if (this.privateKey) {
63+
cb(null, this.privateKey);
64+
} else if (this.wallet) {
65+
const privKey = this.wallet.getPrivateKey().toString('hex');
66+
cb(null, Buffer.from(privKey, 'hex'));
67+
} else {
68+
cb('unlock wallet');
69+
}
10470
} else {
10571
cb('unknown account');
10672
}
10773
};
10874

109-
opts.getAccounts = async (cb) => {
110-
if (!this.wallet) {
111-
await this.unlockWallet();
75+
opts.getAccounts = (cb) => {
76+
let address;
77+
if (this.walletAddress) {
78+
address = this.walletAddress;
79+
} else if (this.wallet) {
80+
address = this.wallet.getChecksumAddressString();
81+
this.walletAddress = address;
11282
}
113-
const address = this.wallet.getChecksumAddressString();
114-
this.walletAddress = address;
83+
11584
cb(false, [address]);
11685
};
11786

@@ -143,26 +112,6 @@ export default class SpringWallet {
143112
return bip39.validateMnemonic(phrase);
144113
}
145114

146-
/**
147-
* Returns wallet address
148-
* @method createWallet
149-
* @param {String} 12-words Plain mnemonic phrase
150-
* @returns {String} wallet address
151-
*/
152-
static async createWallet(phrase, password) {
153-
if (!bip39.validateMnemonic(phrase)) {
154-
throw new Error('Not a valid bip39 mnemonic');
155-
}
156-
const encryptedMnemonic = await encryptMnemonic(phrase, password);
157-
const hdKey = HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(phrase));
158-
const wallet = hdKey
159-
.derivePath(MNEMONIC_PATH)
160-
.deriveChild(0)
161-
.getWallet();
162-
const address = wallet.getChecksumAddressString();
163-
return {address, encryptedMnemonic};
164-
}
165-
166115
/**
167116
* Encrypt Plain text mnemonic phrase
168117
* @method encryptMnemonic
@@ -174,14 +123,40 @@ export default class SpringWallet {
174123
return encryptMnemonic(phrase, password);
175124
}
176125

126+
/**
127+
* Decrypt an encrypted mnemonic with a password
128+
* @method decryptMnemonic
129+
* @param {String} encryptedMnemonic - Hex-encoded string of the encrypted mnemonic
130+
* @param {String} password - Password
131+
* @return {Promise<String>} mnemonic - plain text mnemonic phrase
132+
*/
133+
static decryptMnemonic(encryptedMnemonic, password) {
134+
return decryptMnemonic(encryptedMnemonic, password);
135+
}
136+
177137
/**
178138
* Set wallet session in browser's localStorage
179139
* @method setWalletSession
180140
* @param {String} address - derived wallet address
181141
* @param {String} encryptedMnemonic
182142
*/
183143
static setWalletSession(address, encryptedMnemonic) {
184-
localStorage.setItem(STORAGE_SESSION_KEY, JSON.stringify({address, encryptedMnemonic}));
144+
localStorage.setItem(STORAGE_SESSION_KEY, JSON.stringify({ address, encryptedMnemonic }));
145+
}
146+
147+
/**
148+
* Fetch encrypted mnemonic from browser's local storage
149+
* @method getEncryptedMnemonic
150+
* @returns {String} encryptedMnemonic
151+
*/
152+
static getWalletSession() {
153+
const data = localStorage.getItem(STORAGE_SESSION_KEY);
154+
if (!data) {
155+
return null;
156+
}
157+
158+
const { address, encryptedMnemonic } = JSON.parse(data);
159+
return { address, encryptedMnemonic };
185160
}
186161

187162
/**
@@ -190,7 +165,7 @@ export default class SpringWallet {
190165
* @param path Mnemonic Path
191166
* @returns wallet instance
192167
*/
193-
async initializeWalletFromMnemonic(mnemonic) {
168+
static async initializeWalletFromMnemonic(mnemonic) {
194169
const hdKey = await HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic));
195170
const wallet = await hdKey
196171
.derivePath(MNEMONIC_PATH)
@@ -199,45 +174,13 @@ export default class SpringWallet {
199174
return wallet;
200175
}
201176

202-
/**
203-
* Unlocks a wallet
204-
* @method unlockWallet
205-
* @param {String} encryptedMnemonic
206-
* @returns {Promise<Boolean>}
207-
*/
208-
async unlockWallet() {
209-
const password = await getPassword();
210-
const encryptedMnemonic = getEncryptedMnemonic();
211-
try {
212-
const mnemonic = await decryptMnemonic(encryptedMnemonic, password);
213-
this.wallet = await this.initializeWalletFromMnemonic(mnemonic);
214-
const address = await this.wallet.getChecksumAddressString();
215-
await encryptSecret(address, password);
216-
return true;
217-
} catch (error) {
218-
throw new Error('Invalid Password');
219-
}
220-
}
221-
222-
/**
223-
* Reinitialize a wallet with new encrypted mnemonic
224-
* checks if the derived wallet address is same
225-
* @method reinitializeWallet
226-
* @param {String} address - wallet address
227-
* @param {String} encryptedMnemonic - new encrypted mnemonic
228-
* @returns {Promise<Boolean>}
229-
*/
230-
static async reinitializeWallet(address, encryptedMnemonic) {
231-
const password = await getPassword();
232-
const mnemonic = await decryptMnemonic(encryptedMnemonic, password);
233-
const Wallet = await this.initializeWalletFromMnemonic(mnemonic, MNEMONIC_PATH);
234-
const derivedWalletAddress = await Wallet.getChecksumAddressString();
235-
if (derivedWalletAddress !== address) {
236-
throw new Error('Different wallet mnemonics');
237-
}
238-
SpringWallet.setWalletSession(address, encryptMnemonic);
239-
return true;
177+
async unlockWallet(mnemonic) {
178+
const wallet = await SpringWallet.initializeWalletFromMnemonic(mnemonic);
179+
this.wallet = wallet;
180+
this.walletAddress = wallet.getChecksumAddressString();
181+
const privKey = wallet.getPrivateKey().toString('hex');
182+
this.privateKey = Buffer.from(privKey, 'hex');
183+
sessionStorage.setItem(STORAGE_SESSION_KEY, privKey);
184+
return this.walletAddress;
240185
}
241186
}
242-
243-
export {decryptMnemonic};

0 commit comments

Comments
 (0)