HenryNguyen5 efccac79ad Contracts UI (#277)
* Refactor BaseNode to be an interface INode

* Initial contract commit

* Remove redundant fallback ABI function

* First working iteration of Contract generator to be used in ENS branch

* Hide abi to clean up logging output

* Strip 0x prefix from output decode

* Handle unnamed output params

* Implement ability to supply output mappings to ABI functions

* Fix null case in outputMapping

* Add flow typing

* Add .call method to functions

* Partial commit for type refactor

* Temp contract type fix -- waiting for NPM modularization

* Misc. Optimizations to tsconfig + webpack

* Convert Contracts to TS

* Remove nested prop passing from contracts, get rid of contract reducers / sagas / redux state

* Add disclaimer modal to footer

* Remove duplicate code & unnecessary styles

* Add contracts to nav

* Wrap Contracts in App

* Add ether/hex validation override for contract creation calls

* First iteration of working deploy contract

* Delete routing file that shouldnt exist

* Revert "Misc. Optimizations to tsconfig + webpack"

This reverts commit 70cba3a07f4255153a9e277b3c41032a4b661c94.

* Cleanup HOC code

* Fix formatting noise

* remove un-used css style

* Remove deterministic contract address computation

* Remove empty files

* Cleanup contract

* Add call request to node interface

* Fix output mapping types

* Revert destructuring overboard

* Add sendCallRequest to rpcNode class and add typing

* Use enum for selecting ABI methods

* Fix tslint error & add media query for modals

* Nest Media Query

* Fix contracts to include new router fixes

* Add transaction capability to contracts

* Get ABI parsing + contract calls almost fully integrated using dynamic contract parser lib

* Refactor contract deploy to have a reusable HOC for contract interact

* Move modal and tx comparasion up file tree

* Include ABI  outputs in display

* Cleanup privaite/public members

* Remove broadcasting step from a contract transaction

* Update TX contract components to inter-op with interact and deploy

* Finish contracts-interact functionality

* Add transaction capability to contracts

* Cleanup privaite/public members

* Remove broadcasting step from a contract transaction

* Apply James's CSS fix

* Cleanup uneeded types

* Remove unecessary class

* Add UI side validation and helper utils, addresess PR comments

* Fix spacing + remove unused imports /  types

* Fix spacing + remove unused imports /  types

* Address PR comments

* Actually address PR comments

* Actually address PR comments
2017-10-16 21:01:28 -07:00

206 lines
5.3 KiB
TypeScript

import React, { Component } from 'react';
import InteractForm from './components/InteractForm';
import InteractExplorer from './components//InteractExplorer';
import Contract from 'libs/contracts';
import { withTx, IWithTx } from '../withTx';
import {
TxModal,
Props as DMProps,
TTxModal
} from 'containers/Tabs/Contracts/components/TxModal';
import { IUserSendParams } from 'libs/contracts/ABIFunction';
import Big from 'bignumber.js';
import {
TxCompare,
Props as TCProps,
TTxCompare
} from 'containers/Tabs/Contracts/components/TxCompare';
interface State {
currentContract: Contract | null;
showExplorer: boolean;
address: string | null;
signedTx: string | null;
rawTx: any | null;
gasLimit: string;
value: string;
displayModal: boolean;
}
class Interact extends Component<IWithTx, State> {
public initialState: State = {
currentContract: null,
showExplorer: false,
address: null,
signedTx: null,
rawTx: null,
gasLimit: '30000',
value: '0',
displayModal: false
};
public state: State = this.initialState;
public componentWillReceiveProps(nextProps: IWithTx) {
if (nextProps.wallet && this.state.currentContract) {
Contract.setConfigForTx(this.state.currentContract, nextProps);
}
}
public accessContract = (contractAbi: string, address: string) => () => {
try {
const parsedAbi = JSON.parse(contractAbi);
const contractInstance = new Contract(parsedAbi);
contractInstance.at(address);
contractInstance.setNode(this.props.nodeLib);
this.setState({
currentContract: contractInstance,
showExplorer: true,
address
});
} catch (e) {
this.props.showNotification(
'danger',
`Contract Access Error: ${(e as Error).message ||
'Can not parse contract'}`
);
this.resetState();
}
};
public render() {
const {
showExplorer,
currentContract,
gasLimit,
value,
signedTx,
displayModal
} = this.state;
const { wallet, showNotification } = this.props;
const txGenerated = !!signedTx;
return (
<div className="Interact">
<InteractForm
accessContract={this.accessContract}
resetState={this.resetState}
/>
<hr />
{showExplorer &&
currentContract && (
<InteractExplorer
{...{
address: currentContract.address,
walletDecrypted: !!wallet,
handleInput: this.handleInput,
contractFunctions: Contract.getFunctions(currentContract),
gasLimit,
value,
handleFunctionSend: this.handleFunctionSend,
txGenerated,
txModal: txGenerated ? this.makeModal() : null,
txCompare: txGenerated ? this.makeCompareTx() : null,
toggleModal: this.toggleModal,
displayModal,
showNotification
}}
/>
)}
</div>
);
}
private makeCompareTx = (): React.ReactElement<TTxCompare> => {
const { nonce, gasLimit, data, value, to, gasPrice } = this.state.rawTx;
const { signedTx } = this.state;
const { chainId } = this.props;
if (!nonce || !signedTx) {
throw Error('Can not display raw tx, nonce empty or no signed tx');
}
const props: TCProps = {
nonce,
gasPrice,
chainId,
data,
gasLimit,
to,
value,
signedTx
};
return <TxCompare {...props} />;
};
private makeModal = (): React.ReactElement<TTxModal> => {
const { networkName, node: { network, service } } = this.props;
const { signedTx } = this.state;
if (!signedTx) {
throw Error('Can not deploy contract, no signed tx');
}
const props: DMProps = {
action: 'send a contract state modifying transaction',
networkName,
network,
service,
handleBroadcastTx: this.handleBroadcastTx,
onClose: this.resetState
};
return <TxModal {...props} />;
};
private toggleModal = () => this.setState({ displayModal: true });
private resetState = () => this.setState(this.initialState);
private handleBroadcastTx = () => {
const { signedTx } = this.state;
if (!signedTx) {
return null;
}
this.props.broadcastTx(signedTx);
this.resetState();
};
private handleFunctionSend = (selectedFunction, inputs) => async () => {
try {
const { address, gasLimit, value } = this.state;
if (!address) {
return null;
}
const parsedInputs = Object.keys(inputs).reduce(
(accu, key) => ({ ...accu, [key]: inputs[key].parsedData }),
{}
);
const userInputs: IUserSendParams = {
input: parsedInputs,
to: address,
gasLimit: new Big(gasLimit),
value
};
const { signedTx, rawTx } = await selectedFunction.send(userInputs);
this.setState({ signedTx, rawTx });
} catch (e) {
this.props.showNotification(
'danger',
`Function send error: ${(e as Error).message}` ||
'Invalid input parameters',
5000
);
}
};
private handleInput = name => ev =>
this.setState({ [name]: ev.target.value });
}
export default withTx(Interact);