on fbcd6b6 help sign in
bridge/src/store/wallet.js
154 lines 4.0 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);
21const DEFAULT_WALLET_TYPE = WALLET_TYPES.TICKET;
23// NB (jtobin):
24//
25// Note that the 'wallet' prop has type depending on the 'walletType' prop.
26// These could be bound together in a single structure (so that
27// 'walletType' acted more explicitly as a data constructor of sorts) but
28// it doesn't necessarily help us much, since we branch on 'walletType'
29// before setting 'wallet'.
30//
31// Wallets are always wrapped in Maybe. For most authentication
32// mechanisms, Maybe wraps a BIP32 HD wallet; for Ethereum private keys,
33// JSON keystore files, and Metamask authentication, it wraps an
34// 'EthereumWallet'.
35function _useWallet(
36 initialWallet = Maybe.Nothing(),
37 initialMnemonic = Maybe.Nothing()
38) {
39 const [walletType, _setWalletType] = useState(DEFAULT_WALLET_TYPE);
40 const [walletHdPath, setWalletHdPath] = useState(DEFAULT_HD_PATH);
41 const [wallet, _setWallet] = useState(initialWallet);
42 const [urbitWallet, _setUrbitWallet] = useState(Maybe.Nothing());
43 const [authMnemonic, setAuthMnemonic] = useState(initialMnemonic);
44 const [networkSeed, setNetworkSeed] = useState(Maybe.Nothing());
45 const [networkRevision, setNetworkRevision] = useState(Maybe.Nothing());
47 const setWalletType = useCallback(
48 walletType => {
49 if (!includes(WALLET_TYPES, walletType)) {
50 throw BRIDGE_ERROR.INVALID_WALLET_TYPE;
51 }
53 _setWalletType(walletType);
54 },
55 [_setWalletType]
56 );
58 const setWallet = useCallback(
59 wallet => {
60 // force that public addresses are derived for each wallet
61 wallet.map(wal => {
62 wal.address = wal.address || addressFromSecp256k1Public(wal.publicKey);
63 return wal;
64 });
66 _setWallet(wallet);
67 },
68 [_setWallet]
69 );
71 const setUrbitWallet = useCallback(
72 urbitWallet => {
73 if (Maybe.Just.hasInstance(urbitWallet)) {
74 // when an urbit wallet is set, also derive
75 // a normal bip32 wallet using the ownership address
76 const wallet = walletFromMnemonic(
77 urbitWallet.value.ownership.seed,
78 DEFAULT_HD_PATH,
79 urbitWallet.value.meta.passphrase
80 );
82 setWallet(wallet);
83 } else {
84 setWallet(Maybe.Nothing());
85 }
87 _setUrbitWallet(urbitWallet);
88 },
89 [setWallet]
90 );
92 const resetWallet = useCallback(() => {
93 _setWalletType(DEFAULT_WALLET_TYPE);
94 setWalletHdPath(DEFAULT_HD_PATH);
95 _setWallet(Maybe.Nothing());
96 _setUrbitWallet(Maybe.Nothing());
97 setAuthMnemonic(Maybe.Nothing());
98 setNetworkSeed(Maybe.Nothing());
99 setNetworkRevision(Maybe.Nothing());
100 }, [
101 _setWalletType,
102 setWalletHdPath,
103 _setWallet,
104 _setUrbitWallet,
105 setAuthMnemonic,
106 setNetworkSeed,
107 setNetworkRevision,
108 ]);
110 return {
111 //
112 walletType,
113 setWalletType,
114 //
115 walletHdPath,
116 setWalletHdPath,
117 //
118 wallet,
119 setWallet,
120 //
121 urbitWallet,
122 setUrbitWallet,
123 authMnemonic,
124 setAuthMnemonic,
125 //
126 networkSeed,
127 setNetworkSeed,
128 networkRevision,
129 setNetworkRevision,
130 //
131 resetWallet,
132 };
135export function WalletProvider({ initialWallet, initialMnemonic, children }) {
136 const wallet = _useWallet(initialWallet, initialMnemonic);
138 return (
139 <WalletContext.Provider value={wallet}>{children}</WalletContext.Provider>
140 );
143// Hook version
144export function useWallet() {
145 return useContext(WalletContext);
148// HOC version
149export const withWallet = Component =>
150 forwardRef((props, ref) => (
151 <WalletContext.Consumer>
152 {wallet => <Component ref={ref} {...wallet} {...props} />}
153 </WalletContext.Consumer>
154 ));