import * as bip32 from 'bip32'; import * as bip39 from 'bip39'; import keccak from 'keccak'; import { reduce } from 'lodash'; import Maybe from 'folktale/maybe'; import * as secp256k1 from 'secp256k1'; import * as ob from 'urbit-ob'; import * as kg from 'urbit-key-generation/dist'; import { isAddress } from 'web3-utils'; const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0"; const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000'; const CURVE_ZERO_ADDR = '0x0000000000000000000000000000000000000000000000000000000000000000'; const WALLET_TYPES = { MNEMONIC: Symbol('MNEMONIC'), TICKET: Symbol('TICKET'), SHARDS: Symbol('SHARDS'), LEDGER: Symbol('LEDGER'), TREZOR: Symbol('TREZOR'), PRIVATE_KEY: Symbol('PRIVATE_KEY'), KEYSTORE: Symbol('KEYSTORE'), }; function EthereumWallet(privateKey) { this.privateKey = privateKey; this.publicKey = secp256k1.publicKeyCreate(this.privateKey); const pub = this.publicKey.toString('hex'); this.address = addressFromSecp256k1Public(pub); } const renderWalletType = wallet => wallet === WALLET_TYPES.MNEMONIC ? 'Mnemonic' : wallet === WALLET_TYPES.TICKET ? 'Ticket' : wallet === WALLET_TYPES.SHARDS ? 'Ticket Shards' : wallet === WALLET_TYPES.LEDGER ? 'Ledger' : wallet === WALLET_TYPES.TREZOR ? 'Trezor' : wallet === WALLET_TYPES.PRIVATE_KEY ? 'Private Key' : wallet === WALLET_TYPES.KEYSTORE ? 'Keystore File' : 'Wallet'; const addressFromSecp256k1Public = pub => { const compressed = false; const uncompressed = secp256k1.publicKeyConvert( Buffer.from(pub, 'hex'), compressed ); const chopped = uncompressed.slice(1); // chop parity byte const hashed = keccak256(chopped); const addr = addHexPrefix(hashed.slice(-20).toString('hex')); return toChecksumAddress(addr); }; const addHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex : '0x' + hex); const stripHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex.slice(2) : hex); const keccak256 = str => keccak('keccak256') .update(str) .digest(); const isValidAddress = a => '0x0' === a || isAddress(a); const toChecksumAddress = address => { const addr = stripHexPrefix(address).toLowerCase(); const hash = keccak256(addr).toString('hex'); return reduce( addr, (acc, char, idx) => parseInt(hash[idx], 16) >= 8 ? acc + char.toUpperCase() : acc + char, '0x' ); }; const eqAddr = (addr0, addr1) => toChecksumAddress(addr0) === toChecksumAddress(addr1); const urbitWalletFromTicket = async (ticket, point, passphrase) => { if (typeof point === 'string') { //TODO why not in kg? point = ob.patp2dec(point); } let uhdw = await kg.generateWallet({ ticket: ticket, ship: point, passphrase: passphrase, }); return uhdw; }; const ownershipWalletFromTicket = async (ticket, point, passphrase) => { if (typeof point === 'string') { //TODO why not in kg? point = ob.patp2dec(point); } return await kg.generateOwnershipWallet({ ticket, ship: point, passphrase, }); }; const walletFromMnemonic = (mnemonic, hdpath, passphrase) => { const seed = bip39.validateMnemonic(mnemonic) ? Maybe.Just(bip39.mnemonicToSeed(mnemonic, passphrase)) : Maybe.Nothing(); const toWallet = (sd, path) => { let wal; try { const hd = bip32.fromSeed(sd); wal = hd.derivePath(path); wal.address = addressFromSecp256k1Public(wal.publicKey); wal = Maybe.Just(wal); } catch (_) { wal = Maybe.Nothing(); } return wal; }; const wallet = seed.chain(sd => toWallet(sd, hdpath)); return wallet; }; export { DEFAULT_HD_PATH, WALLET_TYPES, ETH_ZERO_ADDR, CURVE_ZERO_ADDR, renderWalletType, isValidAddress, toChecksumAddress, addressFromSecp256k1Public, eqAddr, urbitWalletFromTicket, ownershipWalletFromTicket, walletFromMnemonic, addHexPrefix, EthereumWallet, };