mirror of https://github.com/embarklabs/embark.git
Add Contract Events to UI
This commit is contained in:
parent
6ce78dfce3
commit
96c3575e75
|
@ -152,6 +152,13 @@ export const contractLogs = {
|
||||||
failure: (error) => action(CONTRACT_LOGS[FAILURE], {error})
|
failure: (error) => action(CONTRACT_LOGS[FAILURE], {error})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const CONTRACT_EVENTS = createRequestTypes('CONTRACT_EVENTS');
|
||||||
|
export const contractEvents = {
|
||||||
|
request: () => action(CONTRACT_EVENTS[REQUEST]),
|
||||||
|
success: (contractEvents) => action(CONTRACT_EVENTS[SUCCESS], {contractEvents}),
|
||||||
|
failure: (error) => action(CONTRACT_EVENTS[FAILURE], {error})
|
||||||
|
};
|
||||||
|
|
||||||
export const CONTRACTS = createRequestTypes('CONTRACTS');
|
export const CONTRACTS = createRequestTypes('CONTRACTS');
|
||||||
export const contracts = {
|
export const contracts = {
|
||||||
request: () => action(CONTRACTS[REQUEST]),
|
request: () => action(CONTRACTS[REQUEST]),
|
||||||
|
@ -299,6 +306,7 @@ export const gasOracle = {
|
||||||
export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS';
|
export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS';
|
||||||
export const STOP_NEW_PROCESS_LOGS = 'STOP_NEW_PROCESS_LOGS';
|
export const STOP_NEW_PROCESS_LOGS = 'STOP_NEW_PROCESS_LOGS';
|
||||||
export const WATCH_NEW_CONTRACT_LOGS = 'WATCH_NEW_CONTRACT_LOGS';
|
export const WATCH_NEW_CONTRACT_LOGS = 'WATCH_NEW_CONTRACT_LOGS';
|
||||||
|
export const WATCH_NEW_CONTRACT_EVENTS = 'WATCH_NEW_CONTRACT_EVENTS';
|
||||||
export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER';
|
export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER';
|
||||||
export const STOP_BLOCK_HEADER = 'STOP_BLOCK_HEADER';
|
export const STOP_BLOCK_HEADER = 'STOP_BLOCK_HEADER';
|
||||||
export const WATCH_GAS_ORACLE = 'WATCH_GAS_ORACLE';
|
export const WATCH_GAS_ORACLE = 'WATCH_GAS_ORACLE';
|
||||||
|
@ -324,6 +332,12 @@ export function listenToContractLogs() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listenToContractEvents() {
|
||||||
|
return {
|
||||||
|
type: WATCH_NEW_CONTRACT_EVENTS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function initBlockHeader(){
|
export function initBlockHeader(){
|
||||||
return {
|
return {
|
||||||
type: INIT_BLOCK_HEADER
|
type: INIT_BLOCK_HEADER
|
||||||
|
|
|
@ -7,7 +7,14 @@ import {
|
||||||
Form
|
Form
|
||||||
} from "tabler-react";
|
} from "tabler-react";
|
||||||
|
|
||||||
|
const ANY_STATE = 'Any';
|
||||||
|
const TX_STATES = ['Success', 'Fail', ANY_STATE];
|
||||||
|
|
||||||
class ContractLogger extends React.Component {
|
class ContractLogger extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {method: '', event: '', status: ANY_STATE};
|
||||||
|
}
|
||||||
|
|
||||||
getMethods() {
|
getMethods() {
|
||||||
if (!this.props.contract.abiDefinition) {
|
if (!this.props.contract.abiDefinition) {
|
||||||
|
@ -26,45 +33,60 @@ class ContractLogger extends React.Component {
|
||||||
return this.props.contract.abiDefinition.filter(method => method.type === 'event');
|
return this.props.contract.abiDefinition.filter(method => method.type === 'event');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateState(key, value) {
|
||||||
|
this.setState({[key]: value});
|
||||||
|
}
|
||||||
|
|
||||||
|
dataToDisplay() {
|
||||||
|
return this.props.contractLogs.map(contractLog => {
|
||||||
|
const events = this.props.contractEvents
|
||||||
|
.filter(contractEvent => contractEvent.transactionHash === contractLog.transactionHash)
|
||||||
|
.map(contractEvent => contractEvent.event);
|
||||||
|
contractLog.events = events;
|
||||||
|
return contractLog;
|
||||||
|
}).filter(contractLog => {
|
||||||
|
if (this.state.method || this.state.event || this.state.status !== ANY_STATE) {
|
||||||
|
return contractLog.status === '0x1' && this.state.status === 'Success' &&
|
||||||
|
(this.state.method === contractLog.functionName || contractLog.events.includes(this.state.event));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Page.Content title={this.props.contractName + ' Logger'}>
|
<Page.Content title={this.props.contract.className + ' Logger'}>
|
||||||
<Form>
|
<Form>
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
<Grid.Col md={6}>
|
<Grid.Col md={6}>
|
||||||
<Form.Group label="Functions">
|
<Form.Group label="Functions">
|
||||||
<Form.Select>
|
<Form.Select onChange={(event) => this.updateState('method', event.target.value)} value={this.state.method}>
|
||||||
{this.getMethods().map((method, index) => <option key={index}>{method.name}</option>)}
|
<option value=""></option>
|
||||||
|
{this.getMethods().map((method, index) => <option value={method.name} key={index}>{method.name}</option>)}
|
||||||
</Form.Select>
|
</Form.Select>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col md={6}>
|
<Grid.Col md={6}>
|
||||||
<Form.Group label="Events">
|
<Form.Group label="Events">
|
||||||
<Form.Select>
|
<Form.Select onChange={(event) => this.updateState('event', event.target.value)} value={this.state.event}>
|
||||||
{this.getEvents().map((event, index) => <option key={index}>{event.name}</option>)}
|
<option value=""></option>
|
||||||
|
{this.getEvents().map((event, index) => <option value={event.name} key={index}>{event.name}</option>)}
|
||||||
</Form.Select>
|
</Form.Select>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col>
|
<Grid.Col>
|
||||||
<Form.Group label="Tx Status">
|
<Form.Group label="Tx Status">
|
||||||
<Form.Radio
|
{TX_STATES.map(state => (
|
||||||
isInline
|
<Form.Radio
|
||||||
label="Failed"
|
key={state}
|
||||||
name="example-inline-radios"
|
isInline
|
||||||
value="option1"
|
label={state}
|
||||||
/>
|
value={state}
|
||||||
<Form.Radio
|
onChange={(event) => this.updateState('status', event.target.value)}
|
||||||
isInline
|
checked={state === this.state.status}
|
||||||
label="Success"
|
/>
|
||||||
name="example-inline-radios"
|
))}
|
||||||
value="option2"
|
|
||||||
/>
|
|
||||||
<Form.Radio
|
|
||||||
isInline
|
|
||||||
label="Any"
|
|
||||||
name="example-inline-radios"
|
|
||||||
value="option3"
|
|
||||||
/>
|
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
|
@ -78,23 +100,25 @@ class ContractLogger extends React.Component {
|
||||||
className="text-nowrap">
|
className="text-nowrap">
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColHeader>call</Table.ColHeader>
|
<Table.ColHeader>Call</Table.ColHeader>
|
||||||
<Table.ColHeader>Transaction hash</Table.ColHeader>
|
<Table.ColHeader>Events</Table.ColHeader>
|
||||||
<Table.ColHeader>Gas Used</Table.ColHeader>
|
<Table.ColHeader>Gas Used</Table.ColHeader>
|
||||||
<Table.ColHeader>Block number</Table.ColHeader>
|
<Table.ColHeader>Block number</Table.ColHeader>
|
||||||
<Table.ColHeader>Status</Table.ColHeader>
|
<Table.ColHeader>Status</Table.ColHeader>
|
||||||
|
<Table.ColHeader>Transaction hash</Table.ColHeader>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{
|
{
|
||||||
this.props.contractLogs.map((log, index) => {
|
this.dataToDisplay().map((log, index) => {
|
||||||
return (
|
return (
|
||||||
<Table.Row key={'log-' + index}>
|
<Table.Row key={'log-' + index}>
|
||||||
<Table.Col>{`${log.name}.${log.functionName}(${log.paramString})`}</Table.Col>
|
<Table.Col>{`${log.name}.${log.functionName}(${log.paramString})`}</Table.Col>
|
||||||
<Table.Col>{log.transactionHash}</Table.Col>
|
<Table.Col>{log.events.join(' ')}</Table.Col>
|
||||||
<Table.Col>{log.gasUsed}</Table.Col>
|
<Table.Col>{log.gasUsed}</Table.Col>
|
||||||
<Table.Col>{log.blockNumber}</Table.Col>
|
<Table.Col>{log.blockNumber}</Table.Col>
|
||||||
<Table.Col>{log.status}</Table.Col>
|
<Table.Col>{log.status}</Table.Col>
|
||||||
|
<Table.Col>{log.transactionHash}</Table.Col>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -109,8 +133,8 @@ class ContractLogger extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
ContractLogger.propTypes = {
|
ContractLogger.propTypes = {
|
||||||
contractName: PropTypes.string.isRequired,
|
contractLogs: PropTypes.array,
|
||||||
contractLogs: PropTypes.array.isRequired.Header,
|
contractEvents: PropTypes.array,
|
||||||
contract: PropTypes.object.isRequired
|
contract: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,31 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {contractLogs as contractLogsAction, listenToContractLogs} from '../actions';
|
import {contractEvents as contractEventsAction, contractLogs as contractLogsAction, listenToContractLogs, listenToContractEvents} from '../actions';
|
||||||
|
|
||||||
import ContractLogger from '../components/ContractLogger';
|
import ContractLogger from '../components/ContractLogger';
|
||||||
import DataWrapper from "../components/DataWrapper";
|
import DataWrapper from "../components/DataWrapper";
|
||||||
import {getContractLogsByContract} from "../reducers/selectors";
|
import {getContractLogsByContract, getContractEventsByContract} from "../reducers/selectors";
|
||||||
|
|
||||||
class ContractLoggerContainer extends Component {
|
class ContractLoggerContainer extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.contractLogs.length === 0) {
|
if (this.props.contractLogs.length === 0) {
|
||||||
this.props.listenToContractLogs();
|
this.props.listenToContractLogs();
|
||||||
this.props.fetchContractLogs(this.props.contract.className);
|
this.props.fetchContractLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.contractEvents.length === 0) {
|
||||||
|
this.props.listenToContractEvents();
|
||||||
|
this.props.fetchContractEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<DataWrapper shouldRender={this.props.contractLogs !== undefined } {...this.props} render={() => (
|
<DataWrapper shouldRender={this.props.contractLogs !== undefined } {...this.props} render={() => (
|
||||||
<ContractLogger contractLogs={this.props.contractLogs} contractName={this.props.contract.className}/>
|
<ContractLogger contractLogs={this.props.contractLogs}
|
||||||
|
contractEvents={this.props.contractEvents}
|
||||||
|
contract={this.props.contract}/>
|
||||||
)} />
|
)} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -26,15 +33,19 @@ class ContractLoggerContainer extends Component {
|
||||||
|
|
||||||
function mapStateToProps(state, props) {
|
function mapStateToProps(state, props) {
|
||||||
return {
|
return {
|
||||||
contractLogs: getContractLogsByContract(state, props.contract.className)
|
contractLogs: getContractLogsByContract(state, props.contract.className),
|
||||||
|
contractEvents: getContractEventsByContract(state, props.match.params.contractName)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ContractLoggerContainer.propTypes = {
|
ContractLoggerContainer.propTypes = {
|
||||||
contract: PropTypes.object,
|
contract: PropTypes.object,
|
||||||
contractLogs: PropTypes.array,
|
contractLogs: PropTypes.array,
|
||||||
|
contractEvents: PropTypes.array,
|
||||||
fetchContractLogs: PropTypes.func,
|
fetchContractLogs: PropTypes.func,
|
||||||
listenToContractLogs: PropTypes.func,
|
listenToContractLogs: PropTypes.func,
|
||||||
|
fetchContractEvents: PropTypes.func,
|
||||||
|
listenToContractEvents: PropTypes.func,
|
||||||
match: PropTypes.object
|
match: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,6 +53,8 @@ export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
{
|
{
|
||||||
fetchContractLogs: contractLogsAction.request,
|
fetchContractLogs: contractLogsAction.request,
|
||||||
listenToContractLogs: listenToContractLogs
|
listenToContractLogs: listenToContractLogs,
|
||||||
|
fetchContractEvents: contractEventsAction.request,
|
||||||
|
listenToContractEvents: listenToContractEvents
|
||||||
}
|
}
|
||||||
)(ContractLoggerContainer);
|
)(ContractLoggerContainer);
|
||||||
|
|
|
@ -21,6 +21,11 @@ const entitiesDefaultState = {
|
||||||
contractDeploys: [],
|
contractDeploys: [],
|
||||||
contractCompiles: [],
|
contractCompiles: [],
|
||||||
contractLogs: [],
|
contractLogs: [],
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
contractEvents: [],
|
||||||
|
commands: [],
|
||||||
|
>>>>>>> Add Contract Events to UI
|
||||||
messages: [],
|
messages: [],
|
||||||
messageChannels: [],
|
messageChannels: [],
|
||||||
versions: [],
|
versions: [],
|
||||||
|
|
|
@ -64,6 +64,10 @@ export function getContractLogsByContract(state, contractName) {
|
||||||
return state.entities.contractLogs.filter((contractLog => contractLog.name === contractName));
|
return state.entities.contractLogs.filter((contractLog => contractLog.name === contractName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getContractEventsByContract(state, contractName) {
|
||||||
|
return state.entities.contractEvents.filter((contractEvent => contractEvent.name === contractName));
|
||||||
|
}
|
||||||
|
|
||||||
export function getContracts(state) {
|
export function getContracts(state) {
|
||||||
return state.entities.contracts.filter(contract => !contract.isFiddle);
|
return state.entities.contracts.filter(contract => !contract.isFiddle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ export const fetchProcesses = doRequest.bind(null, actions.processes, api.fetchP
|
||||||
export const postCommand = doRequest.bind(null, actions.commands, api.postCommand);
|
export const postCommand = doRequest.bind(null, actions.commands, api.postCommand);
|
||||||
export const postCommandSuggestions = doRequest.bind(null, actions.commandSuggestions, api.postCommandSuggestions);
|
export const postCommandSuggestions = doRequest.bind(null, actions.commandSuggestions, api.postCommandSuggestions);
|
||||||
export const fetchProcessLogs = doRequest.bind(null, actions.processLogs, api.fetchProcessLogs);
|
export const fetchProcessLogs = doRequest.bind(null, actions.processLogs, api.fetchProcessLogs);
|
||||||
|
export const fetchContractEvents = doRequest.bind(null, actions.contractEvents, api.fetchContractEvents);
|
||||||
export const fetchContractLogs = doRequest.bind(null, actions.contractLogs, api.fetchContractLogs);
|
export const fetchContractLogs = doRequest.bind(null, actions.contractLogs, api.fetchContractLogs);
|
||||||
export const fetchContracts = doRequest.bind(null, actions.contracts, api.fetchContracts);
|
export const fetchContracts = doRequest.bind(null, actions.contracts, api.fetchContracts);
|
||||||
export const fetchContract = doRequest.bind(null, actions.contract, api.fetchContract);
|
export const fetchContract = doRequest.bind(null, actions.contract, api.fetchContract);
|
||||||
|
@ -98,6 +99,10 @@ export function *watchFetchContractLogs() {
|
||||||
yield takeEvery(actions.CONTRACT_LOGS[actions.REQUEST], fetchContractLogs);
|
yield takeEvery(actions.CONTRACT_LOGS[actions.REQUEST], fetchContractLogs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function *watchFetchContractEvents() {
|
||||||
|
yield takeEvery(actions.CONTRACT_EVENTS[actions.REQUEST], fetchContractEvents);
|
||||||
|
}
|
||||||
|
|
||||||
export function *watchFetchContract() {
|
export function *watchFetchContract() {
|
||||||
yield takeEvery(actions.CONTRACT[actions.REQUEST], fetchContract);
|
yield takeEvery(actions.CONTRACT[actions.REQUEST], fetchContract);
|
||||||
}
|
}
|
||||||
|
@ -278,6 +283,20 @@ export function *watchListenToContractLogs() {
|
||||||
yield takeEvery(actions.WATCH_NEW_CONTRACT_LOGS, listenToContractLogs);
|
yield takeEvery(actions.WATCH_NEW_CONTRACT_LOGS, listenToContractLogs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function *listenToContractEvents() {
|
||||||
|
const credentials = yield select(getCredentials);
|
||||||
|
const socket = api.webSocketContractEvents(credentials);
|
||||||
|
const channel = yield call(createChannel, socket);
|
||||||
|
while (true) {
|
||||||
|
const contractEvent = yield take(channel);
|
||||||
|
yield put(actions.contractEvents.success([contractEvent]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function *watchListenToContractEvents() {
|
||||||
|
yield takeEvery(actions.WATCH_NEW_CONTRACT_EVENTS, listenToContractEvents);
|
||||||
|
}
|
||||||
|
|
||||||
export function *listenGasOracle() {
|
export function *listenGasOracle() {
|
||||||
const credentials = yield select(getCredentials);
|
const credentials = yield select(getCredentials);
|
||||||
const socket = api.websocketGasOracle(credentials);
|
const socket = api.websocketGasOracle(credentials);
|
||||||
|
@ -318,8 +337,10 @@ export default function *root() {
|
||||||
fork(watchFetchProcesses),
|
fork(watchFetchProcesses),
|
||||||
fork(watchFetchProcessLogs),
|
fork(watchFetchProcessLogs),
|
||||||
fork(watchFetchContractLogs),
|
fork(watchFetchContractLogs),
|
||||||
|
fork(watchFetchContractEvents),
|
||||||
fork(watchListenToProcessLogs),
|
fork(watchListenToProcessLogs),
|
||||||
fork(watchListenToContractLogs),
|
fork(watchListenToContractLogs),
|
||||||
|
fork(watchListenToContractEvents),
|
||||||
fork(watchFetchBlock),
|
fork(watchFetchBlock),
|
||||||
fork(watchFetchTransactions),
|
fork(watchFetchTransactions),
|
||||||
fork(watchPostCommand),
|
fork(watchPostCommand),
|
||||||
|
|
|
@ -94,6 +94,10 @@ export function fetchContractLogs() {
|
||||||
return get(`/contracts/logs`, ...arguments);
|
return get(`/contracts/logs`, ...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fetchContractEvents() {
|
||||||
|
return get(`/blockchain/contracts/events`, ...arguments);
|
||||||
|
}
|
||||||
|
|
||||||
export function fetchContracts() {
|
export function fetchContracts() {
|
||||||
return get('/contracts', ...arguments);
|
return get('/contracts', ...arguments);
|
||||||
}
|
}
|
||||||
|
@ -179,6 +183,10 @@ export function webSocketContractLogs(credentials) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/contracts/logs`, [credentials.token]);
|
return new WebSocket(`ws://${credentials.host}/embark-api/contracts/logs`, [credentials.token]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function webSocketContractEvents(credentials) {
|
||||||
|
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/contracts/event`, [credentials.token]);
|
||||||
|
}
|
||||||
|
|
||||||
export function webSocketBlockHeader(credentials) {
|
export function webSocketBlockHeader(credentials) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/blockHeader`, [credentials.token]);
|
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/blockHeader`, [credentials.token]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue