on 4c8c607 help sign in
bridge/src/store/wallet.js
191 lines 4.9 kB history raw
1import React, {
2 createContext,
3 forwardRef,
4 useCallback,
5 useContext,
6 useState,
7 useEffect,
8} from 'react';
9import { Just, Nothing } from 'folktale/maybe';
10import { includes } from 'lodash';
12import {
13 WALLET_TYPES,
14 DEFAULT_HD_PATH,
15 addressFromSecp256k1Public,
16 walletFromMnemonic,
17 NONCUSTODIAL_WALLETS,
18} from 'lib/wallet';
19import { getAuthToken } from 'lib/authToken';
20import { BRIDGE_ERROR } from 'lib/error';
22import { useNetwork } from 'store/network';
24export const WalletContext = createContext(null);
26const DEFAULT_WALLET_TYPE = WALLET_TYPES.TICKET;
28// NB (jtobin):
29//
30// Note that the 'wallet' prop has type depending on the 'walletType' prop.
31// These could be bound together in a single structure (so that
32// 'walletType' acted more explicitly as a data constructor of sorts) but
33// it doesn't necessarily help us much, since we branch on 'walletType'
34// before setting 'wallet'.
35//
36// Wallets are always wrapped in For most authentication
37// mechanisms, Maybe wraps a BIP32 HD wallet; for Ethereum private keys,
38// JSON keystore files, and Metamask authentication, it wraps an
39// 'EthereumWallet'.
40function _useWallet(initialWallet = Nothing(), initialMnemonic = Nothing()) {
41 const [walletType, _setWalletType] = useState(() =>
42 initialMnemonic.matchWith({
43 Nothing: () => DEFAULT_WALLET_TYPE,
44 Just: () => WALLET_TYPES.MNEMONIC,
45 })
46 );
47 const [walletHdPath, setWalletHdPath] = useState(DEFAULT_HD_PATH);
48 const [wallet, _setWallet] = useState(initialWallet);
49 const [urbitWallet, _setUrbitWallet] = useState(Nothing());
50 const [authMnemonic, setAuthMnemonic] = useState(initialMnemonic);
51 const [networkSeed, setNetworkSeed] = useState(Nothing());
52 const [networkRevision, setNetworkRevision] = useState(Nothing());
54 const [authToken, setAuthToken] = useState(Nothing());
56 const { web3 } = useNetwork();
58 useEffect(() => {
59 (async () => {
60 if (
61 !Just.hasInstance(wallet) ||
62 !Just.hasInstance(web3) ||
63 NONCUSTODIAL_WALLETS.has(walletType)
64 ) {
65 return;
66 }
67 const _wallet = wallet.value;
68 const _web3 = web3.value;
69 const token = await getAuthToken({
70 wallet: _wallet,
71 walletType,
72 walletHdPath,
73 web3: _web3,
74 });
76 setAuthToken(Just(token));
77 })();
78 }, [wallet, walletType, walletHdPath, web3]);
80 const setWalletType = useCallback(
81 walletType => {
82 if (!includes(WALLET_TYPES, walletType)) {
83 throw new Error(BRIDGE_ERROR.INVALID_WALLET_TYPE);
84 }
86 _setWalletType(walletType);
87 },
88 [_setWalletType]
89 );
91 const setWallet = useCallback(
92 wallet => {
93 // force that public addresses are derived for each wallet
94 // NOTE wallet is Maybe<> and .map is Maybe#map
95 wallet.map(wal => {
96 wal.address = wal.address || addressFromSecp256k1Public(wal.publicKey);
97 return wal;
98 });
100 _setWallet(wallet);
101 },
102 [_setWallet]
103 );
105 const setUrbitWallet = useCallback(
106 urbitWallet => {
107 if (Just.hasInstance(urbitWallet)) {
108 // when an urbit wallet is set, also derive
109 // a normal bip32 wallet using the ownership address
110 const wallet = walletFromMnemonic(
111 urbitWallet.value.ownership.seed,
112 DEFAULT_HD_PATH,
113 urbitWallet.value.meta.passphrase
114 );
116 setWallet(wallet);
117 } else {
118 setWallet(Nothing());
119 }
121 _setUrbitWallet(urbitWallet);
122 },
123 [setWallet]
124 );
126 const resetWallet = useCallback(() => {
127 _setWalletType(DEFAULT_WALLET_TYPE);
128 setWalletHdPath(DEFAULT_HD_PATH);
129 _setWallet(Nothing());
130 _setUrbitWallet(Nothing());
131 setAuthMnemonic(Nothing());
132 setNetworkSeed(Nothing());
133 setNetworkRevision(Nothing());
134 }, [
135 _setWalletType,
136 setWalletHdPath,
137 _setWallet,
138 _setUrbitWallet,
139 setAuthMnemonic,
140 setNetworkSeed,
141 setNetworkRevision,
142 ]);
144 return {
145 //
146 walletType,
147 setWalletType,
148 //
149 walletHdPath,
150 setWalletHdPath,
151 //
152 wallet,
153 setWallet,
154 //
155 urbitWallet,
156 setUrbitWallet,
157 authMnemonic,
158 setAuthMnemonic,
159 //
160 networkSeed,
161 setNetworkSeed,
162 networkRevision,
163 setNetworkRevision,
164 //
165 resetWallet,
166 //
167 authToken,
168 setAuthToken,
169 };
172export function WalletProvider({ initialWallet, initialMnemonic, children }) {
173 const wallet = _useWallet(initialWallet, initialMnemonic);
175 return (
176 <WalletContext.Provider value={wallet}>{children}</WalletContext.Provider>
177 );
180// Hook version
181export function useWallet() {
182 return useContext(WalletContext);
185// HOC version
186export const withWallet = Component =>
187 forwardRef((props, ref) => (
188 <WalletContext.Consumer>
189 {wallet => <Component ref={ref} {...wallet} {...props} />}
190 </WalletContext.Consumer>
191 ));