feat(@cockpit/explorer): enable users to send ether through payable methods (#1649)

This commit is contained in:
Pascal Precht 2019-06-06 18:52:01 +02:00 committed by André Medeiros
parent ed65b066e7
commit d10c0b7951
4 changed files with 47 additions and 9 deletions

View File

@ -107,6 +107,9 @@ class ContractsManager {
contract: (callback) => {
self.events.request('contracts:contract', req.body.contractName, (contract) => callback(null, contract));
},
web3: (callback) => {
self.events.request("blockchain:get", web3 => callback(null, web3));
},
account: (callback) => {
self.events.request("blockchain:defaultAccount:get", (account) => callback(null, account));
}
@ -114,14 +117,20 @@ class ContractsManager {
if (error) {
return res.send({error: error.message});
}
const {account, contract} = result;
const {account, contract, web3} = result;
const abi = contract.abiDefinition.find(definition => definition.name === req.body.method);
const funcCall = (abi.constant === true || abi.stateMutability === 'view' || abi.stateMutability === 'pure') ? 'call' : 'send';
self.events.request("blockchain:contract:create", {abi: contract.abiDefinition, address: contract.deployedAddress}, async (contractObj) => {
try {
const gas = await contractObj.methods[req.body.method].apply(this, req.body.inputs).estimateGas();
contractObj.methods[req.body.method].apply(this, req.body.inputs)[funcCall]({from: account, gasPrice: req.body.gasPrice, gas: Math.floor(gas)}, (error, result) => {
const value = req.body.value !== undefined ? web3.utils.toWei(req.body.value, 'ether') : 0
const gas = await contractObj.methods[req.body.method].apply(this, req.body.inputs).estimateGas({ value });
contractObj.methods[req.body.method].apply(this, req.body.inputs)[funcCall]({
from: account,
gasPrice: req.body.gasPrice,
gas: Math.floor(gas),
value
}, (error, result) => {
const paramString = abi.inputs.map((input, idx) => {
const quote = input.type.indexOf("int") === -1 ? '"' : '';
return quote + req.body.inputs[idx] + quote;

View File

@ -214,7 +214,7 @@ export const contractFile = {
export const CONTRACT_FUNCTION = createRequestTypes('CONTRACT_FUNCTION');
export const contractFunction = {
post: (contractName, method, inputs, gasPrice) => action(CONTRACT_FUNCTION[REQUEST], {contractName, method, inputs, gasPrice}),
post: (contractName, method, inputs, gasPrice, value) => action(CONTRACT_FUNCTION[REQUEST], {contractName, method, inputs, gasPrice, value}),
success: (result, payload) => action(CONTRACT_FUNCTION[SUCCESS], {contractFunctions: [{...result, ...payload}]}),
failure: (error) => action(CONTRACT_FUNCTION[FAILURE], {error})
};

View File

@ -28,13 +28,23 @@ import "./ContractOverview.scss";
class ContractFunction extends Component {
constructor(props) {
super(props);
this.state = {inputs: {}, optionsCollapse: false, functionCollapse: false, gasPriceCollapse: false};
this.state = {
inputs: {},
optionsCollapse: false,
functionCollapse: false,
gasPriceCollapse: false,
value: 0
};
}
static isPureCall(method) {
return (method.stateMutability === 'view' || method.stateMutability === 'pure');
}
static isPayable(method) {
return method.payable;
}
static isEvent(method) {
return !this.isPureCall(method) && (method.type === 'event');
}
@ -59,9 +69,13 @@ class ContractFunction extends Component {
}
handleChange(e, name) {
const newInputs = this.state.inputs;
newInputs[name] = e.target.value;
this.setState({inputs: newInputs});
if (name === `${this.props.method.name}-value`) {
this.setState({ value: e.target.value });
} else {
const newInputs = this.state.inputs;
newInputs[name] = e.target.value;
this.setState({inputs: newInputs});
}
}
autoSetGasPrice(e) {
@ -78,7 +92,8 @@ class ContractFunction extends Component {
this.props.contractName,
this.props.method.name,
this.inputsAsArray(),
this.state.inputs.gasPrice * 1000000000
this.state.inputs.gasPrice * 1000000000,
this.state.value
);
}
@ -171,12 +186,25 @@ class ContractFunction extends Component {
: (ContractFunction.isPureCall(this.props.method) &&
this.makeBadge('success', 'white', 'call')) ||
this.makeBadge('warning', 'black', 'send')}
{ContractFunction.isPayable(this.props.method) &&
this.makeBadge('light', 'black', 'payable')
}
</div>
</CardTitle>
</CardHeader>
{!ContractFunction.isFallback(this.props.method) &&
<Collapse isOpen={this.state.functionCollapse} className="relative">
<CardBody>
{ContractFunction.isPayable(this.props.method) &&
<Form inline>
<Label for={this.props.method.name + '-value'} className="mr-2 font-weight-bold contract-function-input">ETH</Label>
<Input name={this.props.method.name}
id={this.props.method.name + '-value'}
onChange={(e) => this.handleChange(e, this.props.method.name + '-value')}
onKeyPress={(e) => this.handleKeyPress(e)}
/>
</Form>
}
<Form inline>
{this.props.method.inputs.map((input, idx) => (
<FormGroup key={idx}>

View File

@ -1,5 +1,6 @@
.contract-function-badge {
cursor: default;
margin-left: 1em;
code {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;