// 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 { MenuItem } from 'material-ui'; import { range } from 'lodash'; import { AddressSelect, Form, Input, InputAddressSelect, Select } from '../../../ui'; import { validateAbi } from '../../../util/validation'; import styles from '../deployContract.css'; class TypedInput extends Component { static propTypes = { onChange: PropTypes.func.isRequired, accounts: PropTypes.object.isRequired, param: PropTypes.object.isRequired, error: PropTypes.any, value: PropTypes.any, label: PropTypes.string }; render () { const { param } = this.props; const { type } = param; if (type === ARRAY_TYPE) { const { accounts, label, value = param.default } = this.props; const { subtype, length } = param; if (length) { const inputs = range(length).map((_, index) => { const onChange = (inputValue) => { const newValues = [].concat(this.props.value); newValues[index] = inputValue; this.props.onChange(newValues); }; return ( ); }); return (
{ inputs }
); } } return this.renderType(type); } renderType (type) { if (type === ADDRESS_TYPE) { return this.renderAddress(); } if (type === BOOL_TYPE) { return this.renderBoolean(); } if (type === STRING_TYPE) { return this.renderDefault(); } if (type === BYTES_TYPE) { return this.renderDefault(); } if (type === INT_TYPE) { return this.renderNumber(); } if (type === FIXED_TYPE) { return this.renderNumber(); } return this.renderDefault(); } renderNumber () { const { label, value, error, param } = this.props; return ( ); } renderDefault () { const { label, value, error } = this.props; return ( ); } renderAddress () { const { accounts, label, value, error } = this.props; return ( ); } renderBoolean () { const { label, value, error } = this.props; const boolitems = ['false', 'true'].map((bool) => { return ( { bool } ); }); return ( ); } onChangeBool = (event, _index, value) => { this.props.onChange(value === 'true'); } onChange = (event, value) => { this.props.onChange(value); } onSubmit = (value) => { this.props.onChange(value); } } export default class DetailsStep extends Component { static contextTypes = { api: PropTypes.object.isRequired } static propTypes = { accounts: PropTypes.object.isRequired, abi: PropTypes.string, abiError: PropTypes.string, code: PropTypes.string, codeError: PropTypes.string, description: PropTypes.string, descriptionError: PropTypes.string, 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 } state = { inputs: [] } render () { const { accounts } = this.props; const { abi, abiError, code, codeError, fromAddress, fromAddressError, name, nameError } = this.props; return (
{ this.renderConstructorInputs() } ); } renderConstructorInputs () { const { accounts, params, paramsError } = this.props; const { inputs } = this.state; if (!inputs || !inputs.length) { return null; } return 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 (
); }); } onFromAddressChange = (event, fromAddress) => { const { onFromAddressChange } = this.props; onFromAddressChange(fromAddress); } onNameChange = (name) => { const { onNameChange } = this.props; onNameChange(name); } onParamChange = (index, value) => { const { params, onParamsChange } = this.props; params[index] = value; onParamsChange(params); } onAbiChange = (abi) => { const { api } = this.context; const { onAbiChange, onParamsChange } = this.props; const { abiError, abiParsed } = validateAbi(abi, api); if (!abiError) { const { inputs } = abiParsed .find((method) => method.type === 'constructor') || { inputs: [] }; const params = []; inputs.forEach((input) => { const param = parseAbiType(input.type); params.push(param.default); }); onParamsChange(params); this.setState({ inputs }); } else { onParamsChange([]); this.setState({ inputs: [] }); } onAbiChange(abi); } onCodeChange = (code) => { const { onCodeChange } = this.props; onCodeChange(code); } } const ARRAY_TYPE = 'ARRAY_TYPE'; const ADDRESS_TYPE = 'ADDRESS_TYPE'; const STRING_TYPE = 'STRING_TYPE'; const BOOL_TYPE = 'BOOL_TYPE'; const BYTES_TYPE = 'BYTES_TYPE'; const INT_TYPE = 'INT_TYPE'; const FIXED_TYPE = 'FIXED_TYPE'; function parseAbiType (type) { const arrayRegex = /^(.+)\[(\d*)\]$/; if (arrayRegex.test(type)) { const matches = arrayRegex.exec(type); const subtype = parseAbiType(matches[1]); const M = parseInt(matches[2]) || null; const defaultValue = !M ? [] : range(M).map(() => subtype.default); return { type: ARRAY_TYPE, subtype: subtype, length: M, default: defaultValue }; } const lengthRegex = /^(u?int|bytes)(\d{1,3})$/; if (lengthRegex.test(type)) { const matches = lengthRegex.exec(type); const subtype = parseAbiType(matches[1]); const length = parseInt(matches[2]); return { ...subtype, length }; } const fixedLengthRegex = /^(u?fixed)(\d{1,3})x(\d{1,3})$/; if (fixedLengthRegex.test(type)) { const matches = fixedLengthRegex.exec(type); const subtype = parseAbiType(matches[1]); const M = parseInt(matches[2]); const N = parseInt(matches[3]); return { ...subtype, M, N }; } if (type === 'string') { return { type: STRING_TYPE, default: '' }; } if (type === 'bool') { return { type: BOOL_TYPE, default: false }; } if (type === 'address') { return { type: ADDRESS_TYPE, default: '0x' }; } if (type === 'bytes') { return { type: BYTES_TYPE, default: '0x' }; } if (type === 'uint') { return { type: INT_TYPE, default: 0, length: 256, signed: false }; } if (type === 'int') { return { type: INT_TYPE, default: 0, length: 256, signed: true }; } if (type === 'ufixed') { return { type: FIXED_TYPE, default: 0, length: 256, signed: false }; } if (type === 'fixed') { return { type: FIXED_TYPE, default: 0, length: 256, signed: true }; } }