on d1230bb help sign in
bridge/src/lib/wallet.js
122 lines 3.4 kB history raw
1import * as bip32 from 'bip32';
2import * as bip39 from 'bip39';
3import keccak from 'keccak';
4import { reduce } from 'lodash';
5import { Just, Nothing } 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';
11export const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0";
12export const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000';
13export const CURVE_ZERO_ADDR =
14 '0x0000000000000000000000000000000000000000000000000000000000000000';
16export const WALLET_TYPES = {
17 MNEMONIC: Symbol('MNEMONIC'),
18 TICKET: Symbol('TICKET'),
19 SHARDS: Symbol('SHARDS'),
20 LEDGER: Symbol('LEDGER'),
21 TREZOR: Symbol('TREZOR'),
22 PRIVATE_KEY: Symbol('PRIVATE_KEY'),
23 KEYSTORE: Symbol('KEYSTORE'),
24};
26export function EthereumWallet(privateKey) {
27 this.privateKey = privateKey;
28 this.publicKey = secp256k1.publicKeyCreate(this.privateKey);
29 const pub = this.publicKey.toString('hex');
30 this.address = addressFromSecp256k1Public(pub);
33export const addressFromSecp256k1Public = pub => {
34 const compressed = false;
35 const uncompressed = secp256k1.publicKeyConvert(
36 Buffer.from(pub, 'hex'),
37 compressed
38 );
39 const chopped = uncompressed.slice(1); // chop parity byte
40 const hashed = keccak256(chopped);
41 const addr = addHexPrefix(hashed.slice(-20).toString('hex'));
42 return toChecksumAddress(addr);
43};
45// TODO: move all of these generic crypto helpers to another file
46export const addHexPrefix = hex =>
47 hex.slice(0, 2) === '0x' ? hex : '0x' + hex;
49export const stripHexPrefix = hex =>
50 hex.slice(0, 2) === '0x' ? hex.slice(2) : hex;
52export const keccak256 = str =>
53 keccak('keccak256')
54 .update(str)
55 .digest();
57export const isValidAddress = a => '0x0' === a || isAddress(a);
59export const isZeroAddress = a => ETH_ZERO_ADDR === a;
61export const toChecksumAddress = address => {
62 const addr = stripHexPrefix(address).toLowerCase();
63 const hash = keccak256(addr).toString('hex');
65 return reduce(
66 addr,
67 (acc, char, idx) =>
68 parseInt(hash[idx], 16) >= 8 ? acc + char.toUpperCase() : acc + char,
69 '0x'
70 );
71};
73export const eqAddr = (addr0, addr1) =>
74 toChecksumAddress(addr0) === toChecksumAddress(addr1);
76export const urbitWalletFromTicket = async (ticket, point, passphrase) => {
77 if (typeof point === 'string') {
78 //TODO why not in kg?
79 point = ob.patp2dec(point);
80 }
82 return await kg.generateWallet({
83 ticket: ticket,
84 ship: point,
85 passphrase: passphrase,
86 });
87};
89export const ownershipWalletFromTicket = async (ticket, point, passphrase) => {
90 if (typeof point === 'string') {
91 //TODO why not in kg?
92 point = ob.patp2dec(point);
93 }
95 return await kg.generateOwnershipWallet({
96 ticket,
97 ship: point,
98 passphrase,
99 });
100};
102export const walletFromMnemonic = (mnemonic, hdpath, passphrase) => {
103 const seed = bip39.validateMnemonic(mnemonic)
104 ? Just(bip39.mnemonicToSeed(mnemonic, passphrase))
105 : Nothing();
107 const toWallet = (sd, path) => {
108 let wal;
109 try {
110 const hd = bip32.fromSeed(sd);
111 wal = hd.derivePath(path);
112 wal.address = addressFromSecp256k1Public(wal.publicKey);
113 wal = Just(wal);
114 } catch (_) {
115 wal = Nothing();
116 }
117 return wal;
118 };
120 const wallet = seed.chain(sd => toWallet(sd, hdpath));
121 return wallet;
122};