diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index a901dd073..cf987b745 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -207,10 +207,18 @@ export const ethGas = { failure: (error) => action(ETH_GAS[FAILURE], {error}) }; +export const GAS_ORACLE = createRequestTypes('GAS_ORACLE'); +export const gasOracle = { + request: () => action(GAS_ORACLE[REQUEST]), + success: (gasOracleStats) => action(GAS_ORACLE[SUCCESS], {gasOracleStats}), + failure: (error) => action(GAS_ORACLE[FAILURE], {error}) +}; + // Web Socket export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS'; export const WATCH_NEW_CONTRACT_LOGS = 'WATCH_NEW_CONTRACT_LOGS'; export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER'; +export const WATCH_GAS_ORACLE = 'WATCH_GAS_ORACLE'; export function listenToProcessLogs(processName) { return { @@ -231,4 +239,10 @@ export function initBlockHeader(){ }; } +export function listenToGasOracle(){ + return { + type: WATCH_GAS_ORACLE + }; +} + diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js index 87ad2ad55..edb2a65d1 100644 --- a/embark-ui/src/api/index.js +++ b/embark-ui/src/api/index.js @@ -132,6 +132,10 @@ export function webSocketBlockHeader() { return new WebSocket(`${constants.wsEndpoint}/blockchain/blockHeader`); } +export function websocketGasOracle() { + return new WebSocket(`${constants.wsEndpoint}/blockchain/gas/oracle`); +} + export function postFiddle(payload) { return post('/contract/compile', payload); } diff --git a/embark-ui/src/components/GasStation.js b/embark-ui/src/components/GasStation.js index b94545d9a..195d167e7 100644 --- a/embark-ui/src/components/GasStation.js +++ b/embark-ui/src/components/GasStation.js @@ -1,7 +1,11 @@ import PropTypes from "prop-types"; import React, {Component} from 'react'; +import {connect} from 'react-redux'; +import {withRouter} from "react-router-dom"; import {Card, Form, Grid, StampCard, Stamp} from 'tabler-react'; import {CopyToClipboard} from 'react-copy-to-clipboard'; +import {listenToGasOracle} from "../actions"; +import {getOracleGasStats} from "../reducers/selectors"; const COLORS = { good: 'green', @@ -23,6 +27,12 @@ class GasStation extends Component { this.formattedGasStats = GasStation.formatGasStats(props.gasStats); } + componentDidMount() { + if (!this.props.gasOracleStats.length) { + this.props.listenToGasOracle(); + } + } + static formatGasStats(gasStats) { const { fast, speed, fastest, avgWait, fastWait, blockNum, safeLowWait, @@ -134,7 +144,20 @@ class GasStation extends Component { } GasStation.propTypes = { - gasStats: PropTypes.object.isRequired + gasStats: PropTypes.object.isRequired, + gasOracleStats: PropTypes.array, + listenToGasOracle: PropTypes.func }; -export default GasStation; +function mapStateToProps(state, _props) { + return { + gasOracleStats: getOracleGasStats(state) + }; +} + +export default withRouter(connect( + mapStateToProps, + { + listenToGasOracle: listenToGasOracle + } +)(GasStation)); diff --git a/embark-ui/src/reducers/index.js b/embark-ui/src/reducers/index.js index 0d9d9ecad..3285b4572 100644 --- a/embark-ui/src/reducers/index.js +++ b/embark-ui/src/reducers/index.js @@ -25,7 +25,8 @@ const entitiesDefaultState = { plugins: [], ensRecords: [], files: [], - gasStats: [] + gasStats: [], + gasOracleStats: [] }; const sorter = { @@ -84,6 +85,9 @@ const filtrer = { return index === self.findIndex((f) => ( file.name === f.name )); + }, + gasOracleStats: function(stat, index, _self) { + return index === 0; // Only keep last one } }; diff --git a/embark-ui/src/reducers/selectors.js b/embark-ui/src/reducers/selectors.js index 8edea1edf..79438df4f 100644 --- a/embark-ui/src/reducers/selectors.js +++ b/embark-ui/src/reducers/selectors.js @@ -88,6 +88,10 @@ export function getGasStats(state) { return state.entities.gasStats[state.entities.gasStats.length - 1]; } +export function getOracleGasStats(state) { + return state.entities.gasOracleStats; +} + export function isWeb3Enabled(state) { return Boolean(state.entities.versions.find((version) => version.name === 'web3')); } diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index 8734c9d03..900cb292b 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -6,7 +6,7 @@ import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects'; const {account, accounts, block, blocks, transaction, transactions, processes, commands, processLogs, contracts, contract, contractProfile, messageSend, versions, plugins, messageListen, fiddle, fiddleDeploy, ensRecord, ensRecords, contractLogs, contractFile, contractFunction, contractDeploy, - fiddleFile, files, ethGas} = actions; + fiddleFile, files, ethGas, gasOracle} = actions; function *doRequest(entity, apiFn, payload) { const {response, error} = yield call(apiFn, payload); @@ -207,6 +207,19 @@ export function *watchListenToContractLogs() { yield takeEvery(actions.WATCH_NEW_CONTRACT_LOGS, listenToContractLogs); } +export function *listenGasOracle() { + const socket = api.websocketGasOracle(); + const channel = yield call(createChannel, socket); + while (true) { + const gasOracleStats = yield take(channel); + yield put(gasOracle.success([gasOracleStats])); + } +} + +export function *watchListenGasOracle() { + yield takeEvery(actions.WATCH_GAS_ORACLE, listenGasOracle); +} + export function *listenToMessages(action) { const socket = api.listenToChannel(action.messageChannels[0]); const channel = yield call(createChannel, socket); @@ -248,6 +261,7 @@ export default function *root() { fork(watchFetchEnsRecord), fork(watchPostEnsRecords), fork(watchFetchFiles), - fork(watchFetchEthGas) + fork(watchFetchEthGas), + fork(watchListenGasOracle) ]); } diff --git a/lib/modules/blockchain_connector/index.js b/lib/modules/blockchain_connector/index.js index 4c5087465..47da89f55 100644 --- a/lib/modules/blockchain_connector/index.js +++ b/lib/modules/blockchain_connector/index.js @@ -446,7 +446,12 @@ class BlockchainConnector { getTransactions(blockFrom, blockLimit, callback) { this.getBlocks(blockFrom, blockLimit, true, (blocks) => { - let transactions = blocks.reduce((acc, block) => acc.concat(block.transactions), []); + let transactions = blocks.reduce((acc, block) => { + if (!block || !block.transactions) { + return acc; + } + return acc.concat(block.transactions); + }, []); callback(transactions); }); } diff --git a/lib/modules/transactionTracker/index.js b/lib/modules/transactionTracker/index.js index e18174b69..d865bec99 100644 --- a/lib/modules/transactionTracker/index.js +++ b/lib/modules/transactionTracker/index.js @@ -3,9 +3,11 @@ class TransactionTracker { this.logger = embark.logger; this.events = embark.events; this.transactions = {}; + this.embark = embark; embark.events.on("block:pending:transaction", this.onPendingTransaction.bind(this)); embark.events.on("block:header", this.onBlockHeader.bind(this)); + this.registerAPICalls(); } onPendingTransaction(pendingTransaction) { @@ -28,6 +30,7 @@ class TransactionTracker { Object.assign(this.transactions[transaction.hash], transaction, {endTimestamp: block.timestamp, wait: block.timestamp - this.transactions[transaction.hash].startTimestamp}); } }); + this.events.emit('blockchain:gas:oracle:new'); }); } @@ -50,6 +53,19 @@ class TransactionTracker { return acc; }, {}); } + + registerAPICalls() { + const self = this; + self.embark.registerAPICall( + 'ws', + '/embark-api/blockchain/gas/oracle', + (ws) => { + self.events.on('blockchain:gas:oracle:new', () => { + ws.send(JSON.stringify(self.calculateGasPriceSpeeds()), () => {}); + }); + } + ); + } } module.exports = TransactionTracker;