on e366ef3 help sign in
bridge/src/store/wallet.js
121 lines 3.4 kB history raw
1import React, { createContext, forwardRef, useContext, useState } from 'react';
2import Maybe from 'folktale/maybe';
3import { includes } from 'lodash';
5import {
6 WALLET_TYPES,
7 DEFAULT_HD_PATH,
8 addressFromSecp256k1Public,
9 walletFromMnemonic,
10} from '../lib/wallet';
11import { BRIDGE_ERROR } from '../lib/error';
13export const WalletContext = createContext(null);
15// NB (jtobin):
16//
17// Note that the 'wallet' prop has type depending on the 'walletType' prop.
18// These could be bound together in a single structure (so that
19// 'walletType' acted more explicitly as a data constructor of sorts) but
20// it doesn't necessarily help us much, since we branch on 'walletType'
21// before setting 'wallet'.
22//
23// Wallets are always wrapped in Maybe. For most authentication
24// mechanisms, Maybe wraps a BIP32 HD wallet; for Ethereum private keys,
25// JSON keystore files, and Metamask authentication, it wraps an
26// 'EthereumWallet'.
27function _useWallet(
28 initialWallet = Maybe.Nothing(),
29 initialMnemonic = Maybe.Nothing()
30) {
31 const [walletType, _setWalletType] = useState(WALLET_TYPES.MNEMONIC);
32 const [walletHdPath, setWalletHdPath] = useState(DEFAULT_HD_PATH);
33 const [wallet, _setWallet] = useState(initialWallet);
34 const [urbitWallet, _setUrbitWallet] = useState(Maybe.Nothing());
35 const [authMnemonic, setAuthMnemonic] = useState(initialMnemonic);
36 const [networkSeed, setNetworkSeed] = useState(Maybe.Nothing());
37 const [networkRevision, setNetworkRevision] = useState(Maybe.Nothing());
39 const setWalletType = walletType => {
40 if (!includes(WALLET_TYPES, walletType)) {
41 throw BRIDGE_ERROR.INVALID_WALLET_TYPE;
42 }
44 _setWalletType(walletType);
45 };
47 const setWallet = wallet => {
48 // force that public addresses are derived for each wallet
49 wallet.map(wal => {
50 wal.address = wal.address || addressFromSecp256k1Public(wal.publicKey);
51 return wal;
52 });
54 _setWallet(wallet);
55 };
57 const setUrbitWallet = urbitWallet => {
58 if (Maybe.Just.hasInstance(urbitWallet)) {
59 // when an urbit wallet is set, also derive
60 // a normal bip32 wallet using the ownership address
61 const wallet = walletFromMnemonic(
62 urbitWallet.value.ownership.seed,
63 DEFAULT_HD_PATH,
64 urbitWallet.value.meta.passphrase
65 );
67 // manually set the public address
68 wallet.map(wal => {
69 wal.address = urbitWallet.value.ownership.keys.address;
70 return wal;
71 });
73 setWallet(wallet);
74 }
76 _setUrbitWallet(urbitWallet);
77 };
79 return {
80 //
81 walletType,
82 setWalletType,
83 //
84 walletHdPath,
85 setWalletHdPath,
86 //
87 wallet,
88 setWallet,
89 //
90 urbitWallet,
91 setUrbitWallet,
92 authMnemonic,
93 setAuthMnemonic,
94 //
95 networkSeed,
96 setNetworkSeed,
97 networkRevision,
98 setNetworkRevision,
99 };
102export function WalletProvider({ initialWallet, initialMnemonic, children }) {
103 const wallet = _useWallet(initialWallet, initialMnemonic);
105 return (
106 <WalletContext.Provider value={wallet}>{children}</WalletContext.Provider>
107 );
110// Hook version
111export function useWallet() {
112 return useContext(WalletContext);
115// HOC version
116export const withWallet = Component =>
117 forwardRef((props, ref) => (
118 <WalletContext.Consumer>
119 {wallet => <Component ref={ref} {...wallet} {...props} />}
120 </WalletContext.Consumer>
121 ));