on d760cbf help sign in
bridge/src/store/wallet.js
152 lines 4.0 kB history raw
1import React, {
2 createContext,
3 forwardRef,
4 useCallback,
5 useContext,
6 useState,
7} from 'react';
8import { Just, Nothing } 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 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(initialWallet = Nothing(), initialMnemonic = Nothing()) {
36 const [walletType, _setWalletType] = useState(DEFAULT_WALLET_TYPE);
37 const [walletHdPath, setWalletHdPath] = useState(DEFAULT_HD_PATH);
38 const [wallet, _setWallet] = useState(initialWallet);
39 const [urbitWallet, _setUrbitWallet] = useState(Nothing());
40 const [authMnemonic, setAuthMnemonic] = useState(initialMnemonic);
41 const [networkSeed, setNetworkSeed] = useState(Nothing());
42 const [networkRevision, setNetworkRevision] = useState(Nothing());
44 const setWalletType = useCallback(
45 walletType => {
46 if (!includes(WALLET_TYPES, walletType)) {
47 throw BRIDGE_ERROR.INVALID_WALLET_TYPE;
48 }
50 _setWalletType(walletType);
51 },
52 [_setWalletType]
53 );
55 const setWallet = useCallback(
56 wallet => {
57 // force that public addresses are derived for each wallet
58 // NOTE wallet is Maybe<> and .map is Maybe#map
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 (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 setWallet(wallet);
81 } else {
82 setWallet(Nothing());
83 }
85 _setUrbitWallet(urbitWallet);
86 },
87 [setWallet]
88 );
90 const resetWallet = useCallback(() => {
91 _setWalletType(DEFAULT_WALLET_TYPE);
92 setWalletHdPath(DEFAULT_HD_PATH);
93 _setWallet(Nothing());
94 _setUrbitWallet(Nothing());
95 setAuthMnemonic(Nothing());
96 setNetworkSeed(Nothing());
97 setNetworkRevision(Nothing());
98 }, [
99 _setWalletType,
100 setWalletHdPath,
101 _setWallet,
102 _setUrbitWallet,
103 setAuthMnemonic,
104 setNetworkSeed,
105 setNetworkRevision,
106 ]);
108 return {
109 //
110 walletType,
111 setWalletType,
112 //
113 walletHdPath,
114 setWalletHdPath,
115 //
116 wallet,
117 setWallet,
118 //
119 urbitWallet,
120 setUrbitWallet,
121 authMnemonic,
122 setAuthMnemonic,
123 //
124 networkSeed,
125 setNetworkSeed,
126 networkRevision,
127 setNetworkRevision,
128 //
129 resetWallet,
130 };
133export function WalletProvider({ initialWallet, initialMnemonic, children }) {
134 const wallet = _useWallet(initialWallet, initialMnemonic);
136 return (
137 <WalletContext.Provider value={wallet}>{children}</WalletContext.Provider>
138 );
141// Hook version
142export function useWallet() {
143 return useContext(WalletContext);
146// HOC version
147export const withWallet = Component =>
148 forwardRef((props, ref) => (
149 <WalletContext.Consumer>
150 {wallet => <Component ref={ref} {...wallet} {...props} />}
151 </WalletContext.Consumer>
152 ));