diff --git a/package.json b/package.json
index 4c8917ce3..d8d79c5b9 100755
--- a/package.json
+++ b/package.json
@@ -78,6 +78,7 @@
"postcss-flexbugs-fixes": "3.0.0",
"postcss-loader": "2.0.6",
"promise": "7.1.1",
+ "prop-types": "^15.6.1",
"qrcode.react": "^0.7.2",
"query-string": "^5.0.1",
"react": "^15.6.1",
diff --git a/src/App.js b/src/App.js
index 5b24cf63b..aa9268e5e 100644
--- a/src/App.js
+++ b/src/App.js
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { inject, observer } from 'mobx-react'
import './assets/stylesheets/application.css';
import { Header, Footer, Home, Manage, stepOne, stepTwo, stepThree, stepFour, Crowdsale, Invest } from './components/index'
+import NoWeb3 from './components/Common/NoWeb3'
import IncompleteDeploy from './components/IncompleteDeploy'
import { getQueryVariable } from './utils/utils'
import {
@@ -12,6 +13,7 @@ import {
import AlertContainer from 'react-alert'
import { TOAST } from './utils/constants'
import { toast } from './utils/utils'
+import { Web3Provider } from './react-web3'
@inject('deploymentStore')
@observer
@@ -25,26 +27,31 @@ class App extends Component {
-
- {/* The route to /4 must be first for the incomplete deploy redirect to work */}
-
+
+
+ {/* The route to /4 must be first for the incomplete deploy redirect to work */}
+
- {
- deploymentStore.deploymentStep !== null ? (
-
- ) : (
-
-
-
-
-
-
-
-
-
- )
- }
-
+ {
+ deploymentStore.deployInProgress ? (
+
+ ) : (
+
+
+
+
+
+
+
+
+
+ )
+ }
+
+
toast.msg = a} {...TOAST.DEFAULT_OPTIONS} />
diff --git a/src/components/Common/NoWeb3.js b/src/components/Common/NoWeb3.js
new file mode 100644
index 000000000..1cc4724f4
--- /dev/null
+++ b/src/components/Common/NoWeb3.js
@@ -0,0 +1,20 @@
+import React, { Component } from 'react';
+
+export default class NoWeb3 extends Component {
+ render () {
+ return (
+
+
+
+
+
MetaMask Not Found
+
+ You don't have MetaMask installed. Check Token Wizard GitHub for the instruction.
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/components/IncompleteDeploy.js b/src/components/IncompleteDeploy.js
index 90110b420..3c0c9cad6 100644
--- a/src/components/IncompleteDeploy.js
+++ b/src/components/IncompleteDeploy.js
@@ -1,16 +1,10 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom'
-import { cancellingIncompleteDeploy } from '../utils/alerts'
+import cancelDeploy from '../utils/cancelDeploy'
class CheckIncompleteDeploy extends Component {
cancel() {
- return cancellingIncompleteDeploy()
- .then(result => {
- if (result.value) {
- localStorage.clear()
- window.location.reload()
- }
- })
+ cancelDeploy()
}
render() {
diff --git a/src/components/stepFour/index.js b/src/components/stepFour/index.js
index 94d1739a9..a80243b8c 100644
--- a/src/components/stepFour/index.js
+++ b/src/components/stepFour/index.js
@@ -31,20 +31,30 @@ import { isObservableArray } from 'mobx'
import JSZip from 'jszip'
import executeSequentially from '../../utils/executeSequentially'
import { PreventRefresh } from '../Common/PreventRefresh'
+import cancelDeploy from '../../utils/cancelDeploy'
+import PropTypes from 'prop-types'
const { PUBLISH } = NAVIGATION_STEPS
@inject('contractStore', 'reservedTokenStore', 'tierStore', 'tokenStore', 'web3Store', 'deploymentStore')
@observer
export class stepFour extends React.Component {
- constructor (props) {
+ constructor (props, context) {
super(props)
this.state = {
contractDownloaded: false,
modal: false,
+ preventRefresh: true,
transactionFailed: false
}
+
this.props.deploymentStore.setDeploymentStep(0)
+ this.props.deploymentStore.setDeployerAccount(context.selectedAccount)
+ }
+
+ static contextTypes = {
+ web3: PropTypes.object,
+ selectedAccount: PropTypes.string
}
contractDownloadSuccess = options => {
@@ -64,12 +74,13 @@ export class stepFour extends React.Component {
deployCrowdsale = () => {
const { contractStore, deploymentStore } = this.props
+ const { web3 } = this.context
const isWhitelistWithCap = contractStore.contractType === CONTRACT_TYPES.whitelistwithcap
const firstRun = deploymentStore.deploymentStep === null
if (isWhitelistWithCap) {
if (firstRun) {
- setupContractDeployment()
+ setupContractDeployment(web3)
.then(this.resumeContractDeployment)
} else {
this.resumeContractDeployment()
@@ -79,8 +90,9 @@ export class stepFour extends React.Component {
resumeContractDeployment = () => {
const { deploymentStore } = this.props
+ const { web3 } = this.context
const startAt = deploymentStore.deploymentStep ? deploymentStore.deploymentStep : 0
- const deploymentSteps = buildDeploymentSteps()
+ const deploymentSteps = buildDeploymentSteps(web3)
executeSequentially(deploymentSteps, startAt, (index) => {
deploymentStore.setDeploymentStep(index)
@@ -239,6 +251,30 @@ export class stepFour extends React.Component {
this.props.history.push(newHistory)
}
+ cancelDeploy = (e) => {
+ e.preventDefault();
+
+ this.hideModal(); // hide modal, otherwise the warning doesn't show up
+
+ // avoid the beforeunload alert when user cancels the deploy
+ this.setState({
+ preventRefresh: false
+ })
+
+ cancelDeploy()
+ .then(
+ (cancelled) => {
+ if (!cancelled) {
+ this.setState({
+ preventRefresh: true
+ })
+ this.showModal()
+ }
+ },
+ () => this.showModal()
+ )
+ }
+
render() {
const { tierStore, contractStore, tokenStore, deploymentStore } = this.props
const crowdsaleSetups = tierStore.tiers.map((tier, index) => {
@@ -391,6 +427,21 @@ export class stepFour extends React.Component {
/>
)
+
+ const modalContent = deploymentStore.invalidAccount ? (
+
+ This deploy was started with account
{deploymentStore.deployerAccount} but the current account is
{this.context.selectedAccount}.
+ Please select the original account to continue with the deploy.
+ If you don't want to continue with that deploy,
click here.
+
+ ) : (
+
+ )
+
return (
@@ -491,13 +542,9 @@ export class stepFour extends React.Component {
title={'Tx Status'}
showModal={this.state.modal}
>
-
+ { modalContent }
-
+ { this.state.preventRefresh ? : null }
)}
}
diff --git a/src/components/stepFour/utils.js b/src/components/stepFour/utils.js
index d7fc263a0..699880951 100644
--- a/src/components/stepFour/utils.js
+++ b/src/components/stepFour/utils.js
@@ -23,7 +23,7 @@ import {
import { getEncodedABIClientSide } from '../../utils/microservices'
import { BigNumber } from 'bignumber.js'
-export const setupContractDeployment = () => {
+export const setupContractDeployment = (web3) => {
if (!contractStore.safeMathLib) {
noContractDataAlert()
return Promise.reject('no contract data')
@@ -36,7 +36,7 @@ export const setupContractDeployment = () => {
const whenTokenABIConstructor = Promise.resolve(tokenAddr)
.then(tokenAddr => {
if (!tokenAddr) {
- return getEncodedABIClientSide(tokenABI, [], 0)
+ return getEncodedABIClientSide(web3, tokenABI, [], 0)
.then(ABIEncoded => {
console.log('token ABI Encoded params constructor:', ABIEncoded)
contractStore.setContractProperty('token', 'abiConstructor', ABIEncoded)
@@ -45,7 +45,7 @@ export const setupContractDeployment = () => {
})
const whenPricingStrategyContract = tierStore.tiers.map((value, index) => {
- return getEncodedABIClientSide(pricingStrategyABI, [], index)
+ return getEncodedABIClientSide(web3, pricingStrategyABI, [], index)
.then(ABIEncoded => {
console.log('pricingStrategy ABI Encoded params constructor:', ABIEncoded)
const newContract = contractStore.pricingStrategy.abiConstructor.concat(ABIEncoded)
@@ -56,14 +56,14 @@ export const setupContractDeployment = () => {
return Promise.all([whenTokenABIConstructor, ...whenPricingStrategyContract])
}
-export const buildDeploymentSteps = () => {
+export const buildDeploymentSteps = (web3) => {
const stepFnCorrelation = {
safeMathLibrary: deploySafeMathLibrary,
token: deployToken,
- pricingStrategy: deployPricingStrategy,
+ pricingStrategy: deployPricingStrategy(web3),
crowdsale: deployCrowdsale,
registerCrowdsaleAddress: registerCrowdsaleAddress,
- finalizeAgent: deployFinalizeAgent,
+ finalizeAgent: deployFinalizeAgent(web3),
tier: setTier,
setReservedTokens: setReservedTokensListMultiple,
updateJoinedCrowdsales: updateJoinedCrowdsales,
@@ -157,7 +157,7 @@ const getPricingStrategyParams = tier => {
]
}
-export const deployPricingStrategy = () => {
+export const deployPricingStrategy = (web3) => () => {
return tierStore.tiers.map((tier, index) => {
return () => {
const abiPricingStrategy = contractStore.pricingStrategy.abi || []
@@ -169,7 +169,7 @@ export const deployPricingStrategy = () => {
return deployContract(abiPricingStrategy, binPricingStrategy, paramsPricingStrategy)
.then(pricingStrategyAddr => contractStore.pricingStrategy.addr.concat(pricingStrategyAddr))
.then(newPricingStrategy => contractStore.setContractProperty('pricingStrategy', 'addr', newPricingStrategy))
- .then(() => getEncodedABIClientSide(abiCrowdsale, [], index, true))
+ .then(() => getEncodedABIClientSide(web3, abiCrowdsale, [], index, true))
.then(ABIEncoded => contractStore.crowdsale.abiConstructor.concat(ABIEncoded))
.then(newContract => contractStore.setContractProperty('crowdsale', 'abiConstructor', newContract))
.then(() => deploymentStore.setAsSuccessful('pricingStrategy'))
@@ -267,7 +267,7 @@ const getFinalizeAgentParams = index => {
]
}
-export const deployFinalizeAgent = () => {
+export const deployFinalizeAgent = (web3) => () => {
return tierStore.tiers.map((tier, index, tiers) => {
return () => {
let abi, bin, paramsFinalizeAgent
@@ -282,7 +282,7 @@ export const deployFinalizeAgent = () => {
bin = contractStore.nullFinalizeAgent.bin || ''
}
- return getEncodedABIClientSide(abi, [], index)
+ return getEncodedABIClientSide(web3, abi, [], index)
.then(ABIEncoded => {
console.log('finalizeAgent ABI encoded params constructor:', ABIEncoded)
diff --git a/src/index.js b/src/index.js
index c3196ee30..d3de6c2a4 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
+import Web3 from 'web3'
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { useStrict } from 'mobx';
@@ -13,6 +14,11 @@ if (!process.env['REACT_APP_REGISTRY_ADDRESS']) {
throw new Error('REACT_APP_REGISTRY_ADDRESS env variable is not present')
}
+const devEnvironment = process.env.NODE_ENV === 'development';
+if (devEnvironment && !window.web3) {
+ window.web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
+}
+
ReactDOM.render(
diff --git a/src/react-web3/Web3Provider.js b/src/react-web3/Web3Provider.js
new file mode 100644
index 000000000..496bb1499
--- /dev/null
+++ b/src/react-web3/Web3Provider.js
@@ -0,0 +1,88 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import Web3 from 'web3'
+
+const ONE_SECOND = 1000;
+
+class Web3Provider extends Component {
+ constructor(props, context) {
+ super(props, context)
+
+ this.state = {
+ selectedAccount: null
+ }
+
+ this.web3 = null
+ this.interval = null
+
+ }
+ render() {
+ const { web3UnavailableScreen: Web3UnavailableScreen } = this.props
+
+ if (window.web3) {
+ if (!this.web3) {
+ this.web3 = new Web3(window.web3.currentProvider);
+ this.fetchAccounts()
+ }
+
+ return this.props.children
+ }
+
+ return
+ }
+
+ componentDidMount() {
+ this.initPoll();
+ }
+
+ initPoll = () => {
+ if (!this.interval) {
+ this.interval = setInterval(this.fetchAccounts, ONE_SECOND)
+ }
+ }
+
+ fetchAccounts = () => {
+ const { web3 } = this
+ const { onChangeAccount } = this.props
+
+ if (!web3 || !web3.eth) {
+ return
+ }
+
+ return web3.eth.getAccounts()
+ .then(accounts => {
+ if (!accounts || !accounts.length) {
+ return
+ }
+
+ let curr = this.state.selectedAccount
+ let next = accounts[0]
+ curr = curr && curr.toLowerCase()
+ next = next && next.toLowerCase()
+
+ const didChange = curr && next && (curr !== next)
+
+ if (didChange && typeof onChangeAccount === 'function') {
+ onChangeAccount(next)
+ }
+
+ this.setState({
+ selectedAccount: next || null
+ })
+ })
+ }
+
+ getChildContext() {
+ return {
+ web3: this.web3,
+ selectedAccount: this.state.selectedAccount
+ }
+ }
+}
+
+Web3Provider.childContextTypes = {
+ web3: PropTypes.object,
+ selectedAccount: PropTypes.string
+}
+
+export default Web3Provider;
diff --git a/src/react-web3/index.js b/src/react-web3/index.js
new file mode 100644
index 000000000..a69bdb015
--- /dev/null
+++ b/src/react-web3/index.js
@@ -0,0 +1,3 @@
+import Web3Provider from './Web3Provider'
+
+export { Web3Provider }
diff --git a/src/stores/DeploymentStore.js b/src/stores/DeploymentStore.js
index be0ce377d..6a55794c5 100644
--- a/src/stores/DeploymentStore.js
+++ b/src/stores/DeploymentStore.js
@@ -5,6 +5,8 @@ class DeploymentStore {
@observable txMap = new Map()
@observable deploymentStep = null
@observable hasEnded = false
+ @observable deployerAccount = null
+ @observable invalidAccount = false
constructor() {
autosave(this, 'DeploymentStore', (store) => {
@@ -72,6 +74,21 @@ class DeploymentStore {
this.deploymentStep = index
}
+ @action setDeployerAccount = (account) => {
+ if (!this.deployInProgress) {
+ this.deployerAccount = account
+ }
+ }
+
+ @action handleAccountChange = (account) => {
+ if (!this.deployerAccount) {
+ // If there is no deployment in progress, do nothing
+ return
+ }
+
+ this.invalidAccount = account !== this.deployerAccount
+ }
+
@action resetDeploymentStep = () => {
this.deploymentStep = null
}
@@ -105,6 +122,11 @@ class DeploymentStore {
if (txStatuses.some(status => !status)) return tx
}
}
+
+ @computed
+ get deployInProgress () {
+ return this.deploymentStep !== null
+ }
}
export default DeploymentStore
diff --git a/src/utils/cancelDeploy.js b/src/utils/cancelDeploy.js
new file mode 100644
index 000000000..917e3dae1
--- /dev/null
+++ b/src/utils/cancelDeploy.js
@@ -0,0 +1,15 @@
+import { cancellingIncompleteDeploy } from './alerts'
+
+const cancelDeploy = () => {
+ return cancellingIncompleteDeploy()
+ .then(result => {
+ if (result.value) {
+ localStorage.clear()
+ window.location = '/' // go to home
+ }
+
+ return result.value
+ })
+}
+
+export default cancelDeploy
diff --git a/src/utils/microservices.js b/src/utils/microservices.js
index 2178366ce..a404c5baa 100644
--- a/src/utils/microservices.js
+++ b/src/utils/microservices.js
@@ -1,17 +1,14 @@
import { findConstructor, toFixed } from '../utils/utils'
import { getconstructorParams } from '../stores/utils'
-import { web3Store } from '../stores'
-export const getEncodedABIClientSide = (abi, vals, crowdsaleNum, isCrowdsale) => {
+export const getEncodedABIClientSide = (web3, abi, vals, crowdsaleNum, isCrowdsale) => {
const abiConstructor = findConstructor(abi)
let params = getconstructorParams(abiConstructor, vals, crowdsaleNum, isCrowdsale)
- return getABIEncoded(params.types, params.vals)
+ return getABIEncoded(web3, params.types, params.vals)
}
-const getABIEncoded = (types, vals) => {
- const { web3 } = web3Store
-
+const getABIEncoded = (web3, types, vals) => {
return new Promise((resolve, reject) => {
if (vals) {
for (let i = 0; i < vals.length; i++) {