Skip to content

Commit

Permalink
feat(@cockpit): Pass tx value as wei and add validation
Browse files Browse the repository at this point in the history
For payable methods inside of the contract interatction section of cockpit, the value input has been updated to pass wei to the API. The input field now accepts value like `100 ether` or 25 szabo`. The value entered is automatically converted to wei and shown to the user in real time.

Additionally, the input is validated for a correct value, with an error shown to the user for incorrectly entered values.

A tooltip has been added to help the user enter correct values.

UI updates can be seen in video here: https://monosnap.com/file/642cHH2HxDeiFLzB2VLqHP9GuqoRfz
  • Loading branch information
emizzle authored and michaelsbradleyjr committed Jul 8, 2019
1 parent 70ff3c1 commit 536a402
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 14 deletions.
3 changes: 1 addition & 2 deletions packages/embark-contracts-manager/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,13 @@ class ContractsManager {
if (error) {
return res.send({error: error.message});
}
const {account, contract, web3} = result;
const {account, contract} = 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 {
let value = typeof req.body.value === "number" ? req.body.value.toString() : req.body.value;
value = web3.utils.toWei(value, 'ether');
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,
Expand Down
1 change: 1 addition & 0 deletions packages/embark-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"babel-plugin-named-asset-import": "0.2.3",
"babel-preset-react-app": "6.1.0",
"bfj": "6.1.1",
"bignumber.js": "2.0.7",
"case-sensitive-paths-webpack-plugin": "2.1.2",
"chalk": "2.4.2",
"classnames": "2.2.6",
Expand Down
35 changes: 29 additions & 6 deletions packages/embark-ui/src/components/ContractOverview.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ import {
CardTitle,
CardFooter,
Collapse,
FormFeedback,
ListGroup,
ListGroupItem
ListGroupItem,
UncontrolledTooltip
} from "reactstrap";
import GasStationContainer from "../containers/GasStationContainer";
import {formatContractForDisplay} from '../utils/presentation';
import FontAwesome from 'react-fontawesome';
import classnames from 'classnames';
import {getWeiBalanceFromString} from '../utils/utils';
import {ETHER_UNITS} from '../constants';

import "./ContractOverview.scss";

Expand All @@ -33,7 +37,9 @@ class ContractFunction extends Component {
optionsCollapse: false,
functionCollapse: false,
gasPriceCollapse: false,
value: 0
value: -1,
unitConvertError: null,
unitConvertDirty: false
};
}

Expand Down Expand Up @@ -70,7 +76,15 @@ class ContractFunction extends Component {

handleChange(e, name) {
if (name === `${this.props.method.name}-value`) {
this.setState({ value: e.target.value });
this.setState({ unitConvertDirty: true });
try {
const weiBalance = getWeiBalanceFromString(e.target.value);
this.setState({ value: weiBalance, unitConvertError: null });
}
catch (e) {
this.setState({ unitConvertError: e.message });
}

} else {
const newInputs = this.state.inputs;
newInputs[name] = e.target.value;
Expand Down Expand Up @@ -108,7 +122,7 @@ class ContractFunction extends Component {
}

callDisabled() {
return this.inputsAsArray().length !== this.props.method.inputs.length;
return (this.inputsAsArray().length !== this.props.method.inputs.length) || this.state.unitConvertError;
}

toggleOptions() {
Expand Down Expand Up @@ -196,13 +210,22 @@ class ContractFunction extends Component {
<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>
<Form inline className="mb-1">
<Label for={this.props.method.name + '-value'} className="mr-2 font-weight-bold contract-function-input">Transaction value</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)}
invalid={this.state.unitConvertError && this.state.unitConvertDirty ? true : null}
valid={!this.state.unitConvertError && this.state.unitConvertDirty ? true : null}
/>
{<FormFeedback valid={this.state.unitConvertError && this.state.unitConvertDirty ? null : true} tooltip>{this.state.unitConvertError ? this.state.unitConvertError : this.state.value} wei</FormFeedback>}
<Button close id="PopoverFocus" className="ml-2" type="button">
<FontAwesome name="question-circle"/>
</Button>
<UncontrolledTooltip trigger="focus" placement="bottom" target="PopoverFocus">
Enter a number followed by an ether unit, ie <code>0.5 ether</code> or <code>16 szabo</code>.<br/><br/>If a number is entered without a unit (ie <code>21000</code>), then <code>wei</code> is assumed.<br/><br/>Valid units include {ETHER_UNITS.map((unit, idx) => <React.Fragment><code>{unit}</code><span>{idx !== ETHER_UNITS.length - 1 ? ", " : ""}</span></React.Fragment>)}
</UncontrolledTooltip>
</Form>
}
<Form inline>
Expand Down
4 changes: 4 additions & 0 deletions packages/embark-ui/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ export const OPERATIONS = {
LESS: -1
};
export const PAGE_TITLE_PREFIX = "Embark Cockpit";
export const BALANCE_REGEX = /([0-9]+(?:\.[0-9]+)?)(?: ?([a-zA-Z]*))?/;
export const ETHER_UNITS = [
"noether", "wei", "kwei", "Kwei", "babbage", "femtoether", "mwei", "Mwei", "lovelace", "picoether", "gwei", "Gwei", "shannon", "nanoether", "nano", "szabo", "microether", "micro", "finney", "milliether", "milli", "ether", "kether", "grand", "mether", "gether", "tether"
];
21 changes: 20 additions & 1 deletion packages/embark-ui/src/utils/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Convert from 'ansi-to-html';
import qs from 'qs';

import {DARK_THEME} from '../constants';
import {DARK_THEME, BALANCE_REGEX} from '../constants';

import {toWei} from 'web3-utils';
import BigNumber from 'bignumber.js';

export function last(array) {
return array && array.length ? array[array.length - 1] : undefined;
Expand Down Expand Up @@ -49,3 +52,19 @@ export function stripQueryParam(location, param) {
}

export const isDarkTheme = (theme) => theme === DARK_THEME;

export function getWeiBalanceFromString(balanceString: string) {
if (!balanceString) {
return 0;
}
const match = balanceString.match(BALANCE_REGEX);
if (!match) {
throw new Error(`Unrecognized balance string "${balanceString}"`);
}
// if no units passed in, assume we are dealing with wei
if (!match[2]) {
return new BigNumber(match[1]).toString(10);
}

return toWei(match[1], match[2]);
}
2 changes: 1 addition & 1 deletion packages/embark-utils/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const unitRegex = /([0-9]+) ([a-zA-Z]+)/;
export const balanceRegex = /([0-9]+) ?([a-zA-Z]*)/;
export const balanceRegex = /([0-9]+(?:\.[0-9]+)?)(?: ?([a-zA-Z]*))?/;
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4542,15 +4542,15 @@ big.js@^3.1.3:
resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==

bignumber.js@2.0.7, "bignumber.js@git+https://github.com/frozeman/bignumber.js-nolookahead.git":
version "2.0.7"
resolved "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934"

bignumber.js@^2.3.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-2.4.0.tgz#838a992da9f9d737e0f4b2db0be62bb09dd0c5e8"
integrity sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg=

"bignumber.js@git+https://github.com/frozeman/bignumber.js-nolookahead.git":
version "2.0.7"
resolved "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934"

binary-extensions@^1.0.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14"
Expand Down

0 comments on commit 536a402

Please sign in to comment.