@@ -1,598 +0,0 @@
1−import { Nothing, Just } from 'folktale/maybe';
2−import { Ok } from 'folktale/result';
3−import React from 'react';
4−import * as need from '../../lib/need';
6−import { Code, H3 } from './Base';
7−import { Button } from './Base';
8−import { CheckboxButton, Input, InnerLabel, InnerLabelDropdown } from './Base';
9−import { Warning } from './Base';
11−import { GAS_LIMITS } from '../../lib/constants';
12−import { BRIDGE_ERROR } from '../../lib/error';
13−import { ROUTE_NAMES } from '../../lib/routeNames';
14−import { compose } from '../../lib/lib';
15−import * as tank from '../../lib/tank';
17− sendSignedTransaction,
21−} from '../../lib/txn';
23−import { withNetwork } from '../../store/network';
24−import { withHistory } from '../../store/history';
25−import { withWallet } from '../../store/wallet';
26−import { withTxnCursor } from '../../store/txnCursor';
27−import { fromWei, toWei } from 'web3-utils';
29−const SUBMISSION_STATES = {
30− PROMPT: 'Send transaction',
31− PREPARING: 'Preparing...',
32− FUNDING: 'Finding transaction funding...',
33− SENDING: 'Sending transaction...',
36−class StatelessTransaction extends React.Component {
42− gasLimit: GAS_LIMITS.DEFAULT.toString(),
43− showGasDetails: false,
50− txStatus: SUBMISSION_STATES.PROMPT,
54− this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
55− this.setUserApproval = this.setUserApproval.bind(this);
56− this.setTxn = this.setTxn.bind(this);
57− this.setStx = this.setStx.bind(this);
58− this.sendTxn = this.sendTxn.bind(this);
59− this.setNonce = this.setNonce.bind(this);
60− this.setChainId = this.setChainId.bind(this);
61− this.setGasPrice = this.setGasPrice.bind(this);
62− this.setGasLimit = this.setGasLimit.bind(this);
63− this.rangeChange = this.rangeChange.bind(this);
64− this.toggleGasDetails = this.toggleGasDetails.bind(this);
65− this.handleChainUpdate = this.handleChainUpdate.bind(this);
68− componentDidMount() {
69− const { props } = this;
71− const addr = need.addressFromWallet(props.wallet);
73− props.web3.matchWith({
76− const validWeb3 = w3.value;
78− const getTxMetadata = [
79− validWeb3.eth.getTransactionCount(addr),
80− validWeb3.eth.net.getId(),
81− validWeb3.eth.getGasPrice(),
84− Promise.all(getTxMetadata).then(r => {
88− gasPrice: fromWei(r[2], 'gwei'),
91− this.setState({ ...txMetadata });
97− handleChainUpdate(chainId) {
98− if (chainId === 'custom') {
113− const { state } = this;
114− this.setState({ userApproval: !state.userApproval });
117− toggleGasDetails() {
119− showGasDetails: !this.state.showGasDetails,
126− userApproval: false,
131− this.setState({ txn });
134− createUnsignedTxn() {
135− const txn = this.props.createUnsignedTxn();
137− this.setState({ txn });
142− userApproval: false,
149− this.setState({ nonce });
153− setChainId(chainId) {
154− this.setState({ chainId });
158− setGasPrice(gasPrice) {
159− this.setState({ gasPrice });
164− this.setGasPrice(e.target.value);
167− setGasLimit(gasLimit) {
168− this.setState({ gasLimit });
174− userApproval: false,
180− const { props, state } = this;
181− const web3 = props.web3.value;
183− const stx = state.stx.matchWith({
184− Just: tx => tx.value,
186− throw new Error(BRIDGE_ERROR.MISSING_TXN);
190− if (props.beforeSend) {
192− this.setState({ txStatus: SUBMISSION_STATES.PREPARING });
193− const res = await props.beforeSend(stx);
194− if (res === false) throw new Error('beforeSend disallowed sending');
196− console.log('beforeSend error', e);
198− txStatus: SUBMISSION_STATES.PROMPT,
199− txError: Just('Something went wrong!'),
205− this.setState({ txStatus: SUBMISSION_STATES.FUNDING });
207− const rawTx = hexify(stx.serialize());
208− const cost = state.gasLimit * toWei(state.gasPrice, 'gwei');
210− const address = need.addressFromWallet(props.wallet);
211− let balance = await web3.eth.getBalance(address);
212− let hasBalance = balance >= cost;
214− let usedTank = false;
215− // if we need to, try and fund the transaction
217− usedTank = await this.ensureFundsFor(web3, address, cost, [rawTx]);
218− hasBalance = usedTank;
221− // if we still don't have sufficient balance, fail and tell the user
224− txStatus: SUBMISSION_STATES.PROMPT,
225− txError: Just(`Insufficient funds.
226− Address ${address} needs at least ${fromWei(cost.toString())} ETH,
227− currently has ${fromWei(balance.toString())} ETH.`),
230− // if we have the balance, proceed with submission
232− this.setState({ txStatus: SUBMISSION_STATES.SENDING });
234− sendSignedTransaction(web3, stx, usedTank)
236− props.setTxnCursor(Just(Ok(txHash)));
239− if (props.newNetworkSeed) {
240− props.setNetworkSeed(Just(props.newNetworkSeed));
241− routeData.promptKeyfile = true;
244− if (props.newRevision) {
245− props.setNetworkRevision(Just(props.newRevision));
248− props.history.popAndPush(ROUTE_NAMES.SENT_TRANSACTION, routeData);
252− txStatus: SUBMISSION_STATES.PROMPT,
259− // uses the gas tank to ensure funds, but if we have to ask the user
260− // for funding, just resolve instead of waiting
261− // returns true if the gas tank was used, false otherwise
262− ensureFundsFor(web3, address, cost, signedTxs) {
263− return new Promise((resolve, reject) => {
265− .ensureFundsFor(web3, null, address, cost, signedTxs, () => {
274− getChainTitle(chainId) {
281− '1337': 'Geth private chains - 1337',
285− return map[chainId];
291− title: this.getChainTitle('1'),
295− title: this.getChainTitle('2'),
299− title: this.getChainTitle('3'),
303− title: this.getChainTitle('4'),
307− title: this.getChainTitle('42'),
311− title: this.getChainTitle('1337'),
315− title: this.getChainTitle('custom'),
322− const { web3, canGenerate } = this.props;
348− const { state } = this;
350− const canSign = Just.hasInstance(txn);
351− const canApprove = Just.hasInstance(stx);
352− const canSend = Just.hasInstance(stx) && userApproval === true;
354− const chainOptions = this.getChainOptions();
356− const generateButtonColor = Nothing.hasInstance(txn) ? 'blue' : 'green';
358− const signerButtonColor = Nothing.hasInstance(stx) ? 'blue' : 'green';
360− const generateTxnButton = (
363− disabled={!canGenerate}
364− prop-color={generateButtonColor}
365− prop-size={'lg wide'}
366− onClick={createUnsignedTxn}>
367− {'Generate Transaction'}
371− const unsignedTxnDisplay = txn.matchWith({
375− <H3 className={'mt-8'}>{'Unsigned Transaction'}</H3>
376− <Code>{JSON.stringify(tx.value, null, 2)}</Code>
381− const gasPriceRangeDialogue = (
383− <div className="mt-12 flex space-between align-baseline">
385− <span>Gas Price:</span>
386− <span className="ml-4 text-700 text-sm">{gasPrice} gwei</span>
388− <div className="text-sm">
389− <span>Max transaction fee: </span>
390− <span className="text-700">
391− {(gasPrice * gasLimit) / 1000000000} eth
403− onChange={this.rangeChange}
406− <div className="flex space-between text-sm mb-8">
413− const toggleGasDetailsDialogue = (
414− <span className="text-link" onClick={toggleGasDetails}>
419− const gasPriceDialogue = (
421− className={'mono mt-4'}
423− prop-format={'innerLabel'}
425− onChange={setGasPrice}>
426− <InnerLabel>{'Gas Price (gwei)'}</InnerLabel>
430− const gasLimitDialogue = (
432− className={'mono mt-4'}
434− prop-format={'innerLabel'}
436− onChange={setGasLimit}>
437− <InnerLabel>{'Gas Limit'}</InnerLabel>
441− const nonceDialogue = (
443− className={'mono mt-4 mb-4'}
445− prop-format={'innerLabel'}
447− onChange={setNonce}>
448− <InnerLabel>{'Nonce'}</InnerLabel>
452− const chainDialogueTitle = customChain
454− : this.getChainTitle(chainId);
455− const chainDialogue = (
460− options={chainOptions}
461− handleUpdate={handleChainUpdate}
462− currentSelectionTitle={chainDialogueTitle}
466− const customChainDialogue = !customChain ? null : (
468− className={'mono mt-4 mb-8'}
470− prop-format={'innerLabel'}
472− onChange={setChainId}>
473− <InnerLabel>{'Chain ID'}</InnerLabel>
477− const onlineParamsDialogue = web3.matchWith({
483− {customChainDialogue}
488− const signTxnButton = (
492− prop-size={'lg wide'}
493− prop-color={signerButtonColor}
494− onClick={async () =>
497− await signTransaction({
500− wallet: this.props.wallet.value,
506− {'Sign Transaction'}
510− const signedTxnDisplay = stx.matchWith({
514− <H3 className={'mt-8'}>{'Signed Transaction'}</H3>
515− <Code>{JSON.stringify(renderSignedTx(tx.value), null, 2)}</Code>
520− const confirmButton = (
523− disabled={!canApprove}
524− onToggle={setUserApproval}
525− state={userApproval}
526− label="I approve this transaction and wish to send."
530− const sending = txStatus !== SUBMISSION_STATES.PROMPT;
531− const sendTxnSpinner = sending ? '' : 'hide';
532− const sendTxnButton = (
534− prop-size={'xl wide'}
536− disabled={!canSend || sending}
538− <span className="relative">
539− <span className={`btn-spinner ${sendTxnSpinner}`} />
545− const sendDialogue = web3.matchWith({
555− const txnErrorDialogue = Nothing.hasInstance(state.txError) ? (
558− <Warning className={'mt-8'}>
559− <H3 style={{ marginTop: 0, paddingTop: 0 }}>
560− {'There was an error sending your transaction.'}
562− {state.txError.value}
569− {unsignedTxnDisplay}
571− {gasPriceRangeDialogue}
572− {toggleGasDetailsDialogue}
574− {showGasDetails && (
580− {onlineParamsDialogue}
593−export default compose(
598−)(StatelessTransaction);