diff --git a/ethstore/src/ethstore.rs b/ethstore/src/ethstore.rs index 4360a39f0..4991c4714 100644 --- a/ethstore/src/ethstore.rs +++ b/ethstore/src/ethstore.rs @@ -86,7 +86,7 @@ impl SecretStore for EthStore { fn insert_account(&self, secret: Secret, password: &str) -> Result { let keypair = try!(KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)); let id: [u8; 16] = Random::random(); - let account = SafeAccount::create(&keypair, id, password, self.iterations, UUID::from(id).into(), "{}".to_owned()); + let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned()); let address = account.address.clone(); try!(self.save(account)); Ok(address) diff --git a/js/package.json b/js/package.json index af56140b1..f9ef9c181 100644 --- a/js/package.json +++ b/js/package.json @@ -43,6 +43,7 @@ "test": "mocha 'src/**/*.spec.js'", "test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'", "test:e2e": "mocha 'src/**/*.e2e.js'", + "test:npm": "(cd .npmjs && npm i) && node test/npmLibrary && (rm -rf .npmjs/node_modules)", "prepush": "npm run lint:cached" }, "devDependencies": { diff --git a/js/parity.package.json b/js/parity.package.json index 7d18cc5ed..0974e072f 100644 --- a/js/parity.package.json +++ b/js/parity.package.json @@ -27,6 +27,7 @@ }, "dependencies": { "bignumber.js": "^2.3.0", - "js-sha3": "^0.5.2" + "js-sha3": "^0.5.2", + "node-fetch": "^1.6.3" } } diff --git a/js/src/api/transport/ws/ws.js b/js/src/api/transport/ws/ws.js index 1cb1fb1c4..7b214fded 100644 --- a/js/src/api/transport/ws/ws.js +++ b/js/src/api/transport/ws/ws.js @@ -84,7 +84,7 @@ export default class Ws extends JsonRpcBase { this._connecting = false; if (this._autoConnect) { - this._connect(); + setTimeout(() => this._connect(), 500); } } diff --git a/js/src/library.js b/js/src/library.js index fbbab2286..2b526c28e 100644 --- a/js/src/library.js +++ b/js/src/library.js @@ -14,10 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import 'babel-polyfill/dist/polyfill.js'; +import es6Promise from 'es6-promise'; +es6Promise.polyfill(); + +const isNode = typeof global !== 'undefined' && typeof global !== 'undefined'; +const isBrowser = typeof self !== 'undefined' && typeof self.window !== 'undefined'; + +if (isBrowser) { + require('whatwg-fetch'); +} + +if (isNode) { + global.fetch = require('node-fetch'); +} + import Abi from './abi'; import Api from './api'; -export { - Abi, - Api -}; +module.exports = { Api, Abi }; diff --git a/js/src/modals/AddContract/addContract.css b/js/src/modals/AddContract/addContract.css index ed92a86d5..0821a180e 100644 --- a/js/src/modals/AddContract/addContract.css +++ b/js/src/modals/AddContract/addContract.css @@ -14,19 +14,3 @@ /* You should have received a copy of the GNU General Public License /* along with Parity. If not, see . */ - -.spaced { - margin: 0.25em 0; -} - -.typeContainer { - display: flex; - flex-direction: column; - - .desc { - font-size: 0.8em; - margin-bottom: 0.5em; - color: #ccc; - z-index: 2; - } -} diff --git a/js/src/modals/AddContract/addContract.js b/js/src/modals/AddContract/addContract.js index 4c73d3da0..110a91837 100644 --- a/js/src/modals/AddContract/addContract.js +++ b/js/src/modals/AddContract/addContract.js @@ -20,13 +20,10 @@ import ContentClear from 'material-ui/svg-icons/content/clear'; import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward'; import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back'; -import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'; - -import { Button, Modal, Form, Input, InputAddress } from '../../ui'; +import { Button, Modal, Form, Input, InputAddress, RadioButtons } from '../../ui'; import { ERRORS, validateAbi, validateAddress, validateName } from '../../util/validation'; import { eip20, wallet } from '../../contracts/abi'; -import styles from './addContract.css'; const ABI_TYPES = [ { @@ -105,13 +102,12 @@ export default class AddContract extends Component { const { abiTypeIndex } = this.state; return ( - - { this.renderAbiTypes() } - + /> ); } @@ -194,20 +190,13 @@ export default class AddContract extends Component { ); } - renderAbiTypes () { - return ABI_TYPES.map((type, index) => ( - - { type.label } - { type.description } - - ) } - key={ index } - /> - )); + getAbiTypes () { + return ABI_TYPES.map((type, index) => ({ + label: type.label, + description: type.description, + key: index, + ...type + })); } onNext = () => { @@ -218,8 +207,8 @@ export default class AddContract extends Component { this.setState({ step: this.state.step - 1 }); } - onChangeABIType = (event, index) => { - const abiType = ABI_TYPES[index]; + onChangeABIType = (value, index) => { + const abiType = value || ABI_TYPES[index]; this.setState({ abiTypeIndex: index, abiType }); this.onEditAbi(abiType.value); } diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 6e23f79c9..06b5a85bb 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -15,13 +15,12 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { MenuItem } from 'material-ui'; -import { AddressSelect, Form, Input, TypedInput } from '../../../ui'; +import { AddressSelect, Form, Input, Select } from '../../../ui'; import { validateAbi } from '../../../util/validation'; import { parseAbiType } from '../../../util/abi'; -import styles from '../deployContract.css'; - export default class DetailsStep extends Component { static contextTypes = { api: PropTypes.object.isRequired @@ -29,24 +28,26 @@ export default class DetailsStep extends Component { static propTypes = { accounts: PropTypes.object.isRequired, - abi: PropTypes.string, - abiError: PropTypes.string, - code: PropTypes.string, - codeError: PropTypes.string, - description: PropTypes.string, - descriptionError: PropTypes.string, + + onFromAddressChange: PropTypes.func.isRequired, + onNameChange: PropTypes.func.isRequired, + onDescriptionChange: PropTypes.func.isRequired, + onAbiChange: PropTypes.func.isRequired, + onCodeChange: PropTypes.func.isRequired, + onParamsChange: PropTypes.func.isRequired, + onInputsChange: PropTypes.func.isRequired, + fromAddress: PropTypes.string, fromAddressError: PropTypes.string, name: PropTypes.string, nameError: PropTypes.string, - params: PropTypes.array, - paramsError: PropTypes.array, - onAbiChange: PropTypes.func.isRequired, - onCodeChange: PropTypes.func.isRequired, - onFromAddressChange: PropTypes.func.isRequired, - onDescriptionChange: PropTypes.func.isRequired, - onNameChange: PropTypes.func.isRequired, - onParamsChange: PropTypes.func.isRequired, + description: PropTypes.string, + descriptionError: PropTypes.string, + abi: PropTypes.string, + abiError: PropTypes.string, + code: PropTypes.string, + codeError: PropTypes.string, + readOnly: PropTypes.bool }; @@ -55,7 +56,9 @@ export default class DetailsStep extends Component { }; state = { - inputs: [] + solcOutput: '', + contracts: {}, + selectedContractIndex: 0 } componentDidMount () { @@ -63,6 +66,7 @@ export default class DetailsStep extends Component { if (abi) { this.onAbiChange(abi); + this.setState({ solcOutput: abi }); } if (code) { @@ -71,8 +75,19 @@ export default class DetailsStep extends Component { } render () { - const { accounts } = this.props; - const { abi, abiError, code, codeError, fromAddress, fromAddressError, name, nameError, readOnly } = this.props; + const { + accounts, + readOnly, + + fromAddress, fromAddressError, + name, nameError, + description, descriptionError, + abiError, + code, codeError + } = this.props; + + const { solcOutput, contracts } = this.state; + const solc = contracts && Object.keys(contracts).length > 0; return (
@@ -83,18 +98,30 @@ export default class DetailsStep extends Component { error={ fromAddressError } accounts={ accounts } onChange={ this.onFromAddressChange } /> + + onChange={ this.onNameChange } /> + + + { this.renderContractSelect() } + + + readOnly={ readOnly || solc } /> - { this.renderConstructorInputs() }
); } - renderConstructorInputs () { - const { accounts, params, paramsError } = this.props; - const { inputs } = this.state; + renderContractSelect () { + const { contracts } = this.state; - if (!inputs || !inputs.length) { + if (!contracts || Object.keys(contracts).length === 0) { return null; } - return inputs.map((input, index) => { - const onChange = (value) => this.onParamChange(index, value); + const { selectedContractIndex } = this.state; + const contractsItems = Object.keys(contracts).map((name, index) => ( + + { name } + + )); - const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; - const value = params[index]; - const error = paramsError[index]; - const param = parseAbiType(input.type); + return ( + + ); + } - return ( -
- -
- ); + onContractChange = (event, index) => { + const { contracts } = this.state; + const contractName = Object.keys(contracts)[index]; + const contract = contracts[contractName]; + + const { abi, bin } = contract; + const code = /^0x/.test(bin) ? bin : `0x${bin}`; + + this.setState({ selectedContractIndex: index }, () => { + this.onAbiChange(abi); + this.onCodeChange(code); }); } + onSolcChange = (event, value) => { + // Change triggered only if valid + if (this.props.abiError) { + return null; + } + + this.onSolcSubmit(value); + } + + onSolcSubmit = (value) => { + try { + const solcParsed = JSON.parse(value); + + if (!solcParsed || !solcParsed.contracts) { + throw new Error('Wrong solc output'); + } + + this.setState({ contracts: solcParsed.contracts }, () => { + this.onContractChange(null, 0); + }); + } catch (e) { + this.setState({ contracts: null }); + this.onAbiChange(value); + } + + this.setState({ solcOutput: value }); + } + onFromAddressChange = (event, fromAddress) => { const { onFromAddressChange } = this.props; onFromAddressChange(fromAddress); } - onNameChange = (name) => { + onNameChange = (event, name) => { const { onNameChange } = this.props; onNameChange(name); } - onParamChange = (index, value) => { - const { params, onParamsChange } = this.props; + onDescriptionChange = (event, description) => { + const { onDescriptionChange } = this.props; - params[index] = value; - onParamsChange(params); + onDescriptionChange(description); } onAbiChange = (abi) => { const { api } = this.context; - const { onAbiChange, onParamsChange } = this.props; + const { onAbiChange, onParamsChange, onInputsChange } = this.props; const { abiError, abiParsed } = validateAbi(abi, api); if (!abiError) { @@ -176,10 +241,10 @@ export default class DetailsStep extends Component { }); onParamsChange(params); - this.setState({ inputs }); + onInputsChange(inputs); } else { onParamsChange([]); - this.setState({ inputs: [] }); + onInputsChange([]); } onAbiChange(abi); diff --git a/js/src/modals/DeployContract/ParametersStep/index.js b/js/src/modals/DeployContract/ParametersStep/index.js new file mode 100644 index 000000000..77545b406 --- /dev/null +++ b/js/src/modals/DeployContract/ParametersStep/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './parametersStep'; diff --git a/js/src/modals/DeployContract/ParametersStep/parametersStep.js b/js/src/modals/DeployContract/ParametersStep/parametersStep.js new file mode 100644 index 000000000..7916c9f5a --- /dev/null +++ b/js/src/modals/DeployContract/ParametersStep/parametersStep.js @@ -0,0 +1,105 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import React, { Component, PropTypes } from 'react'; + +import { Form, TypedInput } from '../../../ui'; +import { parseAbiType } from '../../../util/abi'; + +import styles from '../deployContract.css'; + +export default class ParametersStep extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + }; + + static propTypes = { + accounts: PropTypes.object.isRequired, + onParamsChange: PropTypes.func.isRequired, + + inputs: PropTypes.array, + params: PropTypes.array, + paramsError: PropTypes.array + }; + + render () { + return ( +
+ { this.renderConstructorInputs() } +
+ ); + } + + renderConstructorInputs () { + const { accounts, params, paramsError } = this.props; + const { inputs } = this.props; + + if (!inputs || !inputs.length) { + return null; + } + + const inputsComponents = inputs.map((input, index) => { + const onChange = (value) => this.onParamChange(index, value); + + const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; + const value = params[index]; + const error = paramsError[index]; + const param = parseAbiType(input.type); + + return ( +
+ +
+ ); + }); + + return ( +
+

Choose the contract parameters

+ { inputsComponents } +
+ ); + } + + onParamChange = (index, value) => { + const { params, onParamsChange } = this.props; + + params[index] = value; + onParamsChange(params); + } +} diff --git a/js/src/modals/DeployContract/deployContract.css b/js/src/modals/DeployContract/deployContract.css index 45fd7a852..90097df81 100644 --- a/js/src/modals/DeployContract/deployContract.css +++ b/js/src/modals/DeployContract/deployContract.css @@ -31,3 +31,7 @@ .funcparams { padding-left: 3em; } + +p { + color: rgba(255, 255, 255, 0.498039); +} diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 996948092..55e3166e8 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -22,13 +22,19 @@ import { BusyStep, CompletedStep, CopyToClipboard, Button, IdentityIcon, Modal, import { ERRORS, validateAbi, validateCode, validateName } from '../../util/validation'; import DetailsStep from './DetailsStep'; +import ParametersStep from './ParametersStep'; import ErrorStep from './ErrorStep'; import styles from './deployContract.css'; import { ERROR_CODES } from '../../api/transport/error'; -const steps = ['contract details', 'deployment', 'completed']; +const STEPS = { + CONTRACT_DETAILS: { title: 'contract details' }, + CONTRACT_PARAMETERS: { title: 'contract parameters' }, + DEPLOYMENT: { title: 'deployment', waiting: true }, + COMPLETED: { title: 'completed' } +}; export default class DeployContract extends Component { static contextTypes = { @@ -55,7 +61,6 @@ export default class DeployContract extends Component { abiError: ERRORS.invalidAbi, code: '', codeError: ERRORS.invalidCode, - deployState: '', description: '', descriptionError: null, fromAddress: Object.keys(this.props.accounts)[0], @@ -64,9 +69,12 @@ export default class DeployContract extends Component { nameError: ERRORS.invalidName, params: [], paramsError: [], - step: 0, + inputs: [], + + deployState: '', deployError: null, - rejected: false + rejected: false, + step: 'CONTRACT_DETAILS' } componentWillMount () { @@ -95,20 +103,30 @@ export default class DeployContract extends Component { } render () { - const { step, deployError, rejected } = this.state; + const { step, deployError, rejected, inputs } = this.state; + + const realStep = Object.keys(STEPS).findIndex((k) => k === step); + const realSteps = deployError || rejected + ? null + : Object.keys(STEPS) + .filter((k) => k !== 'CONTRACT_PARAMETERS' || inputs.length > 0) + .map((k) => STEPS[k]); - const realSteps = deployError || rejected ? null : steps; const title = realSteps ? null : (deployError ? 'deployment failed' : 'rejected'); + const waiting = realSteps + ? realSteps.map((s, i) => s.waiting ? i : false).filter((v) => v !== false) + : null; + return ( s.title) : null } title={ title } - waiting={ realSteps ? [1] : null } + waiting={ waiting } visible scroll> { this.renderStep() } @@ -146,20 +164,29 @@ export default class DeployContract extends Component { } switch (step) { - case 0: + case 'CONTRACT_DETAILS': return [ cancelBtn,