@@ -1,4 +1,4 @@
1−import React, { useMemo, useCallback } from 'react';
1+import React, { useMemo, useCallback, useState } from 'react';
2import cn from 'classnames';
2import cn from 'classnames';
@@ -8,11 +8,16 @@ import {
8 LinkButton,
8 LinkButton,
11} from 'indigo-react';
12} from 'indigo-react';
13+import { toBN } from 'web3-utils';
15+import { ReactComponent as InfoIcon } from 'assets/info.svg';
13import { useExploreTxUrls } from 'lib/explorer';
17import { useExploreTxUrls } from 'lib/explorer';
14import { hexify } from 'lib/txn';
18import { hexify } from 'lib/txn';
15import pluralize from 'lib/pluralize';
19import pluralize from 'lib/pluralize';
20+import { safeFromWei, safeToWei } from 'lib/lib';
17import { composeValidator, buildCheckboxValidator } from 'form/validators';
22import { composeValidator, buildCheckboxValidator } from 'form/validators';
18import BridgeForm from 'form/BridgeForm';
23import BridgeForm from 'form/BridgeForm';
@@ -45,6 +50,8 @@ export default function InlineEthereumTransaction({
45 needFunds,
50 needFunds,
46 signedTransactions,
51 signedTransactions,
47 confirmationProgress,
52 confirmationProgress,
54+ unsignedTransactions,
49 // additional from parent
56 // additional from parent
50 label = 'Generate & Sign Transaction',
57 label = 'Generate & Sign Transaction',
@@ -52,7 +59,11 @@ export default function InlineEthereumTransaction({
54 // show receipt after successful broadcast
61 // show receipt after successful broadcast
55− const showReceipt = broadcasted || confirmed || completed;
62+ const [showReceipt, setShowReceipt] = useState(false);
63+ const toggleShowReceipt = useCallback(() => setShowReceipt(!showReceipt), [
56 // show configure controls pre-broadcast
67 // show configure controls pre-broadcast
57 const showConfigureInput = !(signed || broadcasted || confirmed || completed);
68 const showConfigureInput = !(signed || broadcasted || confirmed || completed);
58 // show the send/loading button while signed, broadcasting, or confirme
69 // show the send/loading button while signed, broadcasting, or confirme
@@ -62,6 +73,12 @@ export default function InlineEthereumTransaction({
62 // show signed tx only when signing (for offline usage)
73 // show signed tx only when signing (for offline usage)
63 const showSignedTx = signed;
74 const showSignedTx = signed;
76+ const [showGasDetails, _setShowGasDetails] = useState(false);
77+ const toggleGasDetails = useCallback(
78+ () => _setShowGasDetails(!showGasDetails),
79+ [_setShowGasDetails, showGasDetails]
65 const validate = useMemo(
82 const validate = useMemo(
67 composeValidator({
84 composeValidator({
@@ -88,11 +105,7 @@ export default function InlineEthereumTransaction({
88 </Grid.Item>
105 </Grid.Item>
90 } else if (completed) {
107 } else if (completed) {
91− return (
108+ return undefined;
92− <Grid.Item full className="pv4 black f5">
96 } else if (showBroadcastButton) {
109 } else if (showBroadcastButton) {
98 <Grid.Item
111 <Grid.Item
@@ -137,6 +150,29 @@ export default function InlineEthereumTransaction({
137 [signedTransactions]
150 [signedTransactions]
153+ const numTxs = useMemo(() => (unsignedTransactions || []).length || 1, [
154+ unsignedTransactions,
156+ const maxCost = useMemo(
161+ .mul(toBN(gasPrice))
166+ [gasLimit, gasPrice, numTxs]
169+ const gasInfo = useMemo(() => {
170+ const extra = numTxs === 1 ? '' : `* ${numTxs} TXN`;
171+ return showGasDetails
172+ ? `${gasPrice} Gwei * ${gasLimit} ${extra} = ${maxCost} ETH`
174+ }, [showGasDetails, maxCost, gasPrice, gasLimit, numTxs]);
141 <Grid className={cn(className, 'mt1')}>
177 <Grid className={cn(className, 'mt1')}>
142 <BridgeForm validate={validate} onValues={onValues}>
178 <BridgeForm validate={validate} onValues={onValues}>
@@ -172,6 +208,7 @@ export default function InlineEthereumTransaction({
174 <Condition when="useAdvanced" is={true}>
210 <Condition when="useAdvanced" is={true}>
211+ <Grid.Item full className="mb2"></Grid.Item>
175 <Grid.Divider />
212 <Grid.Divider />
176 <Grid.Item
213 <Grid.Item
@@ -180,8 +217,16 @@ export default function InlineEthereumTransaction({
180 justify="between"
217 justify="between"
181 className="mt2">
218 className="mt2">
182 <Flex.Item as={H5}>Gas Price</Flex.Item>
219 <Flex.Item as={H5}>Gas Price</Flex.Item>
183− <Flex.Item as={H5}>{gasPrice} Gwei</Flex.Item>
221+ <Flex.Item as={H5}>
225+ onClick={toggleGasDetails}
184 </Grid.Item>
228 </Grid.Item>
185 {/* TODO(shrugs): move to indigo/RangeInput */}
230 {/* TODO(shrugs): move to indigo/RangeInput */}
186 <Grid.Item
231 <Grid.Item
@@ -228,21 +273,28 @@ export default function InlineEthereumTransaction({
234− <HashReceiptList txHashes={txHashes} />
240 {completed && (
276 {completed && (
242 <Grid.Item full as={RestartButton} onClick={onReturn}>
278 <Grid.Item full as={RestartButton} onClick={onReturn}>
244 </Grid.Item>
280 </Grid.Item>
245 <Grid.Divider />
281 <Grid.Divider />
286+ onClose={toggleShowReceipt}
292+ className="underline mv2 pointer"
294+ onClick={toggleShowReceipt}>
@@ -278,34 +330,42 @@ function SignedTransactionList({ serializedTxsHex, nonce, gasPrice }) {
281−function HashReceiptList({ txHashes }) {
333+function TransactionReceipt({ txHashes, maxCost, onClose }) {
282 const txUrls = useExploreTxUrls(txHashes);
334 const txUrls = useExploreTxUrls(txHashes);
283− const header = pluralize(txHashes.length, 'Hash', 'Hashes');
337+ <Grid.Item full as={Flex} justify="between" className="pv2 black f5">
338+ <Flex.Item>Receipt</Flex.Item>
339+ <Flex.Item onClick={onClose} className="underline pointer">
343+ <Grid.Item full as={Flex} justify="between" className="pv2 black f5">
344+ <Flex.Item>Gas Price</Flex.Item>
345+ <Flex.Item className="mono">{maxCost} ETH</Flex.Item>
286 <Grid.Divider />
347 <Grid.Divider />
287 <Grid.Item full as={Flex} col className="pv4">
348 <Grid.Item full as={Flex} col className="pv4">
288− <Flex.Item as={H5}>Transaction {header}</Flex.Item>
349+ <Flex.Item as={H5}>Receipt</Flex.Item>
290 {txHashes &&
351 {txHashes &&
291 txHashes.map((txHash, i) => (
352 txHashes.map((txHash, i) => (
292− <Flex.Item as={Flex}>
353+ <Flex.Item as={Flex} className="mv3">
294 <Flex.Item
355 <Flex.Item
297 as="code"
358 as="code"
298− className="f6 mono gray4 wrap">
359+ className="f6 mono gray4 wrap ">
300 </Flex.Item>
361 </Flex.Item>
301− <Flex.Item as={LinkButton} href={txUrls[i]}>
362+ <Flex.Item className="ml8" as={LinkButton} href={txUrls[i]}>
302 Etherscan↗
363 Etherscan↗
303 </Flex.Item>
364 </Flex.Item>
305 </Flex.Item>
366 </Flex.Item>
307 </Grid.Item>
368 </Grid.Item>