on b516e1e help sign in
bridge/src/lib/wallet.js
120 lines 2.9 kB history raw
1import * as bip32 from 'bip32'
2import * as bip39 from 'bip39'
3import keccak from 'keccak'
4import * as lodash from 'lodash'
5import Maybe from 'folktale/maybe'
6import * as secp256k1 from 'secp256k1'
8const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0"
10const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000'
11const CURVE_ZERO_ADDR = "0x0000000000000000000000000000000000000000000000000000000000000000"
13const WALLET_NAMES = {
14 MNEMONIC: Symbol('MNEMONIC'),
15 TICKET: Symbol('TICKET'),
16 SHARDS: Symbol('SHARDS'),
17 LEDGER: Symbol('LEDGER'),
18 TREZOR: Symbol('TREZOR'),
19 PRIVATE_KEY: Symbol('PRIVATE_KEY')
22function EthereumWallet(privateKey) {
23 this.privateKey = privateKey
24 this.publicKey = secp256k1.publicKeyCreate(this.privateKey)
25 const pub = this.publicKey.toString('hex')
26 this.address = addressFromSecp256k1Public(pub)
29const renderWalletType = (wallet) =>
30 wallet === WALLET_NAMES.MNEMONIC
31 ? 'Mnemonic'
32 : wallet === WALLET_NAMES.TICKET
33 ? 'Ticket'
34 : wallet === WALLET_NAMES.SHARDS
35 ? 'Ticket Shards'
36 : wallet === WALLET_NAMES.LEDGER
37 ? 'Ledger'
38 : wallet === WALLET_NAMES.TREZOR
39 ? 'Trezor'
40 : wallet === WALLET_NAMES.PRIVATE_KEY
41 ? 'Private Key'
42 : 'Wallet'
44const addressFromSecp256k1Public = pub => {
45 const compressed = false
46 const uncompressed = secp256k1.publicKeyConvert(
47 Buffer.from(pub, 'hex'),
48 compressed
49 )
50 const chopped = uncompressed.slice(1) // chop parity byte
51 const hashed = keccak256(chopped)
52 const addr = addHexPrefix(hashed.slice(-20).toString('hex'))
53 return toChecksumAddress(addr)
56const addHexPrefix = hex =>
57 hex.slice(0, 2) === '0x'
58 ? hex
59 : '0x' + hex
61const stripHexPrefix = hex =>
62 hex.slice(0, 2) === '0x'
63 ? hex.slice(2)
64 : hex
66const keccak256 = str =>
67 keccak('keccak256').update(str).digest()
69const isValidAddress = (address) =>
70 /^0x[0-9a-fA-F]{40}$/.test(address)
72const toChecksumAddress = (address) => {
73 const addr = stripHexPrefix(address).toLowerCase()
74 const hash = keccak256(addr).toString('hex')
76 return lodash.reduce(addr, (acc, char, idx) =>
77 parseInt(hash[idx], 16) >= 8
78 ? acc + char.toUpperCase()
79 : acc + char,
80 '0x')
83const eqAddr = (addr0, addr1) =>
84 toChecksumAddress(addr0) === toChecksumAddress(addr1)
86const walletFromMnemonic = (mnemonic, hdpath) => {
87 const seed =
88 bip39.validateMnemonic(mnemonic)
89 ? Maybe.Just(bip39.mnemonicToSeed(mnemonic))
90 : Maybe.Nothing()
92 const toWallet = (sd, path) => {
93 let wal
94 try {
95 const hd = bip32.fromSeed(sd)
96 wal = Maybe.Just(hd.derivePath(path))
97 } catch (_) {
98 wal = Maybe.Nothing()
99 }
100 return wal
101 }
103 const wallet = seed.chain(sd => toWallet(sd, hdpath))
104 return wallet
107export {
108 DEFAULT_HD_PATH,
109 WALLET_NAMES,
110 ETH_ZERO_ADDR,
111 CURVE_ZERO_ADDR,
112 renderWalletType,
113 isValidAddress,
114 toChecksumAddress,
115 addressFromSecp256k1Public,
116 eqAddr,
117 walletFromMnemonic,
118 addHexPrefix,
119 EthereumWallet