mirror of https://github.com/embarklabs/embark.git
fix(@cockpit/deployment): Check if contracts deployed when connected to metamask
When connected to metamask, and “Injected Web3” is selected on the Deployment page of Cockpit, check to see if contracts have already been deployed or not. For contracts that have not been deployed, or set to an address in the config, these should now all be re-deployable. Supports libraries (that have bytecode with `0x73<address><bytecode>`).
This commit is contained in:
parent
4c3ec26fff
commit
c23316351e
|
@ -360,6 +360,13 @@ export const web3EstimateGas = {
|
|||
failure: (error, payload) => action(WEB3_ESTIMAGE_GAS[FAILURE], {web3Error: error, contract: payload.contract})
|
||||
};
|
||||
|
||||
export const WEB3_IS_DEPLOYED = createRequestTypes('WEB3_IS_DEPLOYED');
|
||||
export const web3IsDeployed = {
|
||||
request: (contract, args) => action(WEB3_IS_DEPLOYED[REQUEST], {contract, args}),
|
||||
success: (isDeployed, payload) => action(WEB3_IS_DEPLOYED[SUCCESS], {contract: payload.contract, isDeployed}),
|
||||
failure: (error, payload) => action(WEB3_IS_DEPLOYED[FAILURE], {web3Error: error, contract: payload.contract})
|
||||
};
|
||||
|
||||
export const START_DEBUG = createRequestTypes('START_DEBUG');
|
||||
export const startDebug = {
|
||||
request: (txHash) => action(START_DEBUG[REQUEST], {txHash}),
|
||||
|
|
|
@ -18,6 +18,7 @@ import classNames from 'classnames';
|
|||
import {DEPLOYMENT_PIPELINES} from '../constants';
|
||||
import Description from './Description';
|
||||
import ContractOverviewContainer from '../containers/ContractOverviewContainer';
|
||||
import FontAwesome from 'react-fontawesome';
|
||||
|
||||
const orderClassName = (address) => {
|
||||
return classNames('mr-4', {
|
||||
|
@ -38,12 +39,14 @@ const NoWeb3 = () => (
|
|||
</Row>
|
||||
);
|
||||
|
||||
const LayoutContract = ({contract, children, cardTitle}) => (
|
||||
const LayoutContract = ({contract, children, title = contract.className, isLoading = false, isDeployed = false, deployedTitleSuffix, notDeployedTitleSuffix}) => (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
<span className={orderClassName(contract.address)}>{contract.deployIndex + 1}</span>
|
||||
{cardTitle}
|
||||
{title}
|
||||
{isLoading && <FontAwesome name="spinner" spin />}
|
||||
{!isLoading && <span>{(isDeployed && deployedTitleSuffix) || notDeployedTitleSuffix}</span>}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
|
@ -55,7 +58,11 @@ const LayoutContract = ({contract, children, cardTitle}) => (
|
|||
LayoutContract.propTypes = {
|
||||
contract: PropTypes.object,
|
||||
children: PropTypes.any,
|
||||
cardTitle: PropTypes.any
|
||||
isLoading: PropTypes.bool,
|
||||
isDeployed: PropTypes.bool,
|
||||
title: PropTypes.any,
|
||||
deployedTitleSuffix: PropTypes.string,
|
||||
notDeployedTitleSuffix: PropTypes.string
|
||||
};
|
||||
|
||||
const DeploymentResult = ({deployment}) => {
|
||||
|
@ -127,13 +134,29 @@ class Web3Contract extends React.Component {
|
|||
return this.inputsAsArray().length !== constructor.inputs.length;
|
||||
}
|
||||
|
||||
isDeployed() {
|
||||
const contractInStore = this.props.web3ContractsDeployed[this.props.contract.className];
|
||||
return contractInStore && contractInStore.isDeployed;
|
||||
}
|
||||
isCheckingIfDeployed() {
|
||||
const contractInStore = this.props.web3ContractsDeployed[this.props.contract.className];
|
||||
return contractInStore && contractInStore.running;
|
||||
}
|
||||
|
||||
render() {
|
||||
const abiConstructor = findConstructor(this.props.contract.abiDefinition);
|
||||
const argumentsRequired = abiConstructor && abiConstructor.inputs.length > 0;
|
||||
const isDeployed = this.isDeployed();
|
||||
const isCheckingIfDeployed = this.isCheckingIfDeployed();
|
||||
return (
|
||||
<LayoutContract contract={this.props.contract} cardTitle={this.props.contract.className}>
|
||||
<LayoutContract contract={this.props.contract}
|
||||
isLoading={isCheckingIfDeployed}
|
||||
isDeployed={isDeployed}
|
||||
deployedTitleSuffix={`deployed at ${this.props.contract.deployedAddress}`}
|
||||
notDeployedTitleSuffix={`not deployed`}>
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
{isDeployed && <p><strong>Contract deployed</strong></p>}
|
||||
{argumentsRequired &&
|
||||
<React.Fragment>
|
||||
<h5>Arguments:</h5>
|
||||
|
@ -178,16 +201,16 @@ Web3Contract.propTypes = {
|
|||
web3EstimateGas: PropTypes.func,
|
||||
web3Deploy: PropTypes.func,
|
||||
gasEstimate: PropTypes.object,
|
||||
deployment: PropTypes.object
|
||||
deployment: PropTypes.object,
|
||||
web3ContractsDeployed: PropTypes.object
|
||||
};
|
||||
|
||||
const EmbarkContract = ({contract, toggleContractOverview}) => (
|
||||
<LayoutContract contract={contract} cardTitle={
|
||||
<React.Fragment>
|
||||
<a href='#toggleContract' onClick={() => toggleContractOverview(contract)}>{contract.className}</a>
|
||||
<span>{(contract.address && `deployed at ${contract.address}`) || 'not deployed'}</span>
|
||||
</React.Fragment>
|
||||
}>
|
||||
<LayoutContract contract={contract}
|
||||
isDeployed={!!contract.address}
|
||||
deployedTitleSuffix={`deployed at ${contract.address}`}
|
||||
notDeployedTitleSuffix={'not deployed'}
|
||||
title={<a href='#toggleContract' onClick={() => toggleContractOverview(contract)}>{contract.className}</a>}>
|
||||
{contract.address && <p><strong>Arguments:</strong> {JSON.stringify(contract.args)}</p>}
|
||||
{contract.transactionHash &&
|
||||
<React.Fragment>
|
||||
|
@ -251,7 +274,7 @@ ContractsHeader.propTypes = {
|
|||
updateDeploymentPipeline: PropTypes.func
|
||||
};
|
||||
|
||||
const Contract = ({web3, contract, deploymentPipeline, web3Deploy, web3EstimateGas, web3Deployments, web3GasEstimates, toggleContractOverview}) => {
|
||||
const Contract = ({web3, contract, deploymentPipeline, web3Deploy, web3EstimateGas, web3Deployments, web3GasEstimates, web3ContractsDeployed, toggleContractOverview}) => {
|
||||
const deployment = web3Deployments[contract.className];
|
||||
const gasEstimate = web3GasEstimates[contract.className];
|
||||
switch (deploymentPipeline) {
|
||||
|
@ -263,7 +286,8 @@ const Contract = ({web3, contract, deploymentPipeline, web3Deploy, web3EstimateG
|
|||
gasEstimate={gasEstimate}
|
||||
contract={contract}
|
||||
web3Deploy={web3Deploy}
|
||||
web3EstimateGas={web3EstimateGas}/>;
|
||||
web3EstimateGas={web3EstimateGas}
|
||||
web3ContractsDeployed={web3ContractsDeployed}/>;
|
||||
default:
|
||||
return <React.Fragment/>;
|
||||
}
|
||||
|
@ -280,7 +304,8 @@ Contract.propTypes = {
|
|||
web3Deploy: PropTypes.func,
|
||||
web3Deployments: PropTypes.object,
|
||||
web3EstimateGas: PropTypes.func,
|
||||
web3GasEstimates: PropTypes.object
|
||||
web3GasEstimates: PropTypes.object,
|
||||
web3ContractsDeployed: PropTypes.object
|
||||
};
|
||||
|
||||
class ContractsDeployment extends React.Component {
|
||||
|
@ -343,7 +368,8 @@ ContractsDeployment.propTypes = {
|
|||
web3GasEstimates: PropTypes.object,
|
||||
web3: PropTypes.object,
|
||||
web3Deploy: PropTypes.func,
|
||||
web3EstimateGas: PropTypes.func
|
||||
web3EstimateGas: PropTypes.func,
|
||||
web3ContractsDeployed: PropTypes.object
|
||||
};
|
||||
|
||||
export default ContractsDeployment;
|
||||
|
|
|
@ -10,7 +10,14 @@ import {
|
|||
import ContractsDeployment from '../components/ContractsDeployment';
|
||||
import DataWrapper from "../components/DataWrapper";
|
||||
import PageHead from '../components/PageHead';
|
||||
import {getContracts, getDeploymentPipeline, getWeb3, getWeb3GasEstimates, getWeb3Deployments} from "../reducers/selectors";
|
||||
import {
|
||||
getContracts,
|
||||
getDeploymentPipeline,
|
||||
getWeb3,
|
||||
getWeb3GasEstimates,
|
||||
getWeb3Deployments,
|
||||
getWeb3ContractsDeployed
|
||||
} from "../reducers/selectors";
|
||||
|
||||
class DeploymentContainer extends Component {
|
||||
componentDidMount() {
|
||||
|
@ -29,6 +36,7 @@ class DeploymentContainer extends Component {
|
|||
web3EstimateGas={this.props.web3EstimateGas}
|
||||
web3Deployments={this.props.web3Deployments}
|
||||
web3GasEstimates={this.props.web3GasEstimates}
|
||||
web3ContractsDeployed={this.props.web3ContractsDeployed}
|
||||
updateDeploymentPipeline={this.props.updateDeploymentPipeline} />
|
||||
)} />
|
||||
</React.Fragment>
|
||||
|
@ -43,6 +51,7 @@ function mapStateToProps(state) {
|
|||
web3: getWeb3(state),
|
||||
web3Deployments: getWeb3Deployments(state),
|
||||
web3GasEstimates: getWeb3GasEstimates(state),
|
||||
web3ContractsDeployed: getWeb3ContractsDeployed(state),
|
||||
error: state.errorMessage,
|
||||
loading: state.loading
|
||||
};
|
||||
|
@ -60,7 +69,7 @@ DeploymentContainer.propTypes = {
|
|||
web3Deploy: PropTypes.func,
|
||||
web3Deployments: PropTypes.object,
|
||||
web3EstimateGas: PropTypes.func,
|
||||
web3GasEstimates: PropTypes.object,
|
||||
web3GasEstimates: PropTypes.object
|
||||
};
|
||||
|
||||
export default connect(
|
||||
|
|
|
@ -3,7 +3,7 @@ import {REQUEST, SUCCESS, FAILURE, CONTRACT_COMPILE, FILES, LOGOUT, AUTHENTICATE
|
|||
FETCH_CREDENTIALS, UPDATE_BASE_ETHER, CHANGE_THEME, FETCH_THEME, EXPLORER_SEARCH, DEBUGGER_INFO,
|
||||
SIGN_MESSAGE, VERIFY_MESSAGE, TOGGLE_BREAKPOINT, UPDATE_PREVIEW_URL,
|
||||
UPDATE_DEPLOYMENT_PIPELINE, WEB3_CONNECT, WEB3_DEPLOY, WEB3_ESTIMAGE_GAS, FETCH_EDITOR_TABS,
|
||||
SAVE_FILE, SAVE_FOLDER, REMOVE_FILE, DECODED_TRANSACTION} from "../actions";
|
||||
SAVE_FILE, SAVE_FOLDER, REMOVE_FILE, DECODED_TRANSACTION, WEB3_IS_DEPLOYED} from "../actions";
|
||||
import {EMBARK_PROCESS_NAME, DARK_THEME, DEPLOYMENT_PIPELINES, DEFAULT_HOST, ELEMENTS_LIMIT} from '../constants';
|
||||
|
||||
const BN_FACTOR = 10000;
|
||||
|
@ -353,7 +353,7 @@ function breakpoints(state = {}, action) {
|
|||
return state;
|
||||
}
|
||||
|
||||
function web3(state = {deployments: {}, gasEstimates: {}}, action) {
|
||||
function web3(state = {deployments: {}, gasEstimates: {}, contractsDeployed: {}}, action) {
|
||||
if (action.type === WEB3_CONNECT[SUCCESS]) {
|
||||
return {...state, instance: action.web3};
|
||||
} else if (action.type === WEB3_DEPLOY[REQUEST]) {
|
||||
|
@ -368,6 +368,12 @@ function web3(state = {deployments: {}, gasEstimates: {}}, action) {
|
|||
return {...state, gasEstimates: {...state['gasEstimates'], [action.contract.className]: {gas: action.gas, running: false, error: null}}};
|
||||
} else if (action.type === WEB3_ESTIMAGE_GAS[FAILURE]){
|
||||
return {...state, gasEstimates: {...state['gasEstimates'], [action.contract.className]: {error: action.web3Error, running: false}}};
|
||||
} else if (action.type === WEB3_IS_DEPLOYED[REQUEST]) {
|
||||
return {...state, contractsDeployed: {...state['contractsDeployed'], [action.contract.className]: {running: true, error: null}}};
|
||||
} else if (action.type === WEB3_IS_DEPLOYED[SUCCESS]){
|
||||
return {...state, contractsDeployed: {...state['contractsDeployed'], [action.contract.className]: {isDeployed: action.isDeployed, running: false, error: null}}};
|
||||
} else if (action.type === WEB3_IS_DEPLOYED[FAILURE]){
|
||||
return {...state, contractsDeployed: {...state['contractsDeployed'], [action.contract.className]: {error: action.web3Error, running: false}}};
|
||||
}
|
||||
|
||||
return state
|
||||
|
|
|
@ -237,6 +237,10 @@ export function getWeb3Deployments(state) {
|
|||
return state.web3.deployments;
|
||||
}
|
||||
|
||||
export function getWeb3ContractsDeployed(state) {
|
||||
return state.web3.contractsDeployed;
|
||||
}
|
||||
|
||||
export function getDebuggerInfo(state) {
|
||||
return state.debuggerInfo;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as storage from '../services/storage';
|
|||
import * as web3Service from '../services/web3';
|
||||
import {eventChannel} from 'redux-saga';
|
||||
import {all, call, fork, put, takeLatest, takeEvery, take, select, race} from 'redux-saga/effects';
|
||||
import {getCredentials, getWeb3} from '../reducers/selectors';
|
||||
import {getCredentials, getWeb3, getContracts} from '../reducers/selectors';
|
||||
import { DEPLOYMENT_PIPELINES } from '../constants';
|
||||
import {searchExplorer} from './searchSaga';
|
||||
|
||||
|
@ -20,6 +20,9 @@ function *doRequest(entity, serviceFn, payload) {
|
|||
|
||||
function *doWeb3Request(entity, serviceFn, payload) {
|
||||
payload.web3 = yield select(getWeb3);
|
||||
if(payload.type === actions.WEB3_DEPLOY[actions.SUCCESS]) {
|
||||
payload.contract.deployedAddress = payload.receipt.contractAddress;
|
||||
}
|
||||
try {
|
||||
const result = yield call(serviceFn, payload);
|
||||
yield put(entity.success(result, payload));
|
||||
|
@ -40,6 +43,14 @@ function *web3Connect(action) {
|
|||
}
|
||||
}
|
||||
|
||||
function *web3ContractsDeployed(action) {
|
||||
const contracts = yield select(getContracts);
|
||||
let i = 0;
|
||||
while(i < contracts.length) {
|
||||
yield put(actions.web3IsDeployed.request(contracts[i++]));
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchPlugins = doRequest.bind(null, actions.plugins, api.fetchPlugins);
|
||||
export const fetchVersions = doRequest.bind(null, actions.versions, api.fetchVersions);
|
||||
export const fetchAccount = doRequest.bind(null, actions.account, api.fetchAccount);
|
||||
|
@ -99,6 +110,7 @@ export const explorerSearch = searchExplorer.bind(null, actions.explorerSearch);
|
|||
|
||||
export const web3Deploy = doWeb3Request.bind(null, actions.web3Deploy, web3Service.deploy);
|
||||
export const web3EstimateGas = doWeb3Request.bind(null, actions.web3EstimateGas, web3Service.estimateGas);
|
||||
export const web3IsDeployed = doWeb3Request.bind(null, actions.web3IsDeployed, web3Service.isDeployed);
|
||||
|
||||
|
||||
export function *watchFetchTransaction() {
|
||||
|
@ -325,16 +337,25 @@ export function *watchVerifyMessage() {
|
|||
|
||||
export function *watchWeb3Deploy() {
|
||||
yield takeEvery(actions.WEB3_DEPLOY[actions.REQUEST], web3Deploy);
|
||||
yield takeEvery(actions.WEB3_DEPLOY[actions.SUCCESS], web3IsDeployed);
|
||||
}
|
||||
|
||||
export function *watchWeb3EstimateGas() {
|
||||
yield takeEvery(actions.WEB3_ESTIMAGE_GAS[actions.REQUEST], web3EstimateGas);
|
||||
}
|
||||
|
||||
export function *watchWeb3IsDeployed() {
|
||||
yield takeEvery(actions.WEB3_IS_DEPLOYED[actions.REQUEST], web3IsDeployed);
|
||||
}
|
||||
|
||||
export function *watchUpdateDeploymentPipeline() {
|
||||
yield takeEvery(actions.UPDATE_DEPLOYMENT_PIPELINE, web3Connect);
|
||||
}
|
||||
|
||||
export function *watchWeb3Connect() {
|
||||
yield takeEvery(actions.WEB3_CONNECT[actions.SUCCESS], web3ContractsDeployed);
|
||||
}
|
||||
|
||||
export function *watchFetchEditorTabs() {
|
||||
yield takeEvery(actions.FETCH_EDITOR_TABS[actions.REQUEST], fetchEditorTabs);
|
||||
}
|
||||
|
@ -615,7 +636,9 @@ export default function *root() {
|
|||
fork(watchVerifyMessage),
|
||||
fork(watchWeb3EstimateGas),
|
||||
fork(watchWeb3Deploy),
|
||||
fork(watchWeb3IsDeployed),
|
||||
fork(watchUpdateDeploymentPipeline),
|
||||
fork(watchWeb3Connect),
|
||||
fork(watchListenDebugger),
|
||||
fork(watchFetchEditorTabs),
|
||||
fork(watchAddEditorTabs),
|
||||
|
|
|
@ -30,3 +30,18 @@ export function deploy({web3, contract, args}) {
|
|||
.then(() => {});
|
||||
});
|
||||
}
|
||||
|
||||
export function isDeployed({web3, contract}) {
|
||||
if(!contract.deployedAddress) return Promise.resolve(false);
|
||||
return new Promise((resolve, reject) => {
|
||||
web3.eth.getCode(contract.deployedAddress, (err, byteCode) => {
|
||||
if(err) return reject(err);
|
||||
const deployedAddress = contract.deployedAddress.replace("0x", "").toLowerCase();
|
||||
resolve(
|
||||
byteCode === `0x${contract.runtimeBytecode}` || // case when contract has been deployed or redeployed
|
||||
byteCode === `0x73${deployedAddress}${contract.runtimeBytecode.replace("730000000000000000000000000000000000000000", "")}` || // case when library deployed already
|
||||
byteCode === `0x${contract.runtimeBytecode.replace("0000000000000000000000000000000000000000", deployedAddress)}` // case when library has been redeployed
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue