embark-area-51/lib/modules/webserver/backend/contracts/components/function.js

272 lines
9.6 KiB
JavaScript
Raw Normal View History

2018-08-02 19:17:40 +00:00
/*global web3*/
import React from 'react';
import ContractContext from './contract-context';
import PropTypes from 'prop-types';
2018-04-16 18:05:57 +00:00
class Function extends React.Component {
2018-08-02 19:17:40 +00:00
constructor(props) {
super(props);
this.state = {
onRequest: false,
fields: {},
methodFields: {
from: '',
to: '',
value: 0,
data: '',
gasLimit: '7000000'
},
receipt: null
};
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
this.handleParameterChange = this.handleParameterChange.bind(this);
this.handleMethodFieldChange = this.handleMethodFieldChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
2018-04-19 18:42:11 +00:00
2018-08-02 19:17:40 +00:00
copyCommand(e) {
e.preventDefault();
2018-04-19 18:42:11 +00:00
2018-08-02 19:17:40 +00:00
let functionLabel = this._getFunctionLabel();
let functionParams = this._getFunctionParamString();
let methodParams = this._getMethodString();
if (this.props.abi.type === "constructor") functionParams = `{arguments: [${functionParams}]}`;
2018-04-19 18:42:11 +00:00
2018-08-02 19:17:40 +00:00
const command = `await ${functionLabel}(${functionParams})${this.props.abi.type !== 'fallback' ? '.' + this._getMethodType() : ''}${methodParams}`;
2018-04-19 18:42:11 +00:00
2018-08-02 19:17:40 +00:00
var dummy = document.createElement("input");
document.body.appendChild(dummy);
dummy.setAttribute('value', command);
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
}
2018-04-19 18:42:11 +00:00
2018-08-02 19:17:40 +00:00
async handleClick(e, instanceUpdateCallback) {
e.preventDefault();
2018-04-19 18:42:11 +00:00
2018-08-02 19:17:40 +00:00
this.setState({onRequest: true, receipt: null});
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
this.props.resultHandler(false, null, null);
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
let executionParams = {
from: this.state.methodFields.from,
gasLimit: this.state.methodFields.gasLimit
};
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
if (this.props.abi.payable) executionParams.value = this.state.methodFields.value;
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
if (this.props.abi.type === 'fallback') {
executionParams.data = this.state.methodFields.data;
executionParams.to = this.props.contract.options.address;
2018-04-16 18:05:57 +00:00
}
2018-08-02 19:17:40 +00:00
let fields = this.state.fields;
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
let functionLabel = this._getFunctionLabel();
let functionParams = this._getFunctionParamString();
let methodParams = this._getMethodString();
if (this.props.abi.type === "constructor") functionParams = `{arguments: [${functionParams}]}`;
console.log(`%cawait ${functionLabel}(${functionParams})${this.props.abi.type !== 'fallback' ? '.' + this._getMethodType() : ''}${methodParams}`, 'font-weight: bold');
let _receipt;
try {
if (this.props.abi.type === 'constructor') {
let contractInstance = await this.props.contract.deploy({arguments: Object.keys(fields).map(val => fields[val])}).send(executionParams);
instanceUpdateCallback(contractInstance.options.address);
this.setState({onRequest: false});
console.log(contractInstance.options.address);
this.props.resultHandler(false, 'New instance: ' + contractInstance.options.address);
} else {
if (this.props.abi.type === 'fallback') _receipt = await web3.eth.sendTransaction(executionParams);
else _receipt = await this.props.contract
.methods[this.props.abi.name + '(' + this.props.abi.inputs.map(input => input.type).join(',') + ')']
.apply(null, Object.keys(fields).map(val => {
let input = this.props.abi.inputs.filter(x => x.name === val)[0];
return input.type.indexOf('bool') === -1 ? fields[val] : (fields[val].toLowerCase() === 'true');
}))[this._getMethodType()](executionParams);
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
if (this._getMethodType() === 'call') {
this.props.resultHandler(false, _receipt, null);
} else {
this.props.resultHandler(false, null, _receipt);
2018-04-16 18:05:57 +00:00
}
2018-08-02 19:17:40 +00:00
this.setState({onRequest: false, receipt: _receipt});
console.log(_receipt);
}
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
} catch (err) {
console.error('%s: %s', err.name, err.message);
this.setState({onRequest: false});
this.props.resultHandler(true, err.name + ": " + err.message, _receipt);
2018-04-16 18:05:57 +00:00
}
2018-08-02 19:17:40 +00:00
}
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
handleParameterChange(e) {
let newState = this.state;
newState.fields[e.target.getAttribute('data-name')] = e.target.value;
this.setState(newState);
}
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
handleMethodFieldChange(e) {
let newState = this.state;
newState.methodFields[e.target.getAttribute('data-param')] = e.target.value;
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
if (e.target.getAttribute('data-param') === 'from') {
newState.selectedAccount = e.target.options[e.target.selectedIndex].text;
2018-04-16 18:05:57 +00:00
}
2018-08-02 19:17:40 +00:00
this.setState(newState);
}
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
_getFunctionLabel() {
if (this.props.abi.type === 'function') if (!this.props.duplicated) return `${this.props.contractName}.methods.${this.props.abi.name}`;
else {
return `${this.props.contractName}.methods['${this.props.abi.name + '(' + (this.props.abi.inputs !== null ? this.props.abi.inputs.map(input => input.type).join(',') : '') + ')'}']`;
}
else if (this.props.abi.type === 'fallback') {
return `web3.eth.sendTransaction`;
2018-04-16 18:05:57 +00:00
}
2018-08-02 19:17:40 +00:00
else return `${this.props.contractName}.deploy`;
}
_getMethodType() {
return (this.props.abi.constant === true || this.props.abi.stateMutability === 'view' || this.props.abi.stateMutability === 'pure') ? 'call' : 'send';
}
_getMethodFields(accounts) {
return <React.Fragment>
from: <select data-param="from" disabled={accounts.length === 0} value={this.state.from}
onChange={this.handleMethodFieldChange}>
<option>-</option>
{
accounts.map(function(item, i) {
return <option key={i} value={item}>{`accounts[${i}]`}</option>;
})
}
</select>
{this.props.abi.payable &&
<span>, value:
<input type="text" data-param="value" value={this.state.methodFields.value} size="6"
onChange={this.handleMethodFieldChange}/>
</span>
}
{this._getMethodType() === 'send' &&
<span>, gasLimit:
<input type="text" data-param="gasLimit" value={this.state.methodFields.gasLimit} size="6"
onChange={this.handleMethodFieldChange}/>
</span>}
{this._getMethodType() === 'send' && this.props.abi.type === 'fallback' &&
<span>, data:
<input type="text" data-param="data" value={this.state.methodFields.data} size="6"
onChange={this.handleMethodFieldChange}/>
</span>}
</React.Fragment>;
}
_getFunctionParamFields() {
return <React.Fragment>
{
this.props.abi.inputs
.map((input, i) => <input key={i} type="text" data-var-type={input.type} data-type="inputParam"
data-name={input.name} placeholder={input.name}
title={input.type + ' ' + input.name} size={input.name.length}
value={this.state.fields[input.name] || ''} onChange={this.handleParameterChange}/>)
.reduce((accu, elem) => {
return accu === null ? [elem] : [...accu, ', ', elem];
}, null)
}
</React.Fragment>;
}
_getFunctionParamString() {
if (this.props.abi.type === 'fallback') return '';
return this.props.abi.inputs
.map((input, _i) => (input.type.indexOf('int') === -1 && input.type.indexOf('bool') === -1 ? '"' : '') + (this.state.fields[input.name] || (input.type.indexOf('int') === -1 ? '' : '0')) + (input.type.indexOf('int') === -1 ? '"' : ''))
.join(', ');
}
_getMethodString(_elem) {
let methodParams = "({";
methodParams += `from: ` + (this.state.selectedAccount || '"0x00"');
if (this._getMethodType() === 'send') {
methodParams += ', gasLimit: ' + (this.state.methodFields.gasLimit || 0);
if (this.props.abi.payable) {
methodParams += ', value: ' + (this.state.methodFields.value || 0);
}
if (this.props.abi.type === 'fallback') {
methodParams += ', data: "' + (this.state.methodFields.data || '"0x00"') + '", to: "' + (this.state.methodFields.to || '"0x00"') + '"';
}
}
return methodParams + "})";
}
2018-04-16 18:05:57 +00:00
2018-08-02 19:17:40 +00:00
render() {
2018-08-02 19:17:40 +00:00
let btnClass = "btn ml-auto ";
let disabled = false;
2018-08-02 19:17:40 +00:00
if (this.state.onRequest) {
disabled = true;
}
2018-08-02 19:17:40 +00:00
if (this.props.definition.code === "") {
btnClass += "btn-secondary";
disabled = true;
} else {
btnClass += "btn-primary";
}
2018-08-02 19:17:40 +00:00
let requestResult;
if (this.state.onRequest) {
requestResult = <img src="../assets/images/loading.gif" className="loading" alt=""/>;
} else {
requestResult = this.props.definition.code === "" ? <React.Fragment>_</React.Fragment>: <React.Fragment>&#9166;</React.Fragment>;
2018-04-16 18:05:57 +00:00
}
2018-08-02 19:17:40 +00:00
return <ContractContext.Consumer>
{(context) => (
<React.Fragment>
<div className="col-md-11">
<code>
await {this._getFunctionLabel()}
{this.props.abi.type !== 'fallback' ? '(' : ''}
{this.props.abi.type !== 'fallback' ? this._getFunctionParamFields() : ''}
{this.props.abi.type !== 'fallback' ? ')' : ''}
{this.props.abi.type !== 'fallback' ? '.' + this._getMethodType() : ''}
({this._getMethodFields(context.accounts)})
</code>
</div>
<div className="col-md-1">
<button className={btnClass}
title={this.props.definition.code === "" ? "Can't execute function" : "Execute function"}
onClick={event => this.handleClick(event, context.updateInstances)} disabled={disabled}>
{requestResult}
</button>
</div>
</React.Fragment>
)}
</ContractContext.Consumer>;
}
}
Function.propTypes = {
definition: PropTypes.object,
abi: PropTypes.object,
contract: PropTypes.object,
resultHandler: PropTypes.func,
duplicated: PropTypes.bool,
contractName: PropTypes.string
};
export default Function;