1import React, { useMemo, useCallback, useState } from 'react';
2import cn from 'classnames';
12import { toBN } from 'web3-utils';
14import { ReactComponent as InfoIcon } from 'assets/info.svg';
16import { useExploreTxUrls } from 'lib/explorer';
17import { hexify } from 'lib/txn';
18import { safeFromWei, safeToWei } from 'lib/lib';
20import { composeValidator, buildCheckboxValidator } from 'form/validators';
21import BridgeForm from 'form/BridgeForm';
22import Condition from 'form/Condition';
24import { GenerateButton, ForwardButton, RestartButton } from './Buttons';
25import CopyButton from './copiable/CopyButton';
26import ProgressButton from './ProgressButton';
27import { convertToInt } from 'lib/convertToInt';
28import NeedFundsNotice from './NeedFundsNotice';
29import NoticeBox from './NoticeBox';
31import './InlineEthereumTransaction.scss';
33export default function InlineEthereumTransaction({
34 // from useEthereumTransaction.bind
59 // additional from parent
60 label = 'Generate & Sign Transaction',
64 // show receipt after successful broadcast
65 const [showReceipt, setShowReceipt] = useState(false);
66 const toggleShowReceipt = useCallback(() => setShowReceipt(!showReceipt), [
70 // show configure controls pre-broadcast
71 const showConfigureInput = !(signed || broadcasted || confirmed || completed);
72 // show the send/loading button while signed, broadcasting, or confirme
73 const showBroadcastButton = signed;
74 const showLoadingButton = broadcasted || confirmed;
75 const canBroadcast = signed && !needFunds;
76 // show signed tx only when signing (for offline usage)
77 const showSignedTx = signed;
79 const [showGasDetails, _setShowGasDetails] = useState(false);
80 const toggleGasDetails = useCallback(
81 () => _setShowGasDetails(!showGasDetails),
82 [_setShowGasDetails, showGasDetails]
85 const validate = useMemo(
88 useAdvanced: buildCheckboxValidator(),
89 viewSigned: buildCheckboxValidator(),
94 const onValues = useCallback(
95 ({ valid, values, form }) => {
96 if (!values.useAdvanced) {
103 const renderPrimarySection = () => {
107 className="eth-tx-button"
111 onClick={() => reset()}>
115 } else if (completed) {
120 className="eth-tx-button"
130 finalCost={finalCost}
131 onClose={toggleShowReceipt}
137 className="underline mv2 pointer"
139 onClick={toggleShowReceipt}>
145 } else if (showBroadcastButton) {
148 className="eth-tx-button"
153 disabled={!canBroadcast}
154 onClick={() => broadcast()}>
158 } else if (showLoadingButton) {
161 className="eth-tx-button"
166 progress={confirmationProgress}>
173 className="eth-tx-button"
176 onClick={generateAndSign}
178 loading={!canSign && initializing}>
185 const serializedTxsHex = useMemo(
187 signedTransactions &&
188 signedTransactions.map(stx => hexify(stx.serialize())),
192 const numTxs = useMemo(() => (unsignedTransactions || []).length || 1, [
193 unsignedTransactions,
195 const maxCost = useMemo(
206 [gasLimit, gasPrice, numTxs]
209 const gasInfo = useMemo(() => {
210 const extra = numTxs === 1 ? '' : `* ${numTxs} txs`;
211 return showGasDetails
212 ? `${gasPrice} Gwei * ${gasLimit} ${extra} = ${maxCost} ETH`
214 }, [showGasDetails, maxCost, gasPrice, gasLimit, numTxs]);
217 <Grid className={cn(className, 'mt1')}>
218 <BridgeForm validate={validate} onValues={onValues}>
221 {renderPrimarySection()}
224 <Grid.Item className="mv1 eth-tx-button" full as={ErrorText}>
231 className="mt3 eth-tx-button"
238 {showConfigureInput && (
241 className="eth-tx-button"
246 inverseLabel="Back to Defaults"
248 disabled={!showConfigureInput || initializing}
251 <Condition when="useAdvanced" is={true}>
252 <Grid.Item className="mb2 eth-tx-button" full></Grid.Item>
255 className="mt2 eth-tx-button"
260 <Flex.Item as={H5}>Transaction Fee</Flex.Item>
266 onClick={toggleGasDetails}
271 {/* TODO(shrugs): move to indigo/RangeInput */}
273 className="eth-tx-button"
281 setGasPrice(convertToInt(e.target.value, 10))
285 className="f6 mt1 eth-tx-button"
290 <Flex.Item as={Text}>Cheap</Flex.Item>
291 <Flex.Item as={Text}>Fast</Flex.Item>
293 <Grid.Divider className="mt4" />
298 {showSignedTx && fakeSigned && (
300 <Grid.Item className="mt2 eth-tx-button" full as={NoticeBox}>
301 Your wallet will sign the transaction upon sending it.
306 {showSignedTx && !fakeSigned && (
309 className="eth-tx-button"
313 label="Signed Transaction"
315 disabled={!showSignedTx}
317 <Condition when="viewSigned" is={true}>
318 <SignedTransactionList
319 serializedTxsHex={serializedTxsHex}
333function SignedTransactionList({ serializedTxsHex, nonce, maxCost }) {
334 return serializedTxsHex.map((serializedTxHex, i) => (
335 <React.Fragment key={i}>
338 className="pv4 black f5 eth-tx-button"
342 <Flex.Item>Nonce</Flex.Item>
343 <Flex.Item>{nonce + i}</Flex.Item>
347 className="pv4 black f5 eth-tx-button"
351 <Flex.Item>Transaction Cost</Flex.Item>
352 <Flex.Item>{maxCost} ETH</Flex.Item>
356 className="mt3 mb2 eth-tx-button"
360 <Flex.Item as={H5}>Signed Transaction Hex</Flex.Item>
361 <Flex.Item as={CopyButton} text={serializedTxHex} />
364 className="mb4 f6 mono gray4 wrap eth-tx-button"
374function TransactionReceipt({ txHashes, finalCost, onClose }) {
375 const txUrls = useExploreTxUrls(txHashes);
379 className="pv2 black f5 eth-tx-button"
383 <Flex.Item>Receipt</Flex.Item>
384 <Flex.Item onClick={onClose} className="underline pointer">
389 className="pv2 black f5 eth-tx-button"
393 <Flex.Item>Transaction Cost</Flex.Item>
394 <Flex.Item className="mono">{finalCost} ETH</Flex.Item>
397 <Grid.Item className="pv4 eth-tx-button" full as={Flex} col>
398 <Flex.Item as={H5}>Receipt</Flex.Item>
401 txHashes.map((txHash, i) => (
402 <Flex.Item as={Flex} className="mv3">
408 className="f6 mono gray4 wrap ">
411 <Flex.Item className="ml8" as={LinkButton} href={txUrls[i]}>
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list