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/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 b5337cbfc..28025424b 100644
--- a/src/components/Common/WhitelistInputBlock.js
+++ b/src/components/Common/WhitelistInputBlock.js
@@ -1,11 +1,13 @@
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, defaultState, VALIDATION_TYPES } from '../../utils/constants'
+import { TEXT_FIELDS, VALIDATION_TYPES } from '../../utils/constants'
+import { validateAddress } from '../../utils/utils'
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,22 +17,23 @@ 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 = {
+ addr: '',
+ min: '',
+ max: '',
validation: {
address: {
pristine: true,
valid: INVALID
}
}
- })
+ }
}
addWhitelistItem = () => {
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: {
@@ -44,51 +47,26 @@ 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 = () => {
- const whitelistInput = {
- addr: '',
- min: '',
- max: ''
- }
- this.props.tierStore.setTierProperty(whitelistInput, 'whitelistInput', this.props.num)
+ tierStore.addWhitelistItem({ addr, min, max }, crowdsaleNum)
}
- 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,20 +80,61 @@ 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((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)
+ })
+ }
+ })
+ })
+ }
+
+
render () {
const { num } = this.props
- const { whitelistInput, whitelistElements } = this.props.tierStore.tiers[num]
+ const { whitelistElements } = this.props.tierStore.tiers[num]
+
+ const dropzoneStyle = {
+ position: 'relative',
+ marginTop: '-15px',
+ marginBottom: '15px'
+ }
+ const uploadCSVStyle = {
+ textDecoration: 'underline',
+ cursor: 'pointer'
+ }
return (
+
+ Upload CSV
+
+
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}
@@ -125,16 +144,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/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/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 {
- 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 {
-
this.changeState(e, cntrct, 0, prop)} />
+
);
return (
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)
diff --git a/src/utils/constants.js b/src/utils/constants.js
index 67481ad74..348962ccf 100644
--- a/src/utils/constants.js
+++ b/src/utils/constants.js
@@ -1,52 +1,10 @@
-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: '',
walletAddress: '',
supply: '',
whitelist: [],
- whitelistElements: [],
- whitelistInput: {}
+ whitelistElements: []
}]
export const CONTRACT_TYPES = {
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) => {