diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js
index 7cc92cd26..9fe035060 100644
--- a/embark-ui/src/actions/index.js
+++ b/embark-ui/src/actions/index.js
@@ -76,6 +76,13 @@ export const processLogs = {
failure: (error) => action(PROCESS_LOGS[FAILURE], {error})
};
+export const CONTRACT_LOGS = createRequestTypes('CONTRACT_LOGS');
+export const contractLogs = {
+ request: () => action(CONTRACT_LOGS[REQUEST]),
+ success: (contractLogs) => action(CONTRACT_LOGS[SUCCESS], {contractLogs}),
+ failure: (error) => action(CONTRACT_LOGS[FAILURE], {error})
+};
+
export const CONTRACTS = createRequestTypes('CONTRACTS');
export const contracts = {
request: () => action(CONTRACTS[REQUEST]),
diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js
index 48dbbda18..a4c6b3f9d 100644
--- a/embark-ui/src/api/index.js
+++ b/embark-ui/src/api/index.js
@@ -56,6 +56,10 @@ export function fetchProcessLogs(payload) {
return get(`/process-logs/${payload.processName}`);
}
+export function fetchContractLogs() {
+ return get(`/contracts/logs`);
+}
+
export function fetchContracts() {
return get('/contracts');
}
diff --git a/embark-ui/src/components/ContractLayout.js b/embark-ui/src/components/ContractLayout.js
index e6afe1fbe..5a4dd9bc0 100644
--- a/embark-ui/src/components/ContractLayout.js
+++ b/embark-ui/src/components/ContractLayout.js
@@ -8,6 +8,7 @@ import {
} from "tabler-react";
import ContractContainer from '../containers/ContractContainer';
+import ContractLoggerContainer from '../containers/ContractLoggerContainer';
import ContractProfileContainer from '../containers/ContractProfileContainer';
const ContractLayout = (props) => (
@@ -48,11 +49,20 @@ const ContractLayout = (props) => (
>
Profile
+
+ Logger
+
+
diff --git a/embark-ui/src/components/ContractLogger.js b/embark-ui/src/components/ContractLogger.js
new file mode 100644
index 000000000..8a1bfd73d
--- /dev/null
+++ b/embark-ui/src/components/ContractLogger.js
@@ -0,0 +1,47 @@
+import PropTypes from "prop-types";
+import React from 'react';
+import {
+ Page,
+ Grid, Table
+} from "tabler-react";
+
+const ContractLogger = ({contractName, contractLogs}) => (
+
+
+
+ {
+ return ([
+ {content: log.functionName},
+ {content: log.paramString},
+ {content: log.transactionHash},
+ {content: log.gasUsed},
+ {content: log.blockNumber},
+ {content: log.status}
+ ]);
+ })
+ }
+ />
+
+
+
+);
+
+ContractLogger.propTypes = {
+ contractName: PropTypes.string.isRequired,
+ contractLogs: PropTypes.array.isRequired
+};
+
+export default ContractLogger;
+
diff --git a/embark-ui/src/containers/ContractLoggerContainer.js b/embark-ui/src/containers/ContractLoggerContainer.js
new file mode 100644
index 000000000..33dd5bb8c
--- /dev/null
+++ b/embark-ui/src/containers/ContractLoggerContainer.js
@@ -0,0 +1,42 @@
+import React, {Component} from 'react';
+import {connect} from 'react-redux';
+import PropTypes from 'prop-types';
+import {withRouter} from 'react-router-dom';
+import {contractLogs as contractLogsAction} from '../actions';
+
+import ContractLogger from '../components/ContractLogger';
+// import DataWrapper from "../components/DataWrapper";
+import {getContractLogsByContract} from "../reducers/selectors";
+
+class ContractProfileContainer extends Component {
+ componentDidMount() {
+ if (this.props.contractLogs.length === 0) {
+ this.props.fetchContractLogs(this.props.match.params.contractName);
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+function mapStateToProps(state, props) {
+ return {
+ contractLogs: getContractLogsByContract(state, props.match.params.contractName)
+ };
+}
+
+ContractProfileContainer.propTypes = {
+ contractLogs: PropTypes.array,
+ fetchContractLogs: PropTypes.func,
+ match: PropTypes.object
+};
+
+export default withRouter(connect(
+ mapStateToProps,
+ {
+ fetchContractLogs: contractLogsAction.request
+ }
+)(ContractProfileContainer));
diff --git a/embark-ui/src/reducers/index.js b/embark-ui/src/reducers/index.js
index d62f0c6d5..19275d675 100644
--- a/embark-ui/src/reducers/index.js
+++ b/embark-ui/src/reducers/index.js
@@ -11,6 +11,7 @@ const entitiesDefaultState = {
processLogs: [],
contracts: [],
contractProfiles: [],
+ contractLogs: [],
commands: [],
messages: [],
messageChannels: [],
@@ -27,6 +28,9 @@ const sorter = {
processLogs: function(a, b) {
return a.timestamp - b.timestamp;
},
+ contractLogs: function(a, b) {
+ return a.timestamp - b.timestamp;
+ },
messages: function(a, b) {
return a.time - b.time;
}
diff --git a/embark-ui/src/reducers/selectors.js b/embark-ui/src/reducers/selectors.js
index fb98d86a9..f39e0277c 100644
--- a/embark-ui/src/reducers/selectors.js
+++ b/embark-ui/src/reducers/selectors.js
@@ -46,6 +46,11 @@ export function getProcessLogsByProcess(state, processName) {
return state.entities.processLogs.filter((processLog => processLog.name === processName));
}
+export function getContractLogsByContract(state, contractName) {
+ return state.entities.contractLogs;
+ // return state.entities.processLogs.filter((processLog => processLog.name === processName));
+}
+
export function getContracts(state) {
return state.entities.contracts;
}
diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js
index 6929c0c5f..0044eebbb 100644
--- a/embark-ui/src/sagas/index.js
+++ b/embark-ui/src/sagas/index.js
@@ -4,7 +4,7 @@ import {eventChannel} from 'redux-saga';
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, messageVersion, messageListen} = actions;
+ contracts, contract, contractProfile, messageSend, messageVersion, messageListen, contractLogs} = actions;
function *doRequest(entity, apiFn, payload) {
const {response, error} = yield call(apiFn, payload);
@@ -24,6 +24,7 @@ export const fetchTransactions = doRequest.bind(null, transactions, api.fetchTra
export const fetchProcesses = doRequest.bind(null, processes, api.fetchProcesses);
export const postCommand = doRequest.bind(null, commands, api.postCommand);
export const fetchProcessLogs = doRequest.bind(null, processLogs, api.fetchProcessLogs);
+export const fetchContractLogs = doRequest.bind(null, contractLogs, api.fetchContractLogs);
export const fetchContracts = doRequest.bind(null, contracts, api.fetchContracts);
export const fetchContract = doRequest.bind(null, contract, api.fetchContract);
export const fetchContractProfile = doRequest.bind(null, contractProfile, api.fetchContractProfile);
@@ -64,6 +65,10 @@ export function *watchFetchProcessLogs() {
yield takeEvery(actions.PROCESS_LOGS[actions.REQUEST], fetchProcessLogs);
}
+export function *watchFetchContractLogs() {
+ yield takeEvery(actions.CONTRACT_LOGS[actions.REQUEST], fetchContractLogs);
+}
+
export function *watchFetchContract() {
yield takeEvery(actions.CONTRACT[actions.REQUEST], fetchContract);
}
@@ -146,6 +151,7 @@ export default function *root() {
fork(watchFetchAccount),
fork(watchFetchProcesses),
fork(watchFetchProcessLogs),
+ fork(watchFetchContractLogs),
fork(watchListenToProcessLogs),
fork(watchFetchBlock),
fork(watchFetchTransactions),
diff --git a/lib/contracts/proxy.js b/lib/contracts/proxy.js
index ce3ed4b18..db001c2fa 100644
--- a/lib/contracts/proxy.js
+++ b/lib/contracts/proxy.js
@@ -8,120 +8,122 @@ let receipts = {};
const {canonicalHost, defaultHost} = require('../utils/host');
-const parseRequest = function(reqBody){
- let jsonO;
- try {
- jsonO = JSON.parse(reqBody);
- } catch(e){
- return; // Request is not a json. Do nothing
- }
- if(jsonO.method === "eth_sendTransaction"){
- commList[jsonO.id] = {
- type: 'contract-log',
- address: jsonO.params[0].to,
- data: jsonO.params[0].data
- };
- } else if(jsonO.method === "eth_getTransactionReceipt"){
- if(transactions[jsonO.params[0]]){
- transactions[jsonO.params[0]].receiptId = jsonO.id;
- receipts[jsonO.id] = transactions[jsonO.params[0]].commListId;
- }
+const parseRequest = function(reqBody) {
+ let jsonO;
+ try {
+ jsonO = JSON.parse(reqBody);
+ } catch (e) {
+ return; // Request is not a json. Do nothing
+ }
+ if (jsonO.method === "eth_sendTransaction") {
+ commList[jsonO.id] = {
+ type: 'contract-log',
+ address: jsonO.params[0].to,
+ data: jsonO.params[0].data
+ };
+ } else if (jsonO.method === "eth_getTransactionReceipt") {
+ if (transactions[jsonO.params[0]]) {
+ transactions[jsonO.params[0]].receiptId = jsonO.id;
+ receipts[jsonO.id] = transactions[jsonO.params[0]].commListId;
}
+ }
};
-const parseResponse = function(ipc, resBody){
- let jsonO;
- try {
- jsonO = JSON.parse(resBody);
- } catch(e) {
- return; // Response is not a json. Do nothing
+const parseResponse = function(ipc, resBody) {
+ let jsonO;
+ try {
+ jsonO = JSON.parse(resBody);
+ } catch (e) {
+ return; // Response is not a json. Do nothing
+ }
+
+ if (commList[jsonO.id]) {
+ commList[jsonO.id].transactionHash = jsonO.result;
+ transactions[jsonO.result] = {commListId: jsonO.id};
+ } else if (receipts[jsonO.id] && jsonO.result && jsonO.result.blockNumber) {
+ commList[receipts[jsonO.id]].blockNumber = jsonO.result.blockNumber;
+ commList[receipts[jsonO.id]].gasUsed = jsonO.result.gasUsed;
+ commList[receipts[jsonO.id]].status = jsonO.result.status;
+
+ if (ipc.connected && !ipc.connecting) {
+ ipc.request('log', commList[receipts[jsonO.id]]);
+ } else {
+ ipc.connecting = true;
+ ipc.connect(() => {
+ ipc.connecting = false;
+ });
}
- if(commList[jsonO.id]){
- commList[jsonO.id].transactionHash = jsonO.result;
- transactions[jsonO.result] = {commListId: jsonO.id};
- } else if(receipts[jsonO.id] && jsonO.result && jsonO.result.blockNumber){
- commList[receipts[jsonO.id]].blockNumber = jsonO.result.blockNumber;
- commList[receipts[jsonO.id]].gasUsed = jsonO.result.gasUsed;
- commList[receipts[jsonO.id]].status = jsonO.result.status;
-
- if(ipc.connected && !ipc.connecting){
- ipc.request('log', commList[receipts[jsonO.id]]);
- } else {
- ipc.connecting = true;
- ipc.connect(() => {
- ipc.connecting = false;
- });
- }
-
- delete transactions[commList[receipts[jsonO.id]].transactionHash];
- delete receipts[jsonO.id];
- delete commList[jsonO.id];
- }
+ delete transactions[commList[receipts[jsonO.id]].transactionHash];
+ delete receipts[jsonO.id];
+ delete commList[jsonO.id];
+ }
};
-exports.serve = function(ipc, host, port, ws){
- let proxy = httpProxy.createProxyServer({
- target: {
- host: canonicalHost(host),
- port: port + constants.blockchain.servicePortOnProxy
- },
- ws: ws
+exports.serve = function(ipc, host, port, ws) {
+ let proxy = httpProxy.createProxyServer({
+ target: {
+ host: canonicalHost(host),
+ port: port + constants.blockchain.servicePortOnProxy
+ },
+ ws: ws
+ });
+
+ proxy.on('error', function(e) {
+ console.error(__("Error forwarding requests to blockchain/simulator"), e.message);
+ });
+
+ proxy.on('proxyRes', (proxyRes) => {
+ let resBody = [];
+ proxyRes.on('data', (b) => resBody.push(b));
+ proxyRes.on('end', function() {
+ resBody = Buffer.concat(resBody).toString();
+ if (resBody) {
+ parseResponse(ipc, resBody);
+ }
});
+ });
- proxy.on('error', function (e) {
- console.error(__("Error forwarding requests to blockchain/simulator"), e.message);
- });
-
- proxy.on('proxyRes', (proxyRes) => {
- let resBody = [];
- proxyRes.on('data', (b) => resBody.push(b));
- proxyRes.on('end', function () {
- resBody = Buffer.concat(resBody).toString();
- if(resBody){
- parseResponse(ipc, resBody);
- }
- });
- });
-
- let server = http.createServer((req, res) => {
- let reqBody = [];
- req.on('data', (b) => { reqBody.push(b); })
- .on('end', () => {
- reqBody = Buffer.concat(reqBody).toString();
- if(reqBody){
- parseRequest(reqBody);
- }
- });
-
- if(!ws){
- proxy.web(req, res);
+ let server = http.createServer((req, res) => {
+ let reqBody = [];
+ req.on('data', (b) => {
+ reqBody.push(b);
+ })
+ .on('end', () => {
+ reqBody = Buffer.concat(reqBody).toString();
+ if (reqBody) {
+ parseRequest(reqBody);
}
+ });
+
+ if (!ws) {
+ proxy.web(req, res);
+ }
+ });
+
+ if (ws) {
+ const WsParser = require('simples/lib/parsers/ws'); // npm install simples
+
+ server.on('upgrade', function(req, socket, head) {
+ proxy.ws(req, socket, head);
});
- if(ws){
- const WsParser = require('simples/lib/parsers/ws'); // npm install simples
+ proxy.on('open', (proxySocket) => {
+ proxySocket.on('data', (data) => {
+ parseResponse(ipc, data.toString().substr(data.indexOf("{")));
+ });
+ });
- server.on('upgrade', function (req, socket, head) {
- proxy.ws(req, socket, head);
- });
+ proxy.on('proxyReqWs', (proxyReq, req, socket) => {
+ var parser = new WsParser(0, false);
+ socket.pipe(parser);
+ parser.on('frame', function(frame) {
+ parseRequest(frame.data);
+ });
- proxy.on('open', (proxySocket) => {
- proxySocket.on('data', (data) => {
- parseResponse(ipc, data.toString().substr(data.indexOf("{")));
- });
- });
+ });
+ }
- proxy.on('proxyReqWs', (proxyReq, req, socket) => {
- var parser = new WsParser(0, false);
- socket.pipe(parser);
- parser.on('frame', function (frame) {
- parseRequest(frame.data);
- });
-
- });
- }
-
- server.listen(port, defaultHost);
- return server;
+ server.listen(port, defaultHost);
+ return server;
};
diff --git a/lib/modules/console_listener/index.js b/lib/modules/console_listener/index.js
index 6a6e557e9..971105f9b 100644
--- a/lib/modules/console_listener/index.js
+++ b/lib/modules/console_listener/index.js
@@ -2,10 +2,12 @@ const utils = require('../../utils/utils.js');
class ConsoleListener {
constructor(embark, options) {
+ this.embark = embark;
this.logger = embark.logger;
this.ipc = options.ipc;
this.events = embark.events;
this.addressToContract = [];
+ this.logs = [];
this.contractsConfig = embark.config.contractsConfig;
this.contractsDeployed = false;
this.outputDone = false;
@@ -13,6 +15,7 @@ class ConsoleListener {
this.events.on('outputDone', () => {
this.outputDone = true;
});
+ this._registerAPI();
this.events.on("contractsDeployed", () => {
this.contractsDeployed = true;
@@ -91,12 +94,25 @@ class ConsoleListener {
gasUsed = utils.hexToNumber(gasUsed);
blockNumber = utils.hexToNumber(blockNumber);
+ this.logs.push(Object.assign({}, request, {name, functionName, paramString}));
+
this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`);
} else {
this.logger.info(JSON.stringify(request));
}
});
}
+
+ _registerAPI() {
+ const apiRoute = '/embark-api/contracts/logs';
+ this.embark.registerAPICall(
+ 'get',
+ apiRoute,
+ (req, res) => {
+ res.send(JSON.stringify(this.logs));
+ }
+ );
+ }
}
module.exports = ConsoleListener;