on 2c54548 help sign in
bridge/src/components/InlineEthereumTransaction.js
359 lines 9.7 kB history raw
1import React, { useMemo, useCallback, useState } from 'react';
2import cn from 'classnames';
3import {
4 Grid,
5 ToggleInput,
6 ErrorText,
7 Flex,
8 LinkButton,
9 H5,
10} from 'indigo-react';
11import { toBN } from 'web3-utils';
13import { useExploreTxUrls } from 'lib/explorer';
14import { hexify } from 'lib/txn';
15import { safeFromWei, safeToWei } from 'lib/lib';
17import { composeValidator, buildCheckboxValidator } from 'form/validators';
18import BridgeForm from 'form/BridgeForm';
19import Condition from 'form/Condition';
21import { GenerateButton, ForwardButton, RestartButton } from './Buttons';
22import ProgressButton from './ProgressButton';
23import NeedFundsNotice from './NeedFundsNotice';
24import NoticeBox from './NoticeBox';
25import FeeDropdown from './L2/Dropdowns/FeeDropdown';
26import CopyButton from './copiable/CopyButton';
28import './InlineEthereumTransaction.scss';
30export default function InlineEthereumTransaction({
31 // from useEthereumTransaction.bind
32 initializing,
33 canSign,
34 generateAndSign,
35 signed,
36 fakeSigned,
37 broadcast,
38 broadcasted,
39 confirmed,
40 completed,
41 reset,
42 error,
43 gasPrice,
44 setGasPrice,
45 resetGasPrice,
46 txHashes,
47 nonce,
48 chainId,
49 needFunds,
50 signedTransactions,
51 confirmationProgress,
52 gasLimit,
53 unsignedTransactions,
54 finalCost,
56 // additional from parent
57 label = 'Generate & Sign Transaction',
58 className,
59 onReturn,
60}) {
61 // show receipt after successful broadcast
62 const [showReceipt, setShowReceipt] = useState(false);
63 const toggleShowReceipt = useCallback(() => setShowReceipt(!showReceipt), [
64 setShowReceipt,
65 showReceipt,
66 ]);
67 // show configure controls pre-broadcast
68 const showConfigureInput = !(signed || broadcasted || confirmed || completed);
69 // show the send/loading button while signed, broadcasting, or confirme
70 const showBroadcastButton = signed;
71 const showLoadingButton = broadcasted || confirmed;
72 const canBroadcast = signed && !needFunds;
73 // show signed tx only when signing (for offline usage)
74 const showSignedTx = signed;
76 const validate = useMemo(
77 () =>
78 composeValidator({
79 useAdvanced: buildCheckboxValidator(),
80 viewSigned: buildCheckboxValidator(),
81 }),
82 []
83 );
85 const onValues = useCallback(
86 ({ valid, values, form }) => {
87 if (!values.useAdvanced) {
88 resetGasPrice();
89 }
90 },
91 [resetGasPrice]
92 );
94 const renderPrimarySection = () => {
95 if (error) {
96 return (
97 <Grid.Item
98 className="eth-tx-button"
99 full
100 as={RestartButton}
101 solid
102 center
103 onClick={() => reset()}>
104 Reset Transaction
105 </Grid.Item>
106 );
107 } else if (completed) {
108 return (
109 <>
110 <Grid.Divider />
111 <Grid.Item
112 className="eth-tx-button"
113 full
114 as={RestartButton}
115 center
116 onClick={onReturn}>
117 Return
118 </Grid.Item>
119 <Grid.Divider />
120 {showReceipt && (
121 <TransactionReceipt
122 txHashes={txHashes}
123 finalCost={finalCost}
124 onClose={toggleShowReceipt}
125 />
126 )}
128 {!showReceipt && (
129 <Grid.Item
130 className="underline mv2 pointer"
131 full
132 onClick={toggleShowReceipt}>
133 View Receipt
134 </Grid.Item>
135 )}
136 </>
137 );
138 } else if (showBroadcastButton) {
139 return (
140 <Grid.Item
141 className="ph4 eth-tx-button"
142 full
143 as={ForwardButton}
144 solid
145 success
146 center
147 disabled={!canBroadcast}
148 onClick={() => broadcast()}>
149 Send Transaction
150 </Grid.Item>
151 );
152 } else if (showLoadingButton) {
153 return (
154 <Grid.Item
155 className="ph4 eth-tx-button"
156 full
157 as={ProgressButton}
158 center
159 success
160 disabled
161 progress={confirmationProgress}>
162 Sending Transaction
163 </Grid.Item>
164 );
165 } else {
166 return (
167 <Grid.Item
168 className="ph4 eth-tx-button"
169 full
170 as={GenerateButton}
171 center
172 onClick={generateAndSign}
173 disabled={!canSign}
174 loading={!canSign && initializing}>
175 {label}
176 </Grid.Item>
177 );
178 }
179 };
181 const serializedTxsHex = useMemo(
182 () =>
183 signedTransactions &&
184 signedTransactions.map(stx => hexify(stx.serialize())),
185 [signedTransactions]
186 );
188 const numTxs = useMemo(() => (unsignedTransactions || []).length || 1, [
189 unsignedTransactions,
190 ]);
191 const maxCost = useMemo(
192 () =>
193 safeFromWei(
194 safeToWei(
195 toBN(gasLimit)
196 .mul(toBN(gasPrice))
197 .mul(toBN(numTxs)),
198 'gwei'
199 ),
200 'ether'
201 ),
202 [gasLimit, gasPrice, numTxs]
203 );
205 return (
206 <Grid className={cn(className, 'mt1', 'inline-ethereum-tx')}>
207 <BridgeForm validate={validate} onValues={onValues}>
208 {() => (
209 <>
210 {showConfigureInput && (
211 <>
212 <Grid.Item
213 className="mt2 text eth-tx-button"
214 full
215 as={Flex}
216 row
217 justify="between">
218 <Flex.Item as={H5}>Gas Fee</Flex.Item>
219 <FeeDropdown setGasPrice={setGasPrice} />
220 </Grid.Item>
222 <Grid.Item
223 className="mt2 text eth-tx-button"
224 full
225 as={Flex}
226 row
227 justify="between">
228 <Flex.Item as={H5}>Max Transaction Cost</Flex.Item>
229 <Flex.Item as={H5}>{maxCost} ETH</Flex.Item>
230 </Grid.Item>
231 <Grid.Divider className="mt4" />
232 </>
233 )}
235 {renderPrimarySection()}
237 {error && (
238 <Grid.Item full as={ErrorText} className="mv1">
239 {error.message}
240 </Grid.Item>
241 )}
243 {needFunds && (
244 <Grid.Item
245 full
246 as={NeedFundsNotice}
247 className="mt3"
248 {...needFunds}
249 />
250 )}
252 {showSignedTx && fakeSigned && (
253 <>
254 <Grid.Item full as={NoticeBox} className="mt2">
255 Your wallet will sign the transaction upon sending it.
256 </Grid.Item>
257 </>
258 )}
260 {showSignedTx && !fakeSigned && (
261 <>
262 <Grid.Item
263 className="eth-tx-button text"
264 full
265 as={ToggleInput}
266 name="viewSigned"
267 label="Signed Transaction"
268 inverseLabel="Hide"
269 disabled={!showSignedTx}
270 />
271 <Condition when="viewSigned" is={true}>
272 <SignedTransactionList
273 serializedTxsHex={serializedTxsHex}
274 maxCost={maxCost}
275 nonce={nonce}
276 />
277 </Condition>
278 </>
279 )}
280 </>
281 )}
282 </BridgeForm>
283 </Grid>
284 );
287function SignedTransactionList({ serializedTxsHex, nonce, maxCost }) {
288 return serializedTxsHex.map((serializedTxHex, i) => (
289 <React.Fragment key={i}>
290 <Grid.Divider />
291 <Grid.Item full as={Flex} justify="between" className="pv4 black f5">
292 <Flex.Item>Nonce</Flex.Item>
293 <Flex.Item>{nonce + i}</Flex.Item>
294 </Grid.Item>
295 <Grid.Divider />
296 <Grid.Item full as={Flex} justify="between" className="pv4 black f5">
297 <Flex.Item>Transaction Cost</Flex.Item>
298 <Flex.Item>{maxCost} ETH</Flex.Item>
299 </Grid.Item>
300 <Grid.Divider />
301 <Grid.Item full as={Flex} justify="between" className="mt3 mb2">
302 <Flex.Item as={H5}>Signed Transaction Hex</Flex.Item>
303 <Flex.Item as={CopyButton} text={serializedTxHex} />
304 </Grid.Item>
305 <Grid.Item full as="code" className="mb4 f6 mono gray4 wrap">
306 {serializedTxHex}
307 </Grid.Item>
308 <Grid.Divider />
309 </React.Fragment>
310 ));
313function TransactionReceipt({ txHashes, finalCost, onClose }) {
314 const txUrls = useExploreTxUrls(txHashes);
315 return (
316 <>
317 <Grid.Item
318 className="pv2 black f5 eth-tx-button"
319 full
320 as={Flex}
321 justify="between">
322 <Flex.Item>Receipt</Flex.Item>
323 <Flex.Item onClick={onClose} className="underline pointer">
324 Close
325 </Flex.Item>
326 </Grid.Item>
327 <Grid.Item
328 className="pv2 black f5 eth-tx-button"
329 full
330 as={Flex}
331 justify="between">
332 <Flex.Item>Transaction Cost</Flex.Item>
333 <Flex.Item className="mono">{finalCost} ETH</Flex.Item>
334 </Grid.Item>
335 <Grid.Divider />
336 <Grid.Item className="pv4 eth-tx-button" full as={Flex} col>
337 <Flex.Item as={H5}>Receipt</Flex.Item>
339 {txHashes &&
340 txHashes.map((txHash, i) => (
341 <Flex.Item as={Flex} className="mv3">
342 <>
343 <Flex.Item
344 key={i}
345 flex
346 as="code"
347 className="f6 mono gray4 wrap ">
348 {txHash}
349 </Flex.Item>
350 <Flex.Item className="ml8" as={LinkButton} href={txUrls[i]}>
351 Etherscan↗
352 </Flex.Item>
353 </>
354 </Flex.Item>
355 ))}
356 </Grid.Item>
357 </>
358 );