Merge pull request #166 from status-im/features/improve-gas-estimator

Add gas estimator to the functions that need it
This commit is contained in:
Iuri Matias 2018-10-25 15:43:39 -04:00 committed by GitHub
commit 87dfffef4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 55 deletions

View File

@ -0,0 +1,21 @@
.contract-function-container .collapse.show .card-body {
min-height: 65px;
}
.contract-function-container .contract-function-button {
position: absolute;
bottom: 15px;
right: 15px;
}
.contract-function-container .gas-price-form #gasPrice {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.contract-function-container .gas-price-form button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border: 0;
box-shadow: none !important;
}

View File

@ -17,12 +17,16 @@ import {
ListGroup,
ListGroupItem
} from "reactstrap";
import GasStationContainer from "../containers/GasStationContainer";
import {formatContractForDisplay} from '../utils/presentation';
import FontAwesome from 'react-fontawesome';
import "./ContractOverview.css";
class ContractFunction extends Component {
constructor(props) {
super(props);
this.state = {inputs: {}, optionsCollapse: false, functionCollapse: false};
this.state = {inputs: {}, optionsCollapse: false, functionCollapse: false, gasPriceCollapse: false};
}
static isPureCall(method) {
@ -49,11 +53,18 @@ class ContractFunction extends Component {
}
handleChange(e, name) {
let newInputs = this.state.inputs;
const newInputs = this.state.inputs;
newInputs[name] = e.target.value;
this.setState({inputs: newInputs});
}
autoSetGasPrice(e) {
e.preventDefault();
const newInputs = this.state.inputs;
newInputs.gasPrice = this.gasStation.getCurrentGas();
this.setState({inputs: newInputs});
}
handleCall(e) {
e.preventDefault();
this.props.postContractFunction(this.props.contractProfile.name, this.props.method.name, this.inputsAsArray(), this.state.inputs.gasPrice * 1000000000);
@ -65,66 +76,85 @@ class ContractFunction extends Component {
toggleOptions() {
this.setState({
optionsCollapse: !this.state.optionsCollapse,
optionsCollapse: !this.state.optionsCollapse
});
}
toggleGasPrice() {
this.setState({
gasPriceCollapse: !this.state.gasPriceCollapse
});
}
toggleFunction() {
this.setState({
functionCollapse: !this.state.functionCollapse,
functionCollapse: !this.state.functionCollapse
});
}
render() {
return (
<Card>
<Card className="contract-function-container">
<CardHeader>
<CardTitle className="collapsable" onClick={() => this.toggleFunction()}>
{ContractFunction.isPureCall(this.props.method) &&
<button className="btn btn-warning btn-sm float-right">call</button>
<button className="btn btn-warning btn-sm float-right" onClick={(e) => this.handleCall(e)}>call</button>
}
{ContractFunction.isEvent(this.props.method) &&
<button className="btn btn-info btn-sm float-right">event</button>
<button className="btn btn-info btn-sm float-right">event</button>
}
{this.props.method.name}({this.props.method.inputs.map(input => input.name).join(', ')})
</CardTitle>
</CardHeader>
<Collapse isOpen={this.state.functionCollapse}>
<Collapse isOpen={this.state.functionCollapse} className="relative">
<CardBody>
<Form action="" method="post" inline>
{this.props.method.inputs.map(input => (
<FormGroup key={input.name} className="pr-1">
<Label for={input.name} className="pr-1">{input.name}: </Label>
<Input name={input.name} id={input.name} placeholder={input.type} onChange={(e) => this.handleChange(e, input.name)}/>
</FormGroup>
))}
<Form method="post" inline>
{this.props.method.inputs.map(input => (
<FormGroup key={input.name}>
<Label for={input.name} className="mr-2 font-weight-bold">{input.name}</Label>
<Input name={input.name} id={input.name} placeholder={input.type}
onChange={(e) => this.handleChange(e, input.name)}/>
</FormGroup>
))}
</Form>
{!ContractFunction.isPureCall(this.props.method) &&
<Col xs={12} className="my-2">
<Row>
<strong className="collapsable" onClick={() => this.toggleOptions()}>
<i className={this.state.optionsCollapse ? 'fa fa-caret-down' : 'fa fa-caret-right'}/>Advanced Options
</strong>
<Col xs={12} className="my-2">
<Row>
<Collapse isOpen={this.state.optionsCollapse}>
<Form action="" method="post" inline>
<FormGroup key="gasPrice" className="pr-1">
<Label for="gasPrice" className="pr-1">Gas Price (in GWei)(optional)</Label>
<Input name="gasPrice" id="gasPrice" onChange={(e) => this.handleChange(e, 'gasPrice')}/>
</FormGroup>
</Form>
</Collapse>
</Row>
</Col>
</Row>
</Col>
<Col xs={12} className="mt-3">
<Row>
<strong className="collapsable" onClick={() => this.toggleOptions()}>
<FontAwesome name={this.state.optionsCollapse ? 'caret-down' : 'caret-right'} className="mr-2"/>
Advanced Options
</strong>
</Row>
<Row>
<Collapse isOpen={this.state.optionsCollapse} className="pl-3">
<Form method="post" inline className="gas-price-form ">
<FormGroup key="gasPrice">
<Label for="gasPrice" className="mr-2">Gas Price (in GWei)(optional)</Label>
<Input name="gasPrice" id="gasPrice" placeholder="uint256"
value={this.state.inputs.gasPrice || ''}
onChange={(e) => this.handleChange(e, 'gasPrice')}/>
<Button onClick={(e) => this.autoSetGasPrice(e)} title="Automatically set the gas price to what is currently in the estimator (default: safe low)">
Auto-set
</Button>
</FormGroup>
</Form>
<p className="collapsable mb-2" onClick={() => this.toggleGasPrice()}>
<FontAwesome name={this.state.gasPriceCollapse ? 'caret-down' : 'caret-right'} className="mr-2"/>
Gas price estimator
</p>
<Collapse isOpen={this.state.gasPriceCollapse}>
<GasStationContainer ref={instance => {
if (instance) this.gasStation = instance.getWrappedInstance();
}}/>
</Collapse>
</Collapse>
</Row>
</Col>
}
<div align="right">
<Button color="primary" disabled={this.callDisabled()} onClick={(e) => this.handleCall(e)}>
{this.buttonTitle()}
</Button>
</div>
<Button className="contract-function-button" color="primary" disabled={this.callDisabled()}
onClick={(e) => this.handleCall(e)}>
{this.buttonTitle()}
</Button>
</CardBody>
</Collapse>
{this.props.contractFunctions && this.props.contractFunctions.length > 0 && <CardFooter>

View File

@ -1,4 +1,4 @@
.copy-to-clipboard {
#root .copy-to-clipboard {
position: absolute;
right: 0;
top: 0;

View File

@ -49,6 +49,10 @@ class GasStation extends Component {
return formattedStats;
}
getCurrentGas() {
return this.getGasOracleFormatted()[this.state.gasOracleSliderIndex].gasPrice / this.PRICE_UNIT_DIVIDER;
}
gasSliderChange(e, name) {
this.setState({
[name]: e.target.value
@ -91,7 +95,7 @@ class GasStation extends Component {
}
return <Row>
<Col>
<Card>
<Card className="mb-0">
<CardHeader>
<CardTitle>
Gas Price Estimator

View File

@ -5,7 +5,6 @@ import PropTypes from 'prop-types';
import {contractProfile as contractProfileAction, contractFunction as contractFunctionAction} from '../actions';
import ContractOverview from '../components/ContractOverview';
import DataWrapper from "../components/DataWrapper";
import GasStationContainer from "../containers/GasStationContainer";
import {getContractProfile, getContractFunctions} from "../reducers/selectors";
class ContractOverviewContainer extends Component {
@ -18,14 +17,10 @@ class ContractOverviewContainer extends Component {
<DataWrapper shouldRender={this.props.contractProfile !== undefined}
{...this.props}
render={({contractProfile, contractFunctions, postContractFunction}) => (
<React.Fragment>
<ContractOverview contractProfile={contractProfile}
contractFunctions={contractFunctions}
contract={this.props.contract}
postContractFunction={postContractFunction}/>
<GasStationContainer/>
</React.Fragment>
<ContractOverview contractProfile={contractProfile}
contractFunctions={contractFunctions}
contract={this.props.contract}
postContractFunction={postContractFunction}/>
)}/>
);
}

View File

@ -1,7 +1,6 @@
import PropTypes from "prop-types";
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {withRouter} from "react-router-dom";
import GasStation from '../components/GasStation';
import {stopGasOracle, listenToGasOracle, gasOracle as ethGasAction, blocks as blocksAction} from "../actions";
import DataWrapper from "../components/DataWrapper";
@ -18,10 +17,14 @@ class GasStationContainer extends Component {
this.props.stopGasOracle();
}
getCurrentGas() {
return this.gasStation.getCurrentGas();
}
render() {
return <DataWrapper shouldRender={Boolean(this.props.gasOracleStats && Object.keys(this.props.gasOracleStats).length && this.props.lastBlock)}
{...this.props} render={({lastBlock, gasOracleStats}) => (
<GasStation gasOracleStats={gasOracleStats} lastBlock={lastBlock}/>
<GasStation gasOracleStats={gasOracleStats} lastBlock={lastBlock} ref={instance => { this.gasStation = instance; }}/>
)}/>;
}
@ -43,12 +46,14 @@ function mapStateToProps(state, _props) {
};
}
export default withRouter(connect(
export default connect(
mapStateToProps,
{
fetchEthGas: ethGasAction.request,
fetchBlocks: blocksAction.request,
listenToGasOracle,
stopGasOracle
}
)(GasStationContainer));
},
null,
{ withRef: true }
)(GasStationContainer);