conflict in saga

This commit is contained in:
Jonathan Rainville 2018-08-31 16:48:59 -04:00 committed by Pascal Precht
parent 9d262e6a25
commit 5690c2e054
No known key found for this signature in database
GPG Key ID: 0EE28D8D6FD85D7D
7 changed files with 50 additions and 93 deletions

View File

@ -200,17 +200,10 @@ export const files = {
failure: (error) => action(FILES[FAILURE], {error}) failure: (error) => action(FILES[FAILURE], {error})
}; };
export const ETH_GAS = createRequestTypes('ETH_GAS');
export const ethGas = {
request: () => action(ETH_GAS[REQUEST]),
success: (gasStats) => action(ETH_GAS[SUCCESS], {gasStats: [gasStats]}),
failure: (error) => action(ETH_GAS[FAILURE], {error})
};
export const GAS_ORACLE = createRequestTypes('GAS_ORACLE'); export const GAS_ORACLE = createRequestTypes('GAS_ORACLE');
export const gasOracle = { export const gasOracle = {
request: () => action(GAS_ORACLE[REQUEST]), request: () => action(GAS_ORACLE[REQUEST]),
success: (gasOracleStats) => action(GAS_ORACLE[SUCCESS], {gasOracleStats}), success: (gasOracleStats) => action(GAS_ORACLE[SUCCESS], {gasOracleStats: [gasOracleStats]}),
failure: (error) => action(GAS_ORACLE[FAILURE], {error}) failure: (error) => action(GAS_ORACLE[FAILURE], {error})
}; };

View File

@ -109,7 +109,7 @@ export function fetchContractFile(payload) {
} }
export function getEthGasAPI() { export function getEthGasAPI() {
return get('/json/ethgasAPI.json', {}, 'https://ethgasstation.info'); return get('/blockchain/gas/oracle', {});
} }
export function fetchLastFiddle() { export function fetchLastFiddle() {

View File

@ -4,8 +4,8 @@ import {connect} from 'react-redux';
import {withRouter} from "react-router-dom"; import {withRouter} from "react-router-dom";
import {Card, Form, Grid, StampCard, Stamp} from 'tabler-react'; import {Card, Form, Grid, StampCard, Stamp} from 'tabler-react';
import {CopyToClipboard} from 'react-copy-to-clipboard'; import {CopyToClipboard} from 'react-copy-to-clipboard';
import {listenToGasOracle} from "../actions"; import {listenToGasOracle, gasOracle as ethGasAction} from "../actions";
import {getOracleGasStats} from "../reducers/selectors"; import {getGasStats, getOracleGasStats} from "../reducers/selectors";
const COLORS = { const COLORS = {
good: 'green', good: 'green',
@ -16,42 +16,20 @@ const COLORS = {
class GasStation extends Component { class GasStation extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
if (!props.gasStats) {
return console.error('gasStats is a needed Prop for GasStation');
}
this.state = { this.state = {
gasSliderIndex: 0,
gasOracleSliderIndex: 0, gasOracleSliderIndex: 0,
copied: false copied: false
}; };
this.formattedGasStats = GasStation.formatGasStats(props.gasStats);
} }
componentDidMount() { componentDidMount() {
this.props.fetchEthGas();
if (!this.props.gasOracleStats.length) { if (!this.props.gasOracleStats.length) {
this.props.listenToGasOracle(); this.props.listenToGasOracle();
} }
} }
static formatGasStats(gasStats) {
const {
fast, speed, fastest, avgWait, fastWait, blockNum, safeLowWait,
block_time, fastestWait, safeLow, average
} = gasStats;
return {
average: {price: average, wait: avgWait},
blockTime: block_time,
blockNum,
speed,
gasSteps: [
{price: safeLow, wait: safeLowWait},
{price: fast, wait: fastWait},
{price: fastest, wait: fastestWait}
]
};
}
getGasOracleFormatted() { getGasOracleFormatted() {
const gasPrices = Object.keys(this.props.gasOracleStats); const gasPrices = Object.keys(this.props.gasOracleStats);
if (!gasPrices.length) { if (!gasPrices.length) {
@ -59,9 +37,11 @@ class GasStation extends Component {
} }
return gasPrices.map(gasPrice => { return gasPrices.map(gasPrice => {
return { return {
gasPrice: gasPrice, gasPrice,
wait: this.props.gasOracleStats[gasPrice].averageWait wait: this.props.gasOracleStats[gasPrice].averageWait
}; };
}).sort((a, b) => {
return a.gasPrice - b.gasPrice;
}); });
} }
@ -72,35 +52,38 @@ class GasStation extends Component {
} }
static getColorForWait(wait) { static getColorForWait(wait) {
if (wait <= 1) { if (wait <= 60) {
return COLORS.good; return COLORS.good;
} }
if (wait <= 3) { if (wait <= 180) {
return COLORS.medium; return COLORS.medium;
} }
return COLORS.bad; return COLORS.bad;
} }
static getColorForPrice(gasPrice) { static getColorForPrice(gasPrice) {
if (gasPrice <= 20) { if (gasPrice <= 20000000000) {
return COLORS.good; return COLORS.good;
} }
if (gasPrice <= 40) { if (gasPrice <= 40000000000) {
return COLORS.medium; return COLORS.medium;
} }
return COLORS.bad; return COLORS.bad;
} }
render() { render() {
const currentGasStep = this.formattedGasStats.gasSteps[this.state.gasSliderIndex];
const formattedGasOracleStats = this.getGasOracleFormatted(); const formattedGasOracleStats = this.getGasOracleFormatted();
const currentGasStep = formattedGasOracleStats[this.state.gasOracleSliderIndex];
if (!formattedGasOracleStats.length) {
return '';
}
return <Grid.Row> return <Grid.Row>
<Grid.Col> <Grid.Col>
<Card> <Card>
<Card.Header> <Card.Header>
<Card.Title>Gas Price Estimator (for Mainnet)</Card.Title> <Card.Title>Gas Price Estimator</Card.Title>
<Card.Options> <Card.Options>
<CopyToClipboard text={currentGasStep.price / 10} <CopyToClipboard text={currentGasStep.gasPrice / 1000000000}
onCopy={() => this.setState({copied: true})} onCopy={() => this.setState({copied: true})}
title="Copy gas price to clipboard"> title="Copy gas price to clipboard">
<span><Stamp color="blue" icon="copy"/></span> <span><Stamp color="blue" icon="copy"/></span>
@ -112,40 +95,31 @@ class GasStation extends Component {
{this.state.copied && <p>Copied Gas Price</p>} {this.state.copied && <p>Copied Gas Price</p>}
<Grid.Row cards={true}> <Grid.Row cards={true}>
<Grid.Col lg={6} md={6} sm={12}> <Grid.Col lg={6} md={6} sm={12}>
<StampCard icon="sliders" color={GasStation.getColorForPrice(currentGasStep.price)}> <StampCard icon="sliders" color={GasStation.getColorForPrice(currentGasStep.gasPrice)}>
{currentGasStep.price / 10} GWei {currentGasStep.gasPrice / 1000000000} Wei
</StampCard> </StampCard>
</Grid.Col> </Grid.Col>
<Grid.Col lg={6} md={6} sm={12}> <Grid.Col lg={6} md={6} sm={12}>
<StampCard icon="clock" color={GasStation.getColorForWait(currentGasStep.wait)}> <StampCard icon="clock" color={GasStation.getColorForWait(currentGasStep.wait)}>
{currentGasStep.wait} minutes {currentGasStep.wait} seconds
</StampCard> </StampCard>
</Grid.Col> </Grid.Col>
</Grid.Row> </Grid.Row>
<Form.Group> <Form.Group>
<input type="range" className="slider"
max={this.formattedGasStats.gasSteps.length - 1}
min={0}
step={1}
value={this.state.gasSliderIndex}
onChange={(e) => this.gasSliderChange(e, 'gasSliderIndex')}
/>
{formattedGasOracleStats.length > 0 &&
<input type="range" className="slider" <input type="range" className="slider"
max={formattedGasOracleStats.length - 1} max={formattedGasOracleStats.length - 1}
min={0} min={0}
step={1} step={1}
value={this.state.gasOracleSliderIndex} value={this.state.gasOracleSliderIndex}
onChange={(e) => this.gasSliderChange(e, 'gasOracleSliderIndex')} onChange={(e) => this.gasSliderChange(e, 'gasOracleSliderIndex')}
/>} />
</Form.Group> </Form.Group>
<Grid.Row cards={true}> {/*<Grid.Row cards={true}>
<Grid.Col lg={4} md={6} sm={12}> <Grid.Col lg={4} md={6} sm={12}>
<StampCard icon="sliders" color="grey"> <StampCard icon="sliders" color="grey">
Average Price: {this.formattedGasStats.average.price / 10} Gwei Average Price: {this.formattedGasStats.average.price} Wei
</StampCard> </StampCard>
</Grid.Col> </Grid.Col>
<Grid.Col lg={4} md={6} sm={12}> <Grid.Col lg={4} md={6} sm={12}>
@ -158,7 +132,7 @@ class GasStation extends Component {
Last Block: {this.formattedGasStats.blockNum} Last Block: {this.formattedGasStats.blockNum}
</StampCard> </StampCard>
</Grid.Col> </Grid.Col>
</Grid.Row> </Grid.Row>*/}
</Card.Body> </Card.Body>
</Card> </Card>
</Grid.Col> </Grid.Col>
@ -167,20 +141,22 @@ class GasStation extends Component {
} }
GasStation.propTypes = { GasStation.propTypes = {
gasStats: PropTypes.object.isRequired, gasOracleStats: PropTypes.object,
gasOracleStats: PropTypes.array, listenToGasOracle: PropTypes.func,
listenToGasOracle: PropTypes.func fetchEthGas: PropTypes.func
}; };
function mapStateToProps(state, _props) { function mapStateToProps(state, _props) {
return { return {
gasOracleStats: getOracleGasStats(state) gasOracleStats: getOracleGasStats(state),
gasStats: getGasStats(state)
}; };
} }
export default withRouter(connect( export default withRouter(connect(
mapStateToProps, mapStateToProps,
{ {
listenToGasOracle: listenToGasOracle listenToGasOracle: listenToGasOracle,
fetchEthGas: ethGasAction.request
} }
)(GasStation)); )(GasStation));

View File

@ -3,11 +3,7 @@ import {connect} from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import { import {contractProfile as contractProfileAction, contractDeploy as contractDeployAction} from '../actions';
contractProfile as contractProfileAction,
contractDeploy as contractDeployAction,
ethGas as ethGasAction
} from '../actions';
import ContractFunctions from '../components/ContractFunctions'; import ContractFunctions from '../components/ContractFunctions';
import DataWrapper from "../components/DataWrapper"; import DataWrapper from "../components/DataWrapper";
import GasStation from "../components/GasStation"; import GasStation from "../components/GasStation";
@ -16,7 +12,6 @@ import {getContractProfile, getContractDeploys, getGasStats} from "../reducers/s
class ContractDeploymentContainer extends Component { class ContractDeploymentContainer extends Component {
componentDidMount() { componentDidMount() {
this.props.fetchContractProfile(this.props.match.params.contractName); this.props.fetchContractProfile(this.props.match.params.contractName);
this.props.fetchEthGas();
} }
render() { render() {
@ -30,12 +25,7 @@ class ContractDeploymentContainer extends Component {
onlyConstructor onlyConstructor
postContractFunction={postContractDeploy}/> postContractFunction={postContractDeploy}/>
)}/> )}/>
<GasStation/>
<DataWrapper shouldRender={this.props.gasStats !== undefined}
{...this.props}
render={({gasStats}) => (
<GasStation gasStats={gasStats}/>
)}/>
</React.Fragment> </React.Fragment>
); );
} }
@ -57,7 +47,6 @@ ContractDeploymentContainer.propTypes = {
contractFunctions: PropTypes.arrayOf(PropTypes.object), contractFunctions: PropTypes.arrayOf(PropTypes.object),
postContractDeploy: PropTypes.func, postContractDeploy: PropTypes.func,
fetchContractProfile: PropTypes.func, fetchContractProfile: PropTypes.func,
fetchEthGas: PropTypes.func,
error: PropTypes.string error: PropTypes.string
}; };
@ -65,7 +54,6 @@ export default withRouter(connect(
mapStateToProps, mapStateToProps,
{ {
fetchContractProfile: contractProfileAction.request, fetchContractProfile: contractProfileAction.request,
postContractDeploy: contractDeployAction.post, postContractDeploy: contractDeployAction.post
fetchEthGas: ethGasAction.request
} }
)(ContractDeploymentContainer)); )(ContractDeploymentContainer));

View File

@ -3,20 +3,15 @@ import {connect} from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import { import {contractProfile as contractProfileAction, contractFunction as contractFunctionAction} from '../actions';
contractProfile as contractProfileAction,
contractFunction as contractFunctionAction,
ethGas as ethGasAction
} from '../actions';
import ContractFunctions from '../components/ContractFunctions'; import ContractFunctions from '../components/ContractFunctions';
import DataWrapper from "../components/DataWrapper"; import DataWrapper from "../components/DataWrapper";
import GasStation from "../components/GasStation"; import GasStation from "../components/GasStation";
import {getContractProfile, getContractFunctions, getGasStats} from "../reducers/selectors"; import {getContractProfile, getContractFunctions} from "../reducers/selectors";
class ContractFunctionsContainer extends Component { class ContractFunctionsContainer extends Component {
componentDidMount() { componentDidMount() {
this.props.fetchContractProfile(this.props.match.params.contractName); this.props.fetchContractProfile(this.props.match.params.contractName);
this.props.fetchEthGas();
} }
render() { render() {
@ -32,8 +27,8 @@ class ContractFunctionsContainer extends Component {
<DataWrapper shouldRender={this.props.gasStats !== undefined} <DataWrapper shouldRender={this.props.gasStats !== undefined}
{...this.props} {...this.props}
render={({gasStats}) => ( render={() => (
<GasStation gasStats={gasStats}/> <GasStation/>
)}/> )}/>
</React.Fragment> </React.Fragment>
); );
@ -44,7 +39,6 @@ function mapStateToProps(state, props) {
return { return {
contractProfile: getContractProfile(state, props.match.params.contractName), contractProfile: getContractProfile(state, props.match.params.contractName),
contractFunctions: getContractFunctions(state, props.match.params.contractName), contractFunctions: getContractFunctions(state, props.match.params.contractName),
gasStats: getGasStats(state),
error: state.errorMessage, error: state.errorMessage,
loading: state.loading loading: state.loading
}; };
@ -64,7 +58,6 @@ export default withRouter(connect(
mapStateToProps, mapStateToProps,
{ {
fetchContractProfile: contractProfileAction.request, fetchContractProfile: contractProfileAction.request,
postContractFunction: contractFunctionAction.post, postContractFunction: contractFunctionAction.post
fetchEthGas: ethGasAction.request
} }
)(ContractFunctionsContainer)); )(ContractFunctionsContainer));

View File

@ -42,7 +42,7 @@ export const sendMessage = doRequest.bind(null, messageSend, api.sendMessage);
export const fetchEnsRecord = doRequest.bind(null, ensRecord, api.fetchEnsRecord); export const fetchEnsRecord = doRequest.bind(null, ensRecord, api.fetchEnsRecord);
export const postEnsRecord = doRequest.bind(null, ensRecords, api.postEnsRecord); export const postEnsRecord = doRequest.bind(null, ensRecords, api.postEnsRecord);
export const fetchFiles = doRequest.bind(null, files, api.fetchFiles); export const fetchFiles = doRequest.bind(null, files, api.fetchFiles);
export const fetchEthGas = doRequest.bind(null, ethGas, api.getEthGasAPI); export const fetchEthGas = doRequest.bind(null, gasOracle, api.getEthGasAPI);
export function *watchFetchTransaction() { export function *watchFetchTransaction() {
yield takeEvery(actions.TRANSACTION[actions.REQUEST], fetchTransaction); yield takeEvery(actions.TRANSACTION[actions.REQUEST], fetchTransaction);
@ -153,7 +153,7 @@ export function *watchFetchFiles() {
} }
export function *watchFetchEthGas() { export function *watchFetchEthGas() {
yield takeEvery(actions.ETH_GAS[actions.REQUEST], fetchEthGas); yield takeEvery(actions.GAS_ORACLE[actions.REQUEST], fetchEthGas);
} }
function createChannel(socket) { function createChannel(socket) {
@ -212,7 +212,7 @@ export function *listenGasOracle() {
const channel = yield call(createChannel, socket); const channel = yield call(createChannel, socket);
while (true) { while (true) {
const gasOracleStats = yield take(channel); const gasOracleStats = yield take(channel);
yield put(gasOracle.success([gasOracleStats])); yield put(gasOracle.success(gasOracleStats));
} }
} }

View File

@ -56,6 +56,13 @@ class TransactionTracker {
registerAPICalls() { registerAPICalls() {
const self = this; const self = this;
self.embark.registerAPICall(
'get',
'/embark-api/blockchain/gas/oracle',
(req, res) => {
res.send(self.calculateGasPriceSpeeds());
}
);
self.embark.registerAPICall( self.embark.registerAPICall(
'ws', 'ws',
'/embark-api/blockchain/gas/oracle', '/embark-api/blockchain/gas/oracle',