on c6f53ef help sign in
bridge/src/components/InlineEthereumTransaction.js
287 lines 7.6 kB history raw
1import React, { useMemo, useCallback, useState } from 'react';
2import cn from 'classnames';
3import { Grid, ErrorText, Flex, LinkButton, H5 } from 'indigo-react';
4import { toBN } from 'web3-utils';
6import { useExploreTxUrls } from 'lib/explorer';
7import { safeFromWei, safeToWei } from 'lib/lib';
9import { composeValidator, buildCheckboxValidator } from 'form/validators';
10import BridgeForm from 'form/BridgeForm';
12import { GenerateButton, ForwardButton, RestartButton } from './Buttons';
13import ProgressButton from './ProgressButton';
14import NeedFundsNotice from './NeedFundsNotice';
15import NoticeBox from './NoticeBox';
17import './InlineEthereumTransaction.scss';
18import FeeDropdown from './L2/Dropdowns/FeeDropdown';
20export default function InlineEthereumTransaction({
21 // from useEthereumTransaction.bind
22 initializing,
23 canSign,
24 generateAndSign,
25 signed,
26 fakeSigned,
27 broadcast,
28 broadcasted,
29 confirmed,
30 completed,
31 reset,
32 error,
33 gasPrice,
34 setGasPrice,
35 resetGasPrice,
36 txHashes,
37 needFunds,
38 confirmationProgress,
39 gasLimit,
40 unsignedTransactions,
41 finalCost,
43 // additional from parent
44 label = 'Generate & Sign Transaction',
45 className,
46 onReturn,
47}) {
48 // show receipt after successful broadcast
49 const [showReceipt, setShowReceipt] = useState(false);
50 const toggleShowReceipt = useCallback(() => setShowReceipt(!showReceipt), [
51 setShowReceipt,
52 showReceipt,
53 ]);
54 // show configure controls pre-broadcast
55 const showConfigureInput = !(signed || broadcasted || confirmed || completed);
56 // show the send/loading button while signed, broadcasting, or confirme
57 const showBroadcastButton = signed;
58 const showLoadingButton = broadcasted || confirmed;
59 const canBroadcast = signed && !needFunds;
60 // show signed tx only when signing (for offline usage)
61 const showSignedTx = signed;
63 const validate = useMemo(
64 () =>
65 composeValidator({
66 useAdvanced: buildCheckboxValidator(),
67 viewSigned: buildCheckboxValidator(),
68 }),
69 []
70 );
72 const onValues = useCallback(
73 ({ valid, values, form }) => {
74 if (!values.useAdvanced) {
75 resetGasPrice();
76 }
77 },
78 [resetGasPrice]
79 );
81 const renderPrimarySection = () => {
82 if (error) {
83 return (
84 <Grid.Item
85 className="eth-tx-button"
86 full
87 as={RestartButton}
88 solid
89 onClick={() => reset()}>
90 Reset Transaction
91 </Grid.Item>
92 );
93 } else if (completed) {
94 return (
95 <>
96 <Grid.Divider />
97 <Grid.Item
98 className="eth-tx-button"
99 full
100 as={RestartButton}
101 onClick={onReturn}>
102 Return
103 </Grid.Item>
104 <Grid.Divider />
105 {showReceipt && (
106 <TransactionReceipt
107 txHashes={txHashes}
108 finalCost={finalCost}
109 onClose={toggleShowReceipt}
110 />
111 )}
113 {!showReceipt && (
114 <Grid.Item
115 className="underline mv2 pointer"
116 full
117 onClick={toggleShowReceipt}>
118 View Receipt
119 </Grid.Item>
120 )}
121 </>
122 );
123 } else if (showBroadcastButton) {
124 return (
125 <Grid.Item
126 className="eth-tx-button"
127 full
128 as={ForwardButton}
129 solid
130 success
131 disabled={!canBroadcast}
132 onClick={() => broadcast()}>
133 Send Transaction
134 </Grid.Item>
135 );
136 } else if (showLoadingButton) {
137 return (
138 <Grid.Item
139 className="eth-tx-button"
140 full
141 as={ProgressButton}
142 success
143 disabled
144 progress={confirmationProgress}>
145 Sending Transaction
146 </Grid.Item>
147 );
148 } else {
149 return (
150 <Grid.Item
151 className="eth-tx-button"
152 full
153 as={GenerateButton}
154 onClick={generateAndSign}
155 disabled={!canSign}
156 loading={!canSign && initializing}>
157 {label}
158 </Grid.Item>
159 );
160 }
161 };
163 const numTxs = useMemo(() => (unsignedTransactions || []).length || 1, [
164 unsignedTransactions,
165 ]);
166 const maxCost = useMemo(
167 () =>
168 safeFromWei(
169 safeToWei(
170 toBN(gasLimit)
171 .mul(toBN(gasPrice))
172 .mul(toBN(numTxs)),
173 'gwei'
174 ),
175 'ether'
176 ),
177 [gasLimit, gasPrice, numTxs]
178 );
180 return (
181 <Grid className={cn(className, 'mt1')}>
182 <BridgeForm validate={validate} onValues={onValues}>
183 {() => (
184 <>
185 {showConfigureInput && (
186 <>
187 <Grid.Item
188 className="mt2 text eth-tx-button"
189 full
190 as={Flex}
191 row
192 justify="between">
193 <Flex.Item as={H5}>Gas Fee</Flex.Item>
194 <FeeDropdown setGasPrice={setGasPrice} />
195 </Grid.Item>
197 <Grid.Item
198 className="mt2 text eth-tx-button"
199 full
200 as={Flex}
201 row
202 justify="between">
203 <Flex.Item as={H5}>Max Transaction Cost</Flex.Item>
204 <Flex.Item as={H5}>{maxCost} ETH</Flex.Item>
205 </Grid.Item>
206 <Grid.Divider className="mt4" />
207 </>
208 )}
210 {renderPrimarySection()}
212 {error && (
213 <Grid.Item className="mv1 eth-tx-button" full as={ErrorText}>
214 {error.message}
215 </Grid.Item>
216 )}
218 {needFunds && (
219 <Grid.Item
220 className="mt3 eth-tx-button"
221 full
222 as={NeedFundsNotice}
223 {...needFunds}
224 />
225 )}
227 {showSignedTx && fakeSigned && (
228 <>
229 <Grid.Item className="mt2 eth-tx-button" full as={NoticeBox}>
230 Your wallet will sign the transaction upon sending it.
231 </Grid.Item>
232 </>
233 )}
234 </>
235 )}
236 </BridgeForm>
237 </Grid>
238 );
241function TransactionReceipt({ txHashes, finalCost, onClose }) {
242 const txUrls = useExploreTxUrls(txHashes);
243 return (
244 <>
245 <Grid.Item
246 className="pv2 black f5 eth-tx-button"
247 full
248 as={Flex}
249 justify="between">
250 <Flex.Item>Receipt</Flex.Item>
251 <Flex.Item onClick={onClose} className="underline pointer">
252 Close
253 </Flex.Item>
254 </Grid.Item>
255 <Grid.Item
256 className="pv2 black f5 eth-tx-button"
257 full
258 as={Flex}
259 justify="between">
260 <Flex.Item>Transaction Cost</Flex.Item>
261 <Flex.Item className="mono">{finalCost} ETH</Flex.Item>
262 </Grid.Item>
263 <Grid.Divider />
264 <Grid.Item className="pv4 eth-tx-button" full as={Flex} col>
265 <Flex.Item as={H5}>Receipt</Flex.Item>
267 {txHashes &&
268 txHashes.map((txHash, i) => (
269 <Flex.Item as={Flex} className="mv3">
270 <>
271 <Flex.Item
272 key={i}
273 flex
274 as="code"
275 className="f6 mono gray4 wrap ">
276 {txHash}
277 </Flex.Item>
278 <Flex.Item className="ml8" as={LinkButton} href={txUrls[i]}>
279 Etherscan↗
280 </Flex.Item>
281 </>
282 </Flex.Item>
283 ))}
284 </Grid.Item>
285 </>
286 );