on 8ca8b8a help sign in
bridge/src/lib/wallet.js
151 lines 3.9 kB history raw
1import * as bip32 from 'bip32';
2import * as bip39 from 'bip39';
3import keccak from 'keccak';
4import { reduce } from 'lodash';
5import Maybe from 'folktale/maybe';
6import * as secp256k1 from 'secp256k1';
7import * as ob from 'urbit-ob';
8import * as kg from 'urbit-key-generation/dist';
9import { isAddress } from 'web3-utils';
11const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0";
13const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000';
14const CURVE_ZERO_ADDR =
15 '0x0000000000000000000000000000000000000000000000000000000000000000';
17const WALLET_TYPES = {
18 MNEMONIC: Symbol('MNEMONIC'),
19 TICKET: Symbol('TICKET'),
20 SHARDS: Symbol('SHARDS'),
21 LEDGER: Symbol('LEDGER'),
22 TREZOR: Symbol('TREZOR'),
23 PRIVATE_KEY: Symbol('PRIVATE_KEY'),
24 KEYSTORE: Symbol('KEYSTORE'),
25};
27function EthereumWallet(privateKey) {
28 this.privateKey = privateKey;
29 this.publicKey = secp256k1.publicKeyCreate(this.privateKey);
30 const pub = this.publicKey.toString('hex');
31 this.address = addressFromSecp256k1Public(pub);
34const renderWalletType = wallet =>
35 wallet === WALLET_TYPES.MNEMONIC
36 ? 'Mnemonic'
37 : wallet === WALLET_TYPES.TICKET
38 ? 'Ticket'
39 : wallet === WALLET_TYPES.SHARDS
40 ? 'Ticket Shards'
41 : wallet === WALLET_TYPES.LEDGER
42 ? 'Ledger'
43 : wallet === WALLET_TYPES.TREZOR
44 ? 'Trezor'
45 : wallet === WALLET_TYPES.PRIVATE_KEY
46 ? 'Private Key'
47 : wallet === WALLET_TYPES.KEYSTORE
48 ? 'Keystore File'
49 : 'Wallet';
51const addressFromSecp256k1Public = pub => {
52 const compressed = false;
53 const uncompressed = secp256k1.publicKeyConvert(
54 Buffer.from(pub, 'hex'),
55 compressed
56 );
57 const chopped = uncompressed.slice(1); // chop parity byte
58 const hashed = keccak256(chopped);
59 const addr = addHexPrefix(hashed.slice(-20).toString('hex'));
60 return toChecksumAddress(addr);
61};
63const addHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex : '0x' + hex);
65const stripHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex.slice(2) : hex);
67const keccak256 = str =>
68 keccak('keccak256')
69 .update(str)
70 .digest();
72const isValidAddress = a => '0x0' === a || isAddress(a);
74const toChecksumAddress = address => {
75 const addr = stripHexPrefix(address).toLowerCase();
76 const hash = keccak256(addr).toString('hex');
78 return reduce(
79 addr,
80 (acc, char, idx) =>
81 parseInt(hash[idx], 16) >= 8 ? acc + char.toUpperCase() : acc + char,
82 '0x'
83 );
84};
86const eqAddr = (addr0, addr1) =>
87 toChecksumAddress(addr0) === toChecksumAddress(addr1);
89const urbitWalletFromTicket = async (ticket, point, passphrase) => {
90 if (typeof point === 'string') {
91 //TODO why not in kg?
92 point = ob.patp2dec(point);
93 }
94 let uhdw = await kg.generateWallet({
95 ticket: ticket,
96 ship: point,
97 passphrase: passphrase,
98 });
99 return uhdw;
100};
102const ownershipWalletFromTicket = async (ticket, point, passphrase) => {
103 if (typeof point === 'string') {
104 //TODO why not in kg?
105 point = ob.patp2dec(point);
106 }
107 return await kg.generateOwnershipWallet({
108 ticket,
109 ship: point,
110 passphrase,
111 });
112};
114const walletFromMnemonic = (mnemonic, hdpath, passphrase) => {
115 const seed = bip39.validateMnemonic(mnemonic)
116 ? Maybe.Just(bip39.mnemonicToSeed(mnemonic, passphrase))
117 : Maybe.Nothing();
119 const toWallet = (sd, path) => {
120 let wal;
121 try {
122 const hd = bip32.fromSeed(sd);
123 wal = hd.derivePath(path);
124 wal.address = addressFromSecp256k1Public(wal.publicKey);
125 wal = Maybe.Just(wal);
126 } catch (_) {
127 wal = Maybe.Nothing();
128 }
129 return wal;
130 };
132 const wallet = seed.chain(sd => toWallet(sd, hdpath));
133 return wallet;
134};
136export {
137 DEFAULT_HD_PATH,
138 WALLET_TYPES,
139 ETH_ZERO_ADDR,
140 CURVE_ZERO_ADDR,
141 renderWalletType,
142 isValidAddress,
143 toChecksumAddress,
144 addressFromSecp256k1Public,
145 eqAddr,
146 urbitWalletFromTicket,
147 ownershipWalletFromTicket,
148 walletFromMnemonic,
149 addHexPrefix,
150 EthereumWallet,
151};