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-04-19 16:09:46 +00:00
|
|
|
|
2018-08-02 19:17:40 +00:00
|
|
|
let btnClass = "btn ml-auto ";
|
|
|
|
let disabled = false;
|
2018-04-19 16:09:46 +00:00
|
|
|
|
2018-08-02 19:17:40 +00:00
|
|
|
if (this.state.onRequest) {
|
|
|
|
disabled = true;
|
|
|
|
}
|
2018-04-19 16:09:46 +00:00
|
|
|
|
2018-08-02 19:17:40 +00:00
|
|
|
if (this.props.definition.code === "") {
|
|
|
|
btnClass += "btn-secondary";
|
|
|
|
disabled = true;
|
|
|
|
} else {
|
|
|
|
btnClass += "btn-primary";
|
|
|
|
}
|
2018-04-19 16:09:46 +00:00
|
|
|
|
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>⏎</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;
|