From c711abae5a6d9baf70287adbfc615026f57637a8 Mon Sep 17 00:00:00 2001 From: Franco Victorio Date: Mon, 26 Feb 2018 15:30:19 -0300 Subject: [PATCH 1/5] Remove getOldState and defaultState --- src/components/Common/WhitelistInputBlock.js | 8 ++-- src/utils/constants.js | 41 -------------------- src/utils/utils.js | 2 - 3 files changed, 3 insertions(+), 48 deletions(-) diff --git a/src/components/Common/WhitelistInputBlock.js b/src/components/Common/WhitelistInputBlock.js index b5337cbfc..0194d3757 100644 --- a/src/components/Common/WhitelistInputBlock.js +++ b/src/components/Common/WhitelistInputBlock.js @@ -3,9 +3,8 @@ import Web3 from 'web3'; import update from 'immutability-helper'; import '../../assets/stylesheets/application.css'; import { InputField } from './InputField' -import { TEXT_FIELDS, defaultState, VALIDATION_TYPES } from '../../utils/constants' +import { TEXT_FIELDS, VALIDATION_TYPES } from '../../utils/constants' import { WhitelistItem } from './WhitelistItem' -import { getOldState } from '../../utils/utils' import { inject, observer } from 'mobx-react' const { ADDRESS, MIN, MAX } = TEXT_FIELDS const {VALID, INVALID} = VALIDATION_TYPES; @@ -15,15 +14,14 @@ const {VALID, INVALID} = VALIDATION_TYPES; export class WhitelistInputBlock extends React.Component { constructor (props) { super(props) - let oldState = getOldState(props, defaultState) - this.state = Object.assign({}, oldState, { + this.state = { validation: { address: { pristine: true, valid: INVALID } } - }) + } } addWhitelistItem = () => { diff --git a/src/utils/constants.js b/src/utils/constants.js index 67481ad74..1ffa34086 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -1,44 +1,3 @@ -export const defaultState = { - contracts: { - token: {}, - crowdsale: {addr:[], abiConstructor:[]}, - pricingStrategy: {addr:[], abiConstructor:[]}, - multisig: {}, - nullFinalizeAgent: {addr:[], abiConstructor:[]}, - finalizeAgent: {addr:[], abiConstructor:[]}, - tokenTransferProxy: {} - }, - token: { - name: '', - ticker: '', - supply: 0, - decimals: '', - reservedTokens: [], - reservedTokensElements: [], - reservedTokensInput: {dim: "tokens"} - }, - crowdsale: [{ - startTime: '', - endTime: '', - walletAddress: '', - supply: '', - whitelist: [], - whitelistElements: [], - whitelistInput: {} - }], - pricingStrategy: [{rate: ''}], - blockTimeGeneration: 17, - compilerVersion: "0.4.11", - optimized: true, - contractName: "MintedTokenCappedCrowdsaleExt", - contractType: "white-list-with-cap", - contractTypes: { - standard: "standard", - capped: "capped", - whitelistwithcap: "white-list-with-cap" - } -} - export const defaultTiers = [{ startTime: '', endTime: '', diff --git a/src/utils/utils.js b/src/utils/utils.js index 927f794a8..85b240668 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -68,8 +68,6 @@ if (!Math.floor10) { const getTimeAsNumber = (time) => new Date(time).getTime() -export const getOldState = (props, defaultState) => (props && props.location && props.location.query && props.location.query.state) || defaultState - export const getStepClass = (step, activeStep) => step === activeStep ? "step-navigation step-navigation_active" : "step-navigation" export const stepsAreValid = (steps) => { From d770a9e52b71cbdc599c7691247303d3eaec97b0 Mon Sep 17 00:00:00 2001 From: Franco Victorio Date: Mon, 26 Feb 2018 16:28:28 -0300 Subject: [PATCH 2/5] Remove whitelistInput from tierStore --- ICO_wizard_state_example.json | 8 +----- src/components/Common/WhitelistInputBlock.js | 30 ++++++++++---------- src/components/manage/index.js | 9 ------ src/components/manage/utils.js | 7 +---- src/components/stepFour/utils.js | 20 ------------- src/components/stepThree/CrowdsaleBlock.js | 12 +------- src/components/stepThree/index.js | 16 ++--------- src/utils/constants.js | 3 +- 8 files changed, 21 insertions(+), 84 deletions(-) diff --git a/ICO_wizard_state_example.json b/ICO_wizard_state_example.json index a613d825b..6ce896dd3 100644 --- a/ICO_wizard_state_example.json +++ b/ICO_wizard_state_example.json @@ -2520,11 +2520,6 @@ "_store": {} } ], - "whiteListInput": { - "addr": "", - "min": "", - "max": "" - }, "tier": "Tier 1", "updatable": "on", "whitelistdisabled": "no" @@ -2535,10 +2530,9 @@ "updatable": "on", "whitelist": [], "whiteListElements": [], - "whiteListInput": {}, "startTime": "2017-10-03T00:05:00", "endTime": "2017-10-07T00:05:00" } ], "children": [] -} \ No newline at end of file +} diff --git a/src/components/Common/WhitelistInputBlock.js b/src/components/Common/WhitelistInputBlock.js index 0194d3757..0043fa8ca 100644 --- a/src/components/Common/WhitelistInputBlock.js +++ b/src/components/Common/WhitelistInputBlock.js @@ -15,6 +15,9 @@ export class WhitelistInputBlock extends React.Component { constructor (props) { super(props) this.state = { + addr: '', + min: '', + max: '', validation: { address: { pristine: true, @@ -28,7 +31,7 @@ export class WhitelistInputBlock extends React.Component { const { tierStore } = this.props const crowdsaleNum = this.props.num const tier = tierStore.tiers[crowdsaleNum] - const { addr, min, max } = tier.whitelistInput + const { addr, min, max } = this.state this.setState(update(this.state, { validation: { @@ -72,21 +75,18 @@ export class WhitelistInputBlock extends React.Component { } clearWhiteListInputs = () => { - const whitelistInput = { + this.setState({ addr: '', min: '', max: '' - } - this.props.tierStore.setTierProperty(whitelistInput, 'whitelistInput', this.props.num) + }) } - handleAddressChange = e => { - this.props.onChange(e, 'crowdsale', this.props.num, 'whitelist_addr') - - const address = e.target.value + handleAddressChange = address => { const isAddressValid = Web3.utils.isAddress(address) ? VALID : INVALID; const newState = update(this.state, { + addr: { $set: address }, validation: { address: { $set: { @@ -102,7 +102,7 @@ export class WhitelistInputBlock extends React.Component { render () { const { num } = this.props - const { whitelistInput, whitelistElements } = this.props.tierStore.tiers[num] + const { whitelistElements } = this.props.tierStore.tiers[num] return (
@@ -112,8 +112,8 @@ export class WhitelistInputBlock extends React.Component { side='white-list-input-property white-list-input-property-left' type='text' title={ADDRESS} - value={whitelistInput && whitelistInput.addr} - onChange={e => this.handleAddressChange(e)} + value={this.state.addr} + onChange={e => this.handleAddressChange(e.target.value)} description={`Address of a whitelisted account. Whitelists are inherited. E.g., if an account whitelisted on Tier 1 and didn't buy max cap on Tier 1, he can buy on Tier 2, and following tiers.`} pristine={this.state.validation.address.pristine} valid={this.state.validation.address.valid} @@ -123,16 +123,16 @@ export class WhitelistInputBlock extends React.Component { side='white-list-input-property white-list-input-property-middle' type='number' title={MIN} - value={whitelistInput && whitelistInput.min} - onChange={e => this.props.onChange(e, 'crowdsale', num, 'whitelist_min')} + value={this.state.min} + onChange={e => this.setState({ min: e.target.value })} description={`Minimum amount tokens to buy. Not a minimal size of a transaction. If minCap is 1 and user bought 1 token in a previous transaction and buying 0.1 token it will allow him to buy.`} /> this.props.onChange(e, 'crowdsale', num, 'whitelist_max')} + value={this.state.max} + onChange={e => this.setState({ max: e.target.value })} description={`Maximum is the hard limit.`} />
diff --git a/src/components/manage/index.js b/src/components/manage/index.js index d5ce0cb7a..e1e6e2a06 100644 --- a/src/components/manage/index.js +++ b/src/components/manage/index.js @@ -370,14 +370,6 @@ export class Manage extends Component { }) } - changeState = (event, parent, key, property) => { - const { tierStore } = this.props - const whitelistInputProps = { ...tierStore.tiers[key].whitelistInput } - const prop = property.split('_')[1] - whitelistInputProps[prop] = event.target.value - tierStore.setTierProperty(whitelistInputProps, 'whitelistInput', key) - } - clickedWhiteListInputBlock = e => { if (e.target.classList.contains('button_fill_plus')) { this.setState({ formPristine: false }) @@ -389,7 +381,6 @@ export class Manage extends Component { this.changeState(e, contract, num, prop)} /> ) } diff --git a/src/components/manage/utils.js b/src/components/manage/utils.js index 739ce6b76..5fff889dd 100644 --- a/src/components/manage/utils.js +++ b/src/components/manage/utils.js @@ -202,12 +202,7 @@ export const processTier = (crowdsaleAddress, crowdsaleNum) => { const newTier = { whitelist: [], - whitelistElements: [], - whitelistInput: { - addr: '', - min: '', - max: '' - } + whitelistElements: [] } const initialValues = {} diff --git a/src/components/stepFour/utils.js b/src/components/stepFour/utils.js index 6ffaf6076..85743f3e2 100644 --- a/src/components/stepFour/utils.js +++ b/src/components/stepFour/utils.js @@ -402,7 +402,6 @@ export const addWhitelist = () => { for (let i = 0; i <= round; i++) { const tier = tierStore.tiers[i] - const whitelistInput = tier.whitelistInput for (let j = 0; j < tier.whitelist.length; j++) { let itemIsAdded = false @@ -418,25 +417,6 @@ export const addWhitelist = () => { whitelist.push.apply(whitelist, tier.whitelist) } } - - if (whitelistInput.addr && whitelistInput.min && whitelistInput.max) { - let itemIsAdded = false - - for (let k = 0; k < whitelist.length; k++) { - if (whitelist[k].addr === whitelistInput.addr) { - itemIsAdded = true - break - } - } - - if (!itemIsAdded) { - whitelist.push({ - 'addr': whitelistInput.addr, - 'min': whitelistInput.min, - 'max': whitelistInput.max - }) - } - } } console.log('whitelist:', whitelist) diff --git a/src/components/stepThree/CrowdsaleBlock.js b/src/components/stepThree/CrowdsaleBlock.js index d21b715ef..86002e726 100644 --- a/src/components/stepThree/CrowdsaleBlock.js +++ b/src/components/stepThree/CrowdsaleBlock.js @@ -18,16 +18,6 @@ export class CrowdsaleBlock extends React.Component { tierStore.setTierProperty(startTime, "startTime", num); tierStore.setTierProperty(endTime, "endTime", num); } - changeState = (event, parent, key, property) => { - if (property.indexOf("whitelist_") === 0) { - const { tierStore } = this.props; - const whitelistInputProps = { ...tierStore.tiers[key].whitelistInput }; - const prop = property.split("_")[1]; - - whitelistInputProps[prop] = event.target.value; - tierStore.setTierProperty(whitelistInputProps, "whitelistInput", key); - } - }; updateTierStore = (event, property) => { const { tierStore, num } = this.props; @@ -43,7 +33,7 @@ export class CrowdsaleBlock extends React.Component {

Whitelist

- this.changeState(e, cntrct, num, prop)} /> + ); return ( diff --git a/src/components/stepThree/index.js b/src/components/stepThree/index.js index c59647082..0ef2f926b 100644 --- a/src/components/stepThree/index.js +++ b/src/components/stepThree/index.js @@ -63,17 +63,6 @@ export class stepThree extends React.Component { this.props.tierStore.invalidateToken(); }; - changeState = (event, parent, key, property) => { - if (property.indexOf("whitelist_") === 0) { - const { tierStore } = this.props; - const whitelistInputProps = { ...tierStore.tiers[key].whitelistInput }; - const prop = property.split("_")[1]; - - whitelistInputProps[prop] = event.target.value; - tierStore.setTierProperty(whitelistInputProps, "whitelistInput", key); - } - }; - addCrowdsale() { const { crowdsaleBlockListStore, tierStore } = this.props; let num = crowdsaleBlockListStore.blockList.length + 1; @@ -83,8 +72,7 @@ export class stepThree extends React.Component { rate: 0, updatable: "off", whitelist: [], - whitelistElements: [], - whitelistInput: {} + whitelistElements: [] }; const newTierValidations = { @@ -347,7 +335,7 @@ export class stepThree extends React.Component {

Whitelist

- this.changeState(e, cntrct, 0, prop)} /> + ); return ( diff --git a/src/utils/constants.js b/src/utils/constants.js index 1ffa34086..348962ccf 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -4,8 +4,7 @@ export const defaultTiers = [{ walletAddress: '', supply: '', whitelist: [], - whitelistElements: [], - whitelistInput: {} + whitelistElements: [] }] export const CONTRACT_TYPES = { From c052843f87214ee072917cf8cdf7157d9197038c Mon Sep 17 00:00:00 2001 From: Franco Victorio Date: Mon, 26 Feb 2018 17:02:44 -0300 Subject: [PATCH 3/5] Move add whitelist item logic to store --- src/components/Common/WhitelistInputBlock.js | 39 ++++---------------- src/components/Common/WhitelistItem.js | 2 +- src/stores/TierStore.js | 21 ++++++++++- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/components/Common/WhitelistInputBlock.js b/src/components/Common/WhitelistInputBlock.js index 0043fa8ca..b2b1b05b4 100644 --- a/src/components/Common/WhitelistInputBlock.js +++ b/src/components/Common/WhitelistInputBlock.js @@ -30,7 +30,6 @@ export class WhitelistInputBlock extends React.Component { addWhitelistItem = () => { const { tierStore } = this.props const crowdsaleNum = this.props.num - const tier = tierStore.tiers[crowdsaleNum] const { addr, min, max } = this.state this.setState(update(this.state, { @@ -45,41 +44,19 @@ export class WhitelistInputBlock extends React.Component { return } - this.setState(update(this.state, { + this.setState({ + addr: '', + min: '', + max: '', validation: { address: { - $set: { - pristine: true, - valid: INVALID - } + pristine: true, + valid: INVALID } } - })) - - this.clearWhiteListInputs() - - const whitelist = tier.whitelist.slice() - - const isAdded = whitelist.find(item => item.addr === addr && !item.deleted) - - if (isAdded) return - - const whitelistElements = tier.whitelistElements.slice() - const whitelistNum = whitelistElements.length - - whitelistElements.push({ addr, min, max, whitelistNum, crowdsaleNum }) - whitelist.push({ addr, min, max }) - - tierStore.setTierProperty(whitelistElements, 'whitelistElements', crowdsaleNum) - tierStore.setTierProperty(whitelist, 'whitelist', crowdsaleNum) - } - - clearWhiteListInputs = () => { - this.setState({ - addr: '', - min: '', - max: '' }) + + tierStore.addWhitelistItem({ addr, min, max }, crowdsaleNum) } handleAddressChange = address => { diff --git a/src/components/Common/WhitelistItem.js b/src/components/Common/WhitelistItem.js index 6a96292c5..0b357055a 100644 --- a/src/components/Common/WhitelistItem.js +++ b/src/components/Common/WhitelistItem.js @@ -7,7 +7,7 @@ import { inject, observer } from 'mobx-react' export class WhitelistItem extends React.Component { removeItem () { const { tierStore, crowdsaleNum, whitelistNum } = this.props - tierStore.removeWhiteListItem(whitelistNum, crowdsaleNum) + tierStore.removeWhitelistItem(whitelistNum, crowdsaleNum) } render () { diff --git a/src/stores/TierStore.js b/src/stores/TierStore.js index 39b3540a2..b172e6fd4 100644 --- a/src/stores/TierStore.js +++ b/src/stores/TierStore.js @@ -168,7 +168,26 @@ class TierStore { }); } - @action removeWhiteListItem = (whitelistNum, crowdsaleNum) => { + @action addWhitelistItem = ({ addr, min, max }, crowdsaleNum) => { + const tier = this.tiers[crowdsaleNum] + + const whitelist = tier.whitelist.slice() + + const isAdded = whitelist.find(item => item.addr === addr && !item.deleted) + + if (isAdded) return + + const whitelistElements = tier.whitelistElements.slice() + const whitelistNum = whitelistElements.length + + whitelistElements.push({ addr, min, max, whitelistNum, crowdsaleNum }) + whitelist.push({ addr, min, max }) + + this.setTierProperty(whitelistElements, 'whitelistElements', crowdsaleNum) + this.setTierProperty(whitelist, 'whitelist', crowdsaleNum) + } + + @action removeWhitelistItem = (whitelistNum, crowdsaleNum) => { let whitelist = this.tiers[crowdsaleNum].whitelist.slice() whitelist[whitelistNum].deleted = true this.setTierProperty(whitelist, 'whitelist', crowdsaleNum) From 319e05d9b7e666973ea2cb203a5c2c0e46f6b9e3 Mon Sep 17 00:00:00 2001 From: Franco Victorio Date: Wed, 28 Feb 2018 15:46:51 -0300 Subject: [PATCH 4/5] Add button to upload a CSV with whitelist --- package-lock.json | 22 +++++++++++++ package.json | 2 ++ src/components/Common/WhitelistInputBlock.js | 34 ++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/package-lock.json b/package-lock.json index 2d0ab18ab..f3e205ad5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -445,6 +445,14 @@ "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", "dev": true }, + "attr-accept": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.2.tgz", + "integrity": "sha512-NUj0itVSnpFkUYCj3XKSRCZ7N9gPwWcyX/tF7HosqyDBPMSygALivvJIGI8VvlPcunns5khMkpxoNshvmhy/ZQ==", + "requires": { + "core-js": "2.5.3" + } + }, "autolinker": { "version": "0.15.3", "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.15.3.tgz", @@ -10668,6 +10676,11 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" }, + "papaparse": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-4.3.7.tgz", + "integrity": "sha1-7R5xgzINHgg53+n3GGGFz8UJe40=" + }, "param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -12643,6 +12656,15 @@ "prop-types": "15.6.0" } }, + "react-dropzone": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-4.2.8.tgz", + "integrity": "sha512-L/q6ySfhdG9Md3P21jFumzlm92TxRT0FtYX6G793Nf8bt7Fzpwx6gJsPk0idV094koj/Y5vRpp0q9+e0bdsjxw==", + "requires": { + "attr-accept": "1.1.2", + "prop-types": "15.6.0" + } + }, "react-error-overlay": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-1.0.10.tgz", diff --git a/package.json b/package.json index 0c1f0adc8..b8f39b6e6 100755 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "mobx-react": "^4.3.3", "moment": "^2.20.1", "object-assign": "4.1.1", + "papaparse": "^4.3.7", "path": "^0.12.7", "postcss-flexbugs-fixes": "3.0.0", "postcss-loader": "2.0.6", @@ -81,6 +82,7 @@ "react-countdown-clock": "^2.0.0", "react-dev-utils": "^3.0.2", "react-dom": "^15.6.1", + "react-dropzone": "^4.2.8", "react-error-overlay": "^1.0.9", "react-router-dom": "^4.1.2", "solc": "^0.4.14", diff --git a/src/components/Common/WhitelistInputBlock.js b/src/components/Common/WhitelistInputBlock.js index b2b1b05b4..a19f8d0e7 100644 --- a/src/components/Common/WhitelistInputBlock.js +++ b/src/components/Common/WhitelistInputBlock.js @@ -1,6 +1,8 @@ import React from 'react' import Web3 from 'web3'; import update from 'immutability-helper'; +import Dropzone from 'react-dropzone'; +import Papa from 'papaparse' import '../../assets/stylesheets/application.css'; import { InputField } from './InputField' import { TEXT_FIELDS, VALIDATION_TYPES } from '../../utils/constants' @@ -77,12 +79,44 @@ export class WhitelistInputBlock extends React.Component { this.setState(newState) } + onDrop = (acceptedFiles, rejectedFiles) => { + acceptedFiles.forEach(file => { + Papa.parse(file, { + skipEmptyLines: true, + complete: results => { + results.data.forEach(([addr, min, max]) => { + this.props.tierStore.addWhitelistItem({ addr, min, max }, this.props.num) + }) + } + }) + }) + } + + render () { const { num } = this.props const { whitelistElements } = this.props.tierStore.tiers[num] + const dropzoneStyle = { + position: 'relative', + marginTop: '-15px', + marginBottom: '15px' + } + const uploadCSVStyle = { + textDecoration: 'underline', + cursor: 'pointer' + } + return (
+ + Upload CSV + +
Date: Wed, 28 Feb 2018 16:01:21 -0300 Subject: [PATCH 5/5] Validate uploaded CSV --- src/components/Common/WhitelistInputBlock.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/Common/WhitelistInputBlock.js b/src/components/Common/WhitelistInputBlock.js index a19f8d0e7..28025424b 100644 --- a/src/components/Common/WhitelistInputBlock.js +++ b/src/components/Common/WhitelistInputBlock.js @@ -6,6 +6,7 @@ import Papa from 'papaparse' import '../../assets/stylesheets/application.css'; import { InputField } from './InputField' import { TEXT_FIELDS, VALIDATION_TYPES } from '../../utils/constants' +import { validateAddress } from '../../utils/utils' import { WhitelistItem } from './WhitelistItem' import { inject, observer } from 'mobx-react' const { ADDRESS, MIN, MAX } = TEXT_FIELDS @@ -79,12 +80,21 @@ export class WhitelistInputBlock extends React.Component { this.setState(newState) } + isAddress = (address) => validateAddress(address) + isNumber = (number) => !isNaN(parseFloat(number)) + onDrop = (acceptedFiles, rejectedFiles) => { acceptedFiles.forEach(file => { Papa.parse(file, { skipEmptyLines: true, complete: results => { - results.data.forEach(([addr, min, max]) => { + results.data.forEach((row) => { + if (row.length !== 3) return + + const [addr, min, max] = row + + if (!this.isAddress(addr) || !this.isNumber(min) || !this.isNumber(max)) return + this.props.tierStore.addWhitelistItem({ addr, min, max }, this.props.num) }) }