on a0efefc help sign in
bridge/src/lib/wallet.js
120 lines 3.3 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 kg from 'urbit-key-generation';
8import { isAddress } from 'web3-utils';
10export const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0";
11export const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000';
12export const ETH_ZERO_ADDR_SHORT = '0x0';
14export const WALLET_TYPES = {
15 MNEMONIC: Symbol('MNEMONIC'),
16 TICKET: Symbol('TICKET'),
17 SHARDS: Symbol('SHARDS'),
18 LEDGER: Symbol('LEDGER'),
19 TREZOR: Symbol('TREZOR'),
20 PRIVATE_KEY: Symbol('PRIVATE_KEY'),
21 KEYSTORE: Symbol('KEYSTORE'),
22 METAMASK: Symbol('METAMASK'),
23};
25export const NONCUSTODIAL_WALLETS = new Set([
26 WALLET_TYPES.METAMASK,
27 WALLET_TYPES.TREZOR,
28 WALLET_TYPES.LEDGER,
29]);
31export function EthereumWallet(privateKey) {
32 this.privateKey = privateKey;
33 this.publicKey = secp256k1.publicKeyCreate(this.privateKey);
34 const pub = this.publicKey.toString('hex');
35 this.address = addressFromSecp256k1Public(pub);
38export const addressFromSecp256k1Public = pub => {
39 const compressed = false;
40 const uncompressed = secp256k1.publicKeyConvert(
41 Buffer.from(pub, 'hex'),
42 compressed
43 );
44 const chopped = uncompressed.slice(1); // chop parity byte
45 const hashed = keccak256(chopped);
46 const addr = addHexPrefix(hashed.slice(-20).toString('hex'));
47 return toChecksumAddress(addr);
48};
50// TODO: move all of these generic crypto helpers to another file
51export const addHexPrefix = hex =>
52 hex.slice(0, 2) === '0x' ? hex : '0x' + hex;
54export const stripHexPrefix = hex =>
55 hex.slice(0, 2) === '0x' ? hex.slice(2) : hex;
57export const keccak256 = str =>
58 keccak('keccak256')
59 .update(str)
60 .digest();
62export const isValidAddress = a => '0x0' === a || isAddress(a);
64export const isZeroAddress = a =>
65 a === ETH_ZERO_ADDR || a === ETH_ZERO_ADDR_SHORT;
67export const toChecksumAddress = address => {
68 const addr = stripHexPrefix(address).toLowerCase();
69 const hash = keccak256(addr).toString('hex');
71 return reduce(
72 addr,
73 (acc, char, idx) =>
74 parseInt(hash[idx], 16) >= 8 ? acc + char.toUpperCase() : acc + char,
75 '0x'
76 );
77};
79export const eqAddr = (addr0, addr1) =>
80 toChecksumAddress(addr0) === toChecksumAddress(addr1);
82export const urbitWalletFromTicket = async (ticket, point, passphrase) => {
83 return await kg.generateWallet({
84 ticket: ticket,
85 ship: point,
86 passphrase: passphrase,
87 });
88};
90export const walletFromMnemonic = (
91 mnemonic,
92 hdpath,
93 passphrase,
94 skipMnemonicCheck
95) => {
96 const seed =
97 skipMnemonicCheck || bip39.validateMnemonic(mnemonic)
98 ? Just(bip39.mnemonicToSeed(mnemonic, passphrase))
99 : Nothing();
101 const toWallet = (sd, path) => {
102 let wal;
103 try {
104 const hd = bip32.fromSeed(sd);
105 wal = hd.derivePath(path);
106 wal.address = addressFromSecp256k1Public(wal.publicKey);
107 wal.passphrase = passphrase || '';
108 wal = Just(wal);
109 } catch (_) {
110 wal = Nothing();
111 }
112 return wal;
113 };
115 const wallet = seed.chain(sd => toWallet(sd, hdpath));
116 return wallet;
117};
119export const abbreviateAddress = address =>
120 `${address.slice(0, 6)}…${address.slice(-4)}`;