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