diff --git a/package.json b/package.json index 9e03d1a..6375e81 100644 --- a/package.json +++ b/package.json @@ -14,19 +14,22 @@ "dependencies": { "@metamask/eth-sig-util": "^4.0.1", "bip39": "^3.0.4", + "crypto-js": "^4.1.1", "ethereumjs-wallet": "^1.0.2", "web3": "^1.7.4", "whatwg-fetch": "^3.6.2" }, "devDependencies": { - "assert": "^2.0.0", - "buffer": "^6.0.3", "@babel/cli": "^7.12.1", "@babel/core": "^7.12.3", - "babel-loader": "^8.1.0", "@babel/preset-env": "^7.12.1", "@babel/preset-typescript": "^7.12.1", "@babel/runtime": "^7.12.1", + "@types/aes-js": "^3.1.1", + "@types/crypto-js": "^4.1.1", + "assert": "^2.0.0", + "babel-loader": "^8.1.0", + "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", diff --git a/src/index.ts b/src/index.ts index 19af80f..44bdeca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import { IAccount, initAccount } from "./data/DataModel"; import { DataManage } from "./manage/DataManage"; import { WALLET_STORAGE_KEY_NAME } from "./config/constants"; import { DEFALUT_TOKENS } from "./config/chain_config"; +import { newMnemonic, restoreWalletByMnemonic } from "./manage/WalletManage"; var global = @@ -88,19 +89,28 @@ export default class JCWallet { } } if (!this.wallet || this.wallet.length === 0) { - // let key = '0xa6c4354fb93a55fb67117969a12465209395ec31089fea9e6e061f873b87a473' - // this.wallet.add(key); - // this.web3.eth.accounts.wallet.save(this.password, WALLET_STORAGE_KEY_NAME); - this.createAccount(); + // this.createAccount(); + this.newWallet('111111'); } } + public newWallet(password: string) { + this.password = password + newMnemonic(this.password) + this.createAccount() + } + + public restoreFromMnemonic(mnemonic: string, password: string) { + this.password = password + this.wallet.clear() + restoreWalletByMnemonic(mnemonic, this.password); + this.createAccount() + } + get currentChain() { return this._currentChain } - - updateCurrentChain(chainId: number) { const chainData = this.chainMap.get(chainId) this._currentChain = chainData @@ -164,13 +174,14 @@ export default class JCWallet { } public createAccount() { - let account = this.web3.eth.accounts.create() - this.wallet.add(account) + // let account = this.web3.eth.accounts.create() + const index = this.getMaxIdexOfType(0); + const accountNew = this.dataManage.newAccount(this.password, index) + const account = this.wallet.add(accountNew) this.wallet.save(this.password, WALLET_STORAGE_KEY_NAME) const chain = this.currentChain.id let data = this.data.find(o => o.address === account.address) if (!data) { - const index = this.getMaxIdexOfType(0); const nickname = `Account ${ index + 1 }` data = initAccount({ address: account.address, @@ -188,11 +199,7 @@ export default class JCWallet { } public importAccount(privateKey: string) { - let account = this.web3.eth.accounts.privateKeyToAccount(privateKey) - if (this.wallet[account.address]) { - return false - } - this.wallet.add(account); + const account = this.wallet.add(privateKey); const chain = this.currentChain.id let data = this.data.find(o => o.address === account.address) if (!data) { @@ -286,4 +293,4 @@ export * from './config/chain_config' export * from './util/number.util' export * from './util/wallet.util' export * from "./data/DataModel"; -export * from './config/chain_config'; \ No newline at end of file +export * from './config/chain_config'; diff --git a/src/manage/DataManage.ts b/src/manage/DataManage.ts index a4bd9b4..c40dd22 100644 --- a/src/manage/DataManage.ts +++ b/src/manage/DataManage.ts @@ -1,7 +1,9 @@ import { IAccount } from "../data/DataModel"; import { singleton } from "../decorator/singleton.decorator"; +import { aesDecrypt, aesEncrypt } from "../util/crypto.util"; const LOCAL_ACCOUNT_DATAS = 'local_account_datas' +const LOCAL_WALLET_MNEMONIC = 'local_wallet_mnemonic' @singleton export class DataManage{ public loadData(){ @@ -21,4 +23,21 @@ export class DataManage{ const dataStr = JSON.stringify(datas) localStorage.setItem(LOCAL_ACCOUNT_DATAS, dataStr) } + + public loadMnemonic(password: string) { + let dataStr: string = localStorage.getItem(LOCAL_WALLET_MNEMONIC) + if (dataStr) { + dataStr = aesDecrypt(dataStr, password) + } + return dataStr + } + + + + public saveMnemonic(mnemonic: string, password: string) { + const dataStr = aesEncrypt(mnemonic, password); + localStorage.setItem(LOCAL_WALLET_MNEMONIC, dataStr); + } + + } \ No newline at end of file diff --git a/src/manage/WalletManage.ts b/src/manage/WalletManage.ts new file mode 100644 index 0000000..9898915 --- /dev/null +++ b/src/manage/WalletManage.ts @@ -0,0 +1,22 @@ +import { hdkey } from 'ethereumjs-wallet' +import { generateMnemonic, mnemonicToSeedSync } from "bip39"; + + +export function newAccount(password: string, index: number) { + const mnemonic = this.loadMnemonic(password) + const seed = mnemonicToSeedSync(mnemonic) + const hdWallet = hdkey.fromMasterSeed(seed) + const keyPair1 = hdWallet.derivePath(`m/44'/60'/0'/0/${index}`) + const w1 = keyPair1.getWallet() + return {address: w1.getAddressString(), privateKey: w1.getPrivateKeyString()} +} + +export function newMnemonic(password: string) { + let mnemonic = generateMnemonic() + this.saveMnemonic(mnemonic, password) + return mnemonic +} + +export function restoreWalletByMnemonic(mnemonic: string, password: string) { + this.saveMnemonic(mnemonic, password) +} \ No newline at end of file diff --git a/src/util/crypto.util.ts b/src/util/crypto.util.ts new file mode 100644 index 0000000..ec1d189 --- /dev/null +++ b/src/util/crypto.util.ts @@ -0,0 +1,32 @@ +import { AES, enc, mode, pad } from 'crypto-js'; +import sha256 from 'crypto-js/sha256' + +function generateIV(password: string) { + const key = sha256(password).toString(); + const keyHex = enc.Base64.parse(key); + const ivHex = keyHex.clone(); + ivHex.sigBytes = 16; + ivHex.words.splice(4); + return {keyHex,ivHex} +} + +export function aesEncrypt(text: string, password: string) { + const {keyHex, ivHex} = generateIV(password); + const messageHex = enc.Utf8.parse(text); + const encrypted = AES.encrypt(messageHex, keyHex, { + "iv": ivHex, + "mode": mode.CBC, + "padding": pad.Pkcs7 + }); + return encrypted.toString(); +} + +export function aesDecrypt(encryptedText: string, password: string) { + const {keyHex, ivHex} = generateIV(password); + const decrypt = AES.decrypt(encryptedText, keyHex, { + "iv": ivHex, + "mode": mode.CBC, + "padding": pad.Pkcs7 + }); + return enc.Utf8.stringify(decrypt); +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 49b46de..656f0f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1204,6 +1204,11 @@ dependencies: defer-to-connect "^1.0.1" +"@types/aes-js@^3.1.1": + version "3.1.1" + resolved "https://registry.npmmirror.com/@types/aes-js/-/aes-js-3.1.1.tgz#34b3978122310c135de4b377270d1d65676fae28" + integrity sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw== + "@types/bn.js@^4.11.3": version "4.11.6" resolved "https://registry.npmmirror.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" @@ -1218,6 +1223,11 @@ dependencies: "@types/node" "*" +"@types/crypto-js@^4.1.1": + version "4.1.1" + resolved "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.1.1.tgz#602859584cecc91894eb23a4892f38cfa927890d" + integrity sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA== + "@types/json-schema@^7.0.5": version "7.0.11" resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -2381,6 +2391,11 @@ crypto-browserify@3.12.0, crypto-browserify@^3.11.0, crypto-browserify@^3.12.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto-js@^4.1.1: + version "4.1.1" + resolved "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" + integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== + cyclist@^1.0.1: version "1.0.1" resolved "https://registry.npmmirror.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"