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 ? ( + + ) : ( + + + + + + + + + + ) + } + +
) + + 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++) {