diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js
index bf5e07d5d..7104d037e 100644
--- a/embark-ui/src/actions/index.js
+++ b/embark-ui/src/actions/index.js
@@ -10,6 +10,10 @@ export const RECEIVE_PROCESSES_ERROR = 'RECEIVE_PROCESSES_ERROR';
export const FETCH_BLOCKS = 'FETCH_BLOCKS';
export const RECEIVE_BLOCKS = 'RECEIVE_BLOCKS';
export const RECEIVE_BLOCKS_ERROR = 'RECEIVE_BLOCKS_ERROR';
+// Transactions
+export const FETCH_TRANSACTIONS = 'FETCH_TRANSACTIONS';
+export const RECEIVE_TRANSACTIONS = 'RECEIVE_TRANSACTIONS';
+export const RECEIVE_TRANSACTIONS_ERROR = 'RECEIVE_TRANSACTIONS_ERROR';
export function fetchAccounts() {
return {
@@ -68,3 +72,23 @@ export function receiveBlocksError() {
type: RECEIVE_BLOCKS_ERROR
};
}
+
+export function fetchTransactions(blockFrom) {
+ return {
+ type: FETCH_TRANSACTIONS,
+ blockFrom
+ };
+}
+
+export function receiveTransactions(transactions) {
+ return {
+ type: RECEIVE_TRANSACTIONS,
+ transactions
+ };
+}
+
+export function receiveTransactionsError() {
+ return {
+ type: RECEIVE_TRANSACTIONS_ERROR
+ };
+}
diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js
index 968f99f00..704e1baa3 100644
--- a/embark-ui/src/api/index.js
+++ b/embark-ui/src/api/index.js
@@ -10,6 +10,10 @@ export function fetchBlocks(from) {
return axios.get(`${BASE_URL}/blockchain/blocks`, {params: {from}});
}
+export function fetchTransactions(blockFrom) {
+ return axios.get(`${BASE_URL}/blockchain/transactions`, {params: {blockFrom}});
+}
+
export function fetchProcesses() {
return axios.get(`${BASE_URL}/processes`);
}
diff --git a/embark-ui/src/components/ExplorerLayout.js b/embark-ui/src/components/ExplorerLayout.js
index b4b70f988..8c9d1483d 100644
--- a/embark-ui/src/components/ExplorerLayout.js
+++ b/embark-ui/src/components/ExplorerLayout.js
@@ -8,6 +8,7 @@ import {
import AccountsContainer from '../containers/AccountsContainer';
import BlocksContainer from '../containers/BlocksContainer';
+import TransactionsContainer from '../containers/TransactionsContainer';
const ExplorerLayout = () => (
@@ -31,6 +32,14 @@ const ExplorerLayout = () => (
>
Blocks
+
+ Transactions
+
@@ -38,6 +47,7 @@ const ExplorerLayout = () => (
+
diff --git a/embark-ui/src/components/Transactions.js b/embark-ui/src/components/Transactions.js
new file mode 100644
index 000000000..257aa2740
--- /dev/null
+++ b/embark-ui/src/components/Transactions.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import {
+ Page,
+ Grid,
+ Card,
+ Table
+} from "tabler-react";
+import PropTypes from 'prop-types';
+
+const Transactions = ({transactions}) => (
+
+
+
+
+ {
+ return ([
+ {content: transaction.blockNumber},
+ {content: transaction.from},
+ {content: transaction.to},
+ {content: transaction.to ? "Contract Call" : "Contract Creation"},
+ {content: transaction.hash}
+ ]);
+ })
+ }
+ />
+
+
+
+
+);
+
+Transactions.propTypes = {
+ transactions: PropTypes.arrayOf(PropTypes.object)
+};
+
+export default Transactions;
diff --git a/embark-ui/src/containers/BlocksContainer.js b/embark-ui/src/containers/BlocksContainer.js
index b2a5cb17a..c939dbd7a 100644
--- a/embark-ui/src/containers/BlocksContainer.js
+++ b/embark-ui/src/containers/BlocksContainer.js
@@ -8,7 +8,6 @@ import Loading from '../components/Loading';
import LoadMore from '../components/LoadMore';
class BlocksContainer extends Component {
-
componentDidMount() {
if (!this.props.blocks.data) {
this.props.fetchBlocks();
diff --git a/embark-ui/src/containers/TransactionsContainer.js b/embark-ui/src/containers/TransactionsContainer.js
new file mode 100644
index 000000000..9298fc9f9
--- /dev/null
+++ b/embark-ui/src/containers/TransactionsContainer.js
@@ -0,0 +1,63 @@
+import React, {Component} from 'react';
+import {connect} from 'react-redux';
+import PropTypes from 'prop-types';
+
+import {fetchTransactions} from '../actions';
+import Transactions from '../components/Transactions';
+import Loading from '../components/Loading';
+import LoadMore from '../components/LoadMore';
+
+class TransactionsContainer extends Component {
+ componentDidMount() {
+ if (!this.props.transactions.data) {
+ this.props.fetchTransactions();
+ }
+ }
+
+ loadMore() {
+ this.props.fetchTransactions(this.loadMoreFrom());
+ }
+
+ loadMoreFrom() {
+ let transactions = this.props.transactions.data;
+ return transactions[transactions.length - 1].blockNumber - 1;
+ }
+
+ render() {
+ const {transactions} = this.props;
+ if (!transactions.data) {
+ return ;
+ }
+
+ if (transactions.error) {
+ return (
+
+ Error API...
+
+ );
+ }
+
+ return (
+
+
+ {(this.loadMoreFrom() > 0) ? this.loadMore()} /> : }
+
+ );
+ }
+}
+
+function mapStateToProps(state) {
+ return {transactions: state.transactions};
+}
+
+TransactionsContainer.propTypes = {
+ transactions: PropTypes.object,
+ fetchTransactions: PropTypes.func
+};
+
+export default connect(
+ mapStateToProps,
+ {
+ fetchTransactions
+ },
+)(TransactionsContainer);
diff --git a/embark-ui/src/reducers/accountsReducer.js b/embark-ui/src/reducers/accountsReducer.js
index a89cde482..c1f76a738 100644
--- a/embark-ui/src/reducers/accountsReducer.js
+++ b/embark-ui/src/reducers/accountsReducer.js
@@ -3,7 +3,7 @@ import {RECEIVE_ACCOUNTS, RECEIVE_ACCOUNTS_ERROR} from "../actions";
export default function accounts(state = {}, action) {
switch (action.type) {
case RECEIVE_ACCOUNTS:
- return Object.assign({}, state, {data: action.accounts.data});
+ return {...state, data: [...state.data || [], ...action.accounts.data]};
case RECEIVE_ACCOUNTS_ERROR:
return Object.assign({}, state, {error: true});
default:
diff --git a/embark-ui/src/reducers/index.js b/embark-ui/src/reducers/index.js
index 2ecb959e4..3f4250d4a 100644
--- a/embark-ui/src/reducers/index.js
+++ b/embark-ui/src/reducers/index.js
@@ -2,11 +2,13 @@ import {combineReducers} from 'redux';
import processesReducer from './processesReducer';
import accountsReducer from './accountsReducer';
import blocksReducer from './blocksReducer';
+import transactionsReducer from './transactionsReducer';
const rootReducer = combineReducers({
accounts: accountsReducer,
processes: processesReducer,
- blocks: blocksReducer
+ blocks: blocksReducer,
+ transactions: transactionsReducer
});
export default rootReducer;
diff --git a/embark-ui/src/reducers/transactionsReducer.js b/embark-ui/src/reducers/transactionsReducer.js
new file mode 100644
index 000000000..36b6a4b7c
--- /dev/null
+++ b/embark-ui/src/reducers/transactionsReducer.js
@@ -0,0 +1,12 @@
+import {RECEIVE_TRANSACTIONS, RECEIVE_TRANSACTIONS_ERROR} from "../actions";
+
+export default function transactions(state = {}, action) {
+ switch (action.type) {
+ case RECEIVE_TRANSACTIONS:
+ return {...state, data: [...state.data || [], ...action.transactions.data]};
+ case RECEIVE_TRANSACTIONS_ERROR:
+ return Object.assign({}, state, {error: true});
+ default:
+ return state;
+ }
+}
diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js
index 7cb1d010d..c2ad5da73 100644
--- a/embark-ui/src/sagas/index.js
+++ b/embark-ui/src/sagas/index.js
@@ -2,6 +2,19 @@ import * as actions from '../actions';
import * as api from '../api';
import {all, call, fork, put, takeEvery} from 'redux-saga/effects';
+export function *fetchTransactions(payload) {
+ try {
+ const transactions = yield call(api.fetchTransactions, payload.blockFrom);
+ yield put(actions.receiveTransactions(transactions));
+ } catch (e) {
+ yield put(actions.receiveTransactionsError());
+ }
+}
+
+export function *watchFetchTransactions() {
+ yield takeEvery(actions.FETCH_TRANSACTIONS, fetchTransactions);
+}
+
export function *fetchBlocks(payload) {
try {
const blocks = yield call(api.fetchBlocks, payload.from);
@@ -42,5 +55,10 @@ export function *watchFetchProcesses() {
}
export default function *root() {
- yield all([fork(watchFetchAccounts), fork(watchFetchProcesses), fork(watchFetchBlocks)]);
+ yield all([
+ fork(watchFetchAccounts),
+ fork(watchFetchProcesses),
+ fork(watchFetchBlocks),
+ fork(watchFetchTransactions)
+ ]);
}
diff --git a/lib/modules/blockchain_connector/index.js b/lib/modules/blockchain_connector/index.js
index 1c287ca91..7e56172ec 100644
--- a/lib/modules/blockchain_connector/index.js
+++ b/lib/modules/blockchain_connector/index.js
@@ -336,39 +336,61 @@ class BlockchainConnector {
(req, res) => {
let from = parseInt(req.query.from, 10);
let limit = req.query.limit || 10;
- let results = [];
- async.waterfall([
- function(callback) {
- if (!isNaN(from)) {
- return callback();
- }
- self.getBlockNumber((err, blockNumber) => {
- if (err) {
- self.logger.error(err);
- from = 0;
- } else {
- from = blockNumber;
- }
- callback();
- });
- },
- function(callback) {
- async.times(limit, function(n, next) {
- self.web3.eth.getBlock(from - n, function(err, block) {
- if (err){
- self.logger.error(err);
- return next();
- }
- results.push(block);
- next();
- });
- }, callback);
- }
- ], function () {
- res.send(results);
- });
+ self.getBlocks(from, limit, false, res.send.bind(res));
}
);
+
+ plugin.registerAPICall(
+ 'get',
+ '/embark-api/blockchain/transactions',
+ (req, res) => {
+ let blockFrom = parseInt(req.query.blockFrom, 10);
+ let blockLimit = req.query.blockLimit || 10;
+ self.getTransactions(blockFrom, blockLimit, res.send.bind(res));
+ }
+ );
+ }
+
+ getTransactions(blockFrom, blockLimit, callback) {
+ this.getBlocks(blockFrom, blockLimit, true, (blocks) => {
+ let transactions = blocks.reduce((acc, block) => acc.concat(block.transactions), []);
+ callback(transactions);
+ });
+ }
+
+ getBlocks(from, limit, returnTransactionObjects, callback) {
+ let self = this;
+ let blocks = [];
+ async.waterfall([
+ function(next) {
+ if (!isNaN(from)) {
+ return next();
+ }
+ self.getBlockNumber((err, blockNumber) => {
+ if (err) {
+ self.logger.error(err);
+ from = 0;
+ } else {
+ from = blockNumber;
+ }
+ next();
+ });
+ },
+ function(next) {
+ async.times(limit, function(n, eachCb) {
+ self.web3.eth.getBlock(from - n, returnTransactionObjects, function(err, block) {
+ if (err){
+ self.logger.error(err);
+ return eachCb();
+ }
+ blocks.push(block);
+ eachCb();
+ });
+ }, next);
+ }
+ ], function () {
+ callback(blocks);
+ });
}