From 59a0eea2957c70a16044f7bb4897c39ab98dc493 Mon Sep 17 00:00:00 2001 From: emizzle Date: Fri, 2 Aug 2019 15:00:15 +1000 Subject: [PATCH] refactor(@embark/embark-deploy-tracker): Add back contract tracking Add back contract tracking to the refactored code. Deployment checks are added as plugins to the `embark-deployment` module. Adds ability to track if a contract has already been deployed, and skips deployment if so. Updates error handling flow for deployment process. Adds a contract class to the `embark-contracts-manager`, to add a `log` function for the contract. This `log` function can be called from any module that has the contract instance. Adds TS interfaces for contracts configuration. Handles the following cases: 1. Contract already deployed 2. Contract not deployed 3. Contract is configured with `{track: false}` (deploy if not deployed, and don't track) 5. Contract is configured with an `address` in the config 6. `trackContracts` set to `false` from `engine` (always deploy but don't track contracts). Currently used for the tests. 7. Contract deployment produces an error 8. Interface deployment shows warning. PR with unit tests and documenation to follow. --- .../embark-contracts-manager/package.json | 13 +- .../embark-contracts-manager/src/contract.ts | 49 +++++++ .../embark-contracts-manager/src/index.js | 20 +-- .../src/deploymentChecks.js | 84 +++++++++++ packages/embark-deploy-tracker/src/index.js | 130 ++---------------- .../src/trackingFunctions.js | 111 +++++++++++++++ .../src/contract_deployer.js | 40 +++--- packages/embark-deployment/src/index.js | 28 ++-- packages/embark-typings/index.d.ts | 1 + .../embark-typings/src/contractsConfig.d.ts | 16 +++ packages/embark-typings/src/logger.d.ts | 3 +- packages/embark/src/cmd/cmd_controller.js | 65 +++++---- .../ethereum-blockchain-client/index.js | 10 +- 13 files changed, 373 insertions(+), 197 deletions(-) create mode 100644 packages/embark-contracts-manager/src/contract.ts create mode 100644 packages/embark-deploy-tracker/src/deploymentChecks.js create mode 100644 packages/embark-deploy-tracker/src/trackingFunctions.js create mode 100644 packages/embark-typings/src/contractsConfig.d.ts diff --git a/packages/embark-contracts-manager/package.json b/packages/embark-contracts-manager/package.json index 9d09f93d9..402ec6e0f 100644 --- a/packages/embark-contracts-manager/package.json +++ b/packages/embark-contracts-manager/package.json @@ -26,21 +26,20 @@ }, "main": "./dist/index.js", "scripts": { - "build": "cross-env BABEL_ENV=node babel src --extensions \".js\" --out-dir dist --root-mode upward --source-maps", + "build": "cross-env BABEL_ENV=node babel src --extensions \".js,.ts\" --out-dir dist --root-mode upward --source-maps", "ci": "npm run qa", "clean": "npm run reset", "lint": "npm-run-all lint:*", "lint:js": "eslint src/", - "// lint:ts": "tslint -c tslint.json \"src/**/*.ts\"", + "lint:ts": "tslint -c tslint.json \"src/**/*.ts\"", "package": "npm pack", - "// qa": "npm-run-all lint typecheck build package", - "qa": "npm-run-all lint build package", + "qa": "npm-run-all lint typecheck build package", "reset": "npx rimraf dist embark-*.tgz package", "start": "npm run watch", - "// typecheck": "tsc", + "typecheck": "tsc", "watch": "run-p watch:*", "watch:build": "npm run build -- --verbose --watch", - "// watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch" + "watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch" }, "eslintConfig": { "extends": "../../.eslintrc.json" @@ -68,4 +67,4 @@ "npm": ">=6.4.1", "yarn": ">=1.12.3" } -} +} \ No newline at end of file diff --git a/packages/embark-contracts-manager/src/contract.ts b/packages/embark-contracts-manager/src/contract.ts new file mode 100644 index 000000000..4c1724152 --- /dev/null +++ b/packages/embark-contracts-manager/src/contract.ts @@ -0,0 +1,49 @@ +import { ContractConfig, Logger } from "embark"; +const { sha3 } = require("embark-utils"); +import { ABIDefinition } from "web3/eth/abi"; + +export default class Contract { + private logger: Logger; + public abiDefinition?: ABIDefinition[]; + public deployedAddress?: string; + public className: string = ""; + public address?: string; + public args?: any[] = []; + public instanceOf?: string; + public gas?: number; + public gasPrice?: number; + public silent?: boolean = false; + public track?: boolean = true; + public deploy?: boolean = true; + public realRuntimeBytecode: string = ""; + public realArgs: any[] = []; + constructor(logger: Logger, contractConfig: ContractConfig) { + this.logger = logger; + this.address = contractConfig.address; + this.args = contractConfig.args; + this.instanceOf = contractConfig.instanceOf; + this.gas = contractConfig.gas; + this.gasPrice = contractConfig.gasPrice; + this.silent = contractConfig.silent; + this.track = contractConfig.track; + this.deploy = contractConfig.deploy; + } + + /** + * Calculates a hash from runtime bytecode, classname, and deploy arguments. + * Used for uniquely identifying a contract, ie in chains.json. + */ + get hash() { + return sha3(this.realRuntimeBytecode + this.className + (this.realArgs || this.args).join(",")); + } + + /** + * Logs a message to the console. Logs with loglevel trace if contract has it's silent + * property set (in the config or internally, ie ENS contracts). Otherwise, logs with + * info log level. + * @param {string} message message to log to the console + */ + public log(message: string) { + this.silent ? this.logger.trace(message) : this.logger.info(message); + } +} diff --git a/packages/embark-contracts-manager/src/index.js b/packages/embark-contracts-manager/src/index.js index de7a91218..b9a6e41f5 100644 --- a/packages/embark-contracts-manager/src/index.js +++ b/packages/embark-contracts-manager/src/index.js @@ -1,7 +1,8 @@ -import { __ } from 'embark-i18n'; +import {__} from 'embark-i18n'; +import Contract from './contract'; const async = require('async'); const constants = require('embark-core/constants'); -const { dappPath, proposeAlternative, toposort } = require('embark-utils'); +const {dappPath, proposeAlternative, toposort} = require('embark-utils'); class ContractsManager { constructor(embark, options) { @@ -123,7 +124,7 @@ class ContractsManager { self.events.request("blockchain:contract:create", {abi: contract.abiDefinition, address: contract.deployedAddress}, async (contractObj) => { try { let value = typeof req.body.value === "number" ? req.body.value.toString() : req.body.value; - const gas = await contractObj.methods[req.body.method].apply(this, req.body.inputs).estimateGas({ value }); + const gas = await contractObj.methods[req.body.method].apply(this, req.body.inputs).estimateGas({value}); contractObj.methods[req.body.method].apply(this, req.body.inputs)[funcCall]({ from: account, gasPrice: req.body.gasPrice, @@ -148,7 +149,7 @@ class ContractsManager { return res.send({result: error.message}); } - if(funcCall === 'call') { + if (funcCall === 'call') { contractLog.status = '0x1'; return res.send({result}); } @@ -223,13 +224,13 @@ class ContractsManager { '/embark-api/contract/deploy', (req, res) => { this.logger.trace(`POST request /embark-api/contract/deploy:\n ${JSON.stringify(req.body)}`); - if(typeof req.body.compiledContract !== 'object'){ + if (typeof req.body.compiledContract !== 'object') { return res.send({error: 'Body parameter \'compiledContract\' must be an object'}); } self.compiledContracts = Object.assign(self.compiledContracts, req.body.compiledContract); const contractNames = Object.keys(req.body.compiledContract); self.build((err, _mgr) => { - if(err){ + if (err) { return res.send({error: err.message}); } @@ -243,12 +244,12 @@ class ContractsManager { }); }, (err) => { let responseData = {}; - if(err){ + if (err) { responseData.error = err.message; } else responseData.result = contractNames; - this.logger.trace(`POST response /embark-api/contract/deploy:\n ${JSON.stringify(responseData)}`); - res.send(responseData); + this.logger.trace(`POST response /embark-api/contract/deploy:\n ${JSON.stringify(responseData)}`); + res.send(responseData); }); }, false, false); } @@ -263,6 +264,7 @@ class ContractsManager { self.events.emit("status", __("Building...")); async.eachOf(contractsConfig.contracts, (contract, className, eachCb) => { + contract = new Contract(self.logger, contract); if (!contract.artifact) { contract.className = className; contract.args = contract.args || []; diff --git a/packages/embark-deploy-tracker/src/deploymentChecks.js b/packages/embark-deploy-tracker/src/deploymentChecks.js new file mode 100644 index 000000000..bfce839eb --- /dev/null +++ b/packages/embark-deploy-tracker/src/deploymentChecks.js @@ -0,0 +1,84 @@ +import {__} from 'embark-i18n'; +import {toChecksumAddress} from 'embark-utils'; +import Web3 from "web3"; + +export default class DeploymentChecks { + constructor({trackingFunctions, logger, events, plugins}) { + this.trackingFunctions = trackingFunctions; + this.logger = logger; + this.events = events; + this.plugins = plugins; + this._web3 = null; + } + + get web3() { + return (async () => { + if (!this._web3) { + const provider = await this.events.request2("blockchain:client:provider", "ethereum"); + this._web3 = new Web3(provider); + } + return this._web3; + })(); + } + + async checkContractConfig(params, cb) { + const {contract} = params; + + // previous event action check + if (!params.shouldDeploy) { + return cb(null, params); + } + + // contract config address field set - do not deploy + if (contract.address !== undefined) { + try { + toChecksumAddress(contract.address); + } catch (e) { + return cb(e); + } + contract.deployedAddress = contract.address; + contract.log(contract.className.bold.cyan + __(" already deployed at ").green + contract.deployedAddress.bold.cyan); + params.shouldDeploy = false; + return cb(null, params); + } + + cb(null, params); + } + + async checkIfAlreadyDeployed(params, cb) { + const {contract} = params; + const trackedContract = this.trackingFunctions.getContract(contract); + + // previous event action check + if (!params.shouldDeploy) { + return cb(null, params); + } + + // contract is not already tracked - deploy + if (!trackedContract || !trackedContract.address) { + return cb(null, params); + } + + // tracked contract has track field set - deploy anyway, but tell user + if (trackedContract.track === false || this.trackingFunctions.trackContracts === false) { + contract.log(contract.className.bold.cyan + __(" will be redeployed").green); + return cb(null, params); + } + + // if bytecode for the contract in chains.json exists on chain - don't deploy + const web3 = await this.web3; + let codeInChain = ""; + try { + codeInChain = await web3.eth.getCode(trackedContract.address); + } + catch (err) { + return cb(err); + } + if (codeInChain.length > 3) { // it is "0x" or "0x0" for empty code, depending on web3 version + contract.deployedAddress = trackedContract.address; + contract.log(contract.className.bold.cyan + __(" already deployed at ").green + contract.deployedAddress.bold.cyan); + params.shouldDeploy = false; + } + cb(null, params); + } +} diff --git a/packages/embark-deploy-tracker/src/index.js b/packages/embark-deploy-tracker/src/index.js index 95933d695..6bbb87629 100644 --- a/packages/embark-deploy-tracker/src/index.js +++ b/packages/embark-deploy-tracker/src/index.js @@ -1,130 +1,24 @@ -import { __ } from 'embark-i18n'; -import { dappPath, sha3 } from 'embark-utils'; -const Web3 = require('web3'); +import DeploymentChecks from "./deploymentChecks"; +import TrackingFunctions from "./trackingFunctions"; class DeployTracker { - constructor(embark, options) { - this.logger = embark.logger; - this.events = embark.events; - this.plugins = options.plugins; - this.fs = embark.fs; + constructor(embark, {trackContracts, env, plugins}) { + const {logger, events, fs, config} = embark; this.embark = embark; - this.trackContracts = (options.trackContracts !== false); // TODO: unclear where env comes from // TODO: we should be getting the env from a request to the config - this.env = options.env; - this.chainConfig = {}; - this.chainFile = embark.config.contractsConfig.tracking; - this.events.on("blockchain:started", this.loadChainTrackerFile.bind(this)); - this.embark.registerActionForEvent('deployment:deployContracts:beforeAll', this.setCurrentChain.bind(this)); - this.embark.registerActionForEvent("deployment:contract:deployed", this.trackAndSaveContract.bind(this)); - this.embark.registerActionForEvent("deploy:contract:shouldDeploy", this.checkIfDeploymentIsNeeded.bind(this)); + const trackingFunctions = new TrackingFunctions({config, fs, logger, events, env, trackContracts}); + const deploymentChecks = new DeploymentChecks({trackingFunctions, logger, events, plugins}); + + this.embark.events.on("blockchain:started", trackingFunctions.loadChainTrackerFile.bind(trackingFunctions)); + this.embark.registerActionForEvent('deployment:deployContracts:beforeAll', trackingFunctions.setCurrentChain.bind(trackingFunctions)); + this.embark.registerActionForEvent("deployment:contract:deployed", trackingFunctions.trackAndSaveContract.bind(trackingFunctions)); + this.embark.registerActionForEvent("deployment:contract:shouldDeploy", deploymentChecks.checkContractConfig.bind(deploymentChecks)); + this.embark.registerActionForEvent("deployment:contract:shouldDeploy", deploymentChecks.checkIfAlreadyDeployed.bind(deploymentChecks)); } - - trackAndSaveContract(params, cb) { - if (!this.embark.config.contractsConfig.tracking) return cb(); - let contract = params.contract; - this.trackContract(contract.className, contract.realRuntimeBytecode, contract.realArgs, contract.deployedAddress); - this.save(); - cb(); - } - - checkIfDeploymentIsNeeded(params, cb) { - if (!this.embark.config.contractsConfig.tracking) return; - if (!this.trackContracts) { - return cb(null, params); - } - - let contract = params.contract; - let trackedContract = this.getContract(contract.className, contract.realRuntimeBytecode, contract.realArgs); - if (trackedContract) { - params.contract.address = trackedContract.address; - } - if (params.shouldDeploy && trackedContract) { - params.shouldDeploy = true; - } - cb(null, params); - } - - loadChainTrackerFile() { - if (this.chainFile === false) return; - if (this.chainFile === undefined) this.chainFile = ".embark/chains.json"; - this.chainFile = dappPath(this.chainFile); - if (!this.fs.existsSync(this.chainFile)) { - this.logger.info(this.chainFile + ' ' + __('file not found, creating it...')); - this.fs.outputJSONSync(this.chainFile, {}); - } - - this.chainConfig = this.fs.readJSONSync(this.chainFile); - } - - setCurrentChain(_params, callback) { - if (!this.embark.config.contractsConfig.tracking) return callback(); - if (this.chainFile === false) return callback(); - if (this.chainConfig === false) { - this.currentChain = {contracts: []}; - return callback(); - } - - this.getBlock(0, (err) => { - if (err) { - // Retry with block 1 (Block 0 fails with Ganache-cli using the --fork option) - return this.getBlock(1, callback); - } - callback(); - }); - } - - async getBlock(blockNum, cb) { - let provider = await this.events.request2("blockchain:client:provider", "ethereum"); - var web3 = new Web3(provider); - - try { - let block = await web3.eth.getBlock(blockNum, true); - let chainId = block.hash; - - if (self.chainConfig[chainId] === undefined) { - self.chainConfig[chainId] = { contracts: {} }; - } - - self.currentChain = self.chainConfig[chainId]; - - self.currentChain.name = self.env; - cb(); - } catch (err) { - return cb(err); - } - } - - loadConfig(config) { - this.chainConfig = config; - return this; - } - - trackContract(name, code, args, address) { - if (!this.currentChain) return false; - this.currentChain.contracts[sha3(code + name + args.join(','))] = { name, address }; - } - - getContract(name, code, args) { - if (!this.currentChain) return false; - let contract = this.currentChain.contracts[sha3(code + name + args.join(','))]; - if (contract && contract.address === undefined) { - return false; - } - return contract; - } - - save() { - if (this.chainConfig === false) { - return; - } - this.fs.writeJSONSync(this.chainFile, this.chainConfig, {spaces: 2}); - } - } module.exports = DeployTracker; diff --git a/packages/embark-deploy-tracker/src/trackingFunctions.js b/packages/embark-deploy-tracker/src/trackingFunctions.js new file mode 100644 index 000000000..3c81a65bb --- /dev/null +++ b/packages/embark-deploy-tracker/src/trackingFunctions.js @@ -0,0 +1,111 @@ +import {__} from 'embark-i18n'; +import {dappPath} from 'embark-utils'; +import Web3 from 'web3'; + +export default class TrackingFunctions { + constructor({config, env, fs, events, logger, trackContracts}) { + this.config = config; + this.chainConfig = {}; + this.chainFile = config.contractsConfig.tracking; + this.currentChain = null; + this.env = env; + this.fs = fs; + this.events = events; + this.logger = logger; + this._web3 = null; + this.trackContracts = (trackContracts !== false); + } + + get web3() { + return (async () => { + if (!this._web3) { + const provider = await this.events.request2("blockchain:client:provider", "ethereum"); + this._web3 = new Web3(provider); + } + return this._web3; + })(); + } + + getContract(contract) { + if (!this.currentChain) return false; + let contractInFile = this.currentChain.contracts[contract.hash]; + if (contractInFile && contractInFile.address === undefined) { + return false; + } + return contractInFile; + } + + trackAndSaveContract(params, cb) { + const {contract} = params; + if (!this.chainFile || !this.trackContracts || contract.track === false) return cb(); + this.trackContract(contract); + this.save(); + cb(); + } + + loadChainTrackerFile() { + if (this.chainFile === false) return; + if (this.chainFile === undefined) this.chainFile = ".embark/chains.json"; + this.chainFile = dappPath(this.chainFile); + if (!this.fs.existsSync(this.chainFile)) { + this.logger.info(this.chainFile + ' ' + __('file not found, creating it...')); + this.fs.outputJSONSync(this.chainFile, {}); + this.chainConfig = {}; + return; + } + + this.chainConfig = this.fs.readJSONSync(this.chainFile); + } + + setCurrentChain(_params, callback) { + if (this.chainFile === false) return callback(); + if (this.chainConfig === false) { + this.currentChain = {contracts: []}; + return callback(); + } + + this.getBlock(0, (err) => { + if (err) { + // Retry with block 1 (Block 0 fails with Ganache-cli using the --fork option) + return this.getBlock(1, callback); + } + callback(); + }); + } + + async getBlock(blockNum, cb) { + try { + const web3 = await this.web3; + let block = await web3.eth.getBlock(blockNum, true); + let chainId = block.hash; + + if (this.chainConfig[chainId] === undefined) { + this.chainConfig[chainId] = {contracts: {}}; + } + + this.currentChain = this.chainConfig[chainId]; + + this.currentChain.name = this.env; + cb(); + } catch (err) { + return cb(err); + } + } + + loadConfig(config) { + this.chainConfig = config; + return this; + } + + trackContract(contract) { + if (!this.currentChain) return false; + this.currentChain.contracts[contract.hash] = {name: contract.className, address: contract.deployedAddress}; + } + + save() { + if (this.chainConfig === false) { + return; + } + this.fs.writeJSONSync(this.chainFile, this.chainConfig, {spaces: 2}); + } +} diff --git a/packages/embark-deployment/src/contract_deployer.js b/packages/embark-deployment/src/contract_deployer.js index 0181cafe9..cacaab9d2 100644 --- a/packages/embark-deployment/src/contract_deployer.js +++ b/packages/embark-deployment/src/contract_deployer.js @@ -1,4 +1,4 @@ -import { __ } from 'embark-i18n'; +import {__} from 'embark-i18n'; const async = require('async'); class ContractDeployer { @@ -8,7 +8,7 @@ class ContractDeployer { this.plugins = options.plugins; this.deployer = {}; this.events.setCommandHandler("deployment:deployer:register", (blockchainType, deployerCb) => { - this.deployer[blockchainType] = deployerCb + this.deployer[blockchainType] = deployerCb; }); this.events.setCommandHandler('deployment:contract:deploy', this.deployContract.bind(this)); @@ -28,33 +28,33 @@ class ContractDeployer { }, (next) => { // self.plugins.emitAndRunActionsForEvent('deployment:contract:arguments', {contract: contract}, (_params) => { - this.plugins.emitAndRunActionsForEvent('deployment:contract:shouldDeploy', {contract: contract, shouldDeploy: true}, (_params) => { - next(); + this.plugins.emitAndRunActionsForEvent('deployment:contract:shouldDeploy', {contract: contract, shouldDeploy: true}, (err, params) => { + next(err, params); }); }, - (next) => { - if (contract.deploy === false) { + (params, next) => { + + if (!params.shouldDeploy) { this.events.emit("deployment:contract:undeployed", contract); - return next(); + return next(null, null); } - console.dir("deploying contract"); - console.dir(contract.className); - // this.deployer[contract.blockchainType].apply(this.deployer, [contract, next]) - this.deployer["ethereum"].apply(this.deployer, [contract, next]) - // next(); + // TODO: implement `blockchainType` a la `this.deployer[contract.blockchainType].apply(this.deployer, [contract, next])` + this.deployer["ethereum"].apply(this.deployer, [contract, next]); }, - (next) => { - console.dir("-------> contract deployed") - if (contract.deploy === false) return next(); - console.dir("-------> contract deployed 2") - this.plugins.emitAndRunActionsForEvent('deployment:contract:deployed', {contract: contract}, (_params) => { - next(); + (receipt, next) => { + if (!receipt) return next(); + this.plugins.emitAndRunActionsForEvent('deployment:contract:deployed', {contract, receipt}, (err, _params) => { + next(err); }); } - ], callback); + ], (err) => { + if (err) { + this.events.emit("deploy:contract:error", contract); + } + callback(err); + }); } - } module.exports = ContractDeployer; diff --git a/packages/embark-deployment/src/index.js b/packages/embark-deployment/src/index.js index 8b9e03d5b..7b059c222 100644 --- a/packages/embark-deployment/src/index.js +++ b/packages/embark-deployment/src/index.js @@ -1,4 +1,4 @@ -import { __ } from 'embark-i18n'; +import {__} from 'embark-i18n'; const async = require('async'); const ContractDeployer = require('./contract_deployer.js'); @@ -15,7 +15,8 @@ class Deployment { this.contractDeployer = new ContractDeployer({ events: this.events, - plugins: this.plugins + plugins: this.plugins, + logger: this.logger }); this.events.setCommandHandler('deployment:contracts:deploy', (contractsList, contractDependencies, cb) => { @@ -27,17 +28,17 @@ class Deployment { this.logger.info(__("deploying contracts")); async.waterfall([ // TODO used to be called this.plugins.emitAndRunActionsForEvent("deploy:beforeAll", (err) => { - (next) => { this.plugins.emitAndRunActionsForEvent('deployment:deployContracts:beforeAll', {}, () => { next() }); }, - (next) => { this.deployAll(contracts, contractDependencies, () => { next() }); }, + (next) => {this.plugins.emitAndRunActionsForEvent('deployment:deployContracts:beforeAll', {}, () => {next()});}, + (next) => {this.deployAll(contracts, contractDependencies, next);}, (next) => { this.events.emit('contractsDeployed'); - this.plugins.emitAndRunActionsForEvent('deployment:deployContracts:afterAll', {}, () => { next() }); + this.plugins.emitAndRunActionsForEvent('deployment:deployContracts:afterAll', {}, () => {next()}); console.dir("==== finished deploying"); } ], done); } - deployContract(contract, callback) { + deployContract(contract, errors, callback) { console.dir("requesting to deploy contract") this.events.request('deployment:contract:deploy', contract, (err) => { if (err) { @@ -63,7 +64,7 @@ class Deployment { function deploy(result, callback) { console.dir("== deploy") if (typeof result === 'function') callback = result; - self.deployContract(contract, callback); + self.deployContract(contract, errors, callback); } const className = contract.className; @@ -75,23 +76,22 @@ class Deployment { contractDeploys[className].push(deploy); }) - async.auto(contractDeploys, (_err, _results) => { - if (_err) { + async.auto(contractDeploys, (err, _results) => { + if (err) { console.dir("error deploying contracts") - console.dir(_err) + console.dir(err) } if (errors.length) { - _err = __("Error deploying contracts. Please fix errors to continue."); - this.logger.error(_err); + err = __("Error deploying contracts. Please fix errors to continue."); this.events.emit("outputError", __("Error deploying contracts, please check console")); - return done(_err); + return done(err); } if (contracts.length === 0) { this.logger.info(__("no contracts found")); return done(); } this.logger.info(__("finished deploying contracts")); - done(_err); + done(err); }); } diff --git a/packages/embark-typings/index.d.ts b/packages/embark-typings/index.d.ts index 54e6cf61d..3dae9dc88 100644 --- a/packages/embark-typings/index.d.ts +++ b/packages/embark-typings/index.d.ts @@ -4,6 +4,7 @@ import "./src/remix-debug-debugtest"; export * from "./src/callbacks"; export * from "./src/contract"; export * from "./src/embark"; +export * from "./src/contractsConfig"; export * from "./src/embarkConfig"; export * from "./src/logger"; export * from "./src/maybe"; diff --git a/packages/embark-typings/src/contractsConfig.d.ts b/packages/embark-typings/src/contractsConfig.d.ts new file mode 100644 index 000000000..e58ab5080 --- /dev/null +++ b/packages/embark-typings/src/contractsConfig.d.ts @@ -0,0 +1,16 @@ +export interface ContractsConfig { + deploy: { [name: string]: ContractConfig } + gas: string | number; + tracking: boolean | string; +} + +export interface ContractConfig { + address?: string; + args?: Array; + instanceOf?: string; + gas?: number; + gasPrice?: number; + silent?: boolean; + track?: boolean; + deploy?: boolean; +} \ No newline at end of file diff --git a/packages/embark-typings/src/logger.d.ts b/packages/embark-typings/src/logger.d.ts index b2c7aba81..7e3225fb5 100644 --- a/packages/embark-typings/src/logger.d.ts +++ b/packages/embark-typings/src/logger.d.ts @@ -1,5 +1,6 @@ export interface Logger { info(text: string): void; warn(text: string): void; - error(text: string, ...args: Array): void; + trace(text: string): void; + error(text: string, ...args: Array): void; } diff --git a/packages/embark/src/cmd/cmd_controller.js b/packages/embark/src/cmd/cmd_controller.js index f207e03b5..4747630f5 100644 --- a/packages/embark/src/cmd/cmd_controller.js +++ b/packages/embark/src/cmd/cmd_controller.js @@ -1,6 +1,6 @@ -import { BlockchainClient, Simulator } from 'embark-blockchain-process'; -import { __ } from 'embark-i18n'; -import { dappPath, embarkPath } from 'embark-utils'; +import {BlockchainClient, Simulator} from 'embark-blockchain-process'; +import {__} from 'embark-i18n'; +import {dappPath, embarkPath} from 'embark-utils'; import findUp from 'find-up'; let async = require('async'); const constants = require('embark-core/constants'); @@ -209,7 +209,7 @@ class EmbarkController { engine.events.emit("status", __("Ready").green); }); - engine.events.on('file-event', async ({ fileType, path }) => { + engine.events.on('file-event', async ({fileType, path}) => { // TODO: re-add async.cargo / or use rxjs to use latest request in the queue console.dir("-- before timeout - file changed") @@ -219,7 +219,12 @@ class EmbarkController { let _contractsConfig = await engine.events.request2("config:contractsConfig"); let contractsConfig = cloneDeep(_contractsConfig); let [contractsList, contractDependencies] = await engine.events.request2("contracts:build", contractsConfig, compiledContracts); - await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); + try { + await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); + } + catch (err) { + engine.logger.error(err); + } } else if (fileType === 'asset') { engine.events.request('pipeline:generateAll', () => { console.dir("outputDone") @@ -238,7 +243,12 @@ class EmbarkController { let _contractsConfig = await engine.events.request2("config:contractsConfig"); let contractsConfig = cloneDeep(_contractsConfig); let [contractsList, contractDependencies] = await engine.events.request2("contracts:build", contractsConfig, compiledContracts); - await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); + try { + await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); + } + catch (err) { + engine.logger.error(err); + } console.dir("deployment done") await engine.events.request2("watcher:start") @@ -470,7 +480,7 @@ class EmbarkController { }); } ], function (err, canExit) { - if(err) { + if (err) { engine.logger.error(err.message || err); } // TODO: this should be moved out and determined somewhere else @@ -499,7 +509,7 @@ class EmbarkController { webpackConfigName: options.webpackConfigName }); - const isSecondaryProcess = (engine) => { return engine.ipc.connected && engine.ipc.isClient(); }; + const isSecondaryProcess = (engine) => {return engine.ipc.connected && engine.ipc.isClient();}; async.waterfall([ function initEngine(callback) { @@ -537,7 +547,7 @@ class EmbarkController { }, function deploy(callback) { // Skip if we are connected to a websocket, the server will do it - if(isSecondaryProcess(engine)) { + if (isSecondaryProcess(engine)) { return callback(); } engine.config.reloadConfig(); @@ -547,7 +557,7 @@ class EmbarkController { }, function waitForWriteFinish(callback) { // Skip if we are connected to a websocket, the server will do it - if(isSecondaryProcess(engine)) { + if (isSecondaryProcess(engine)) { return callback(); } engine.logger.info("Finished deploying".underline); @@ -695,7 +705,7 @@ class EmbarkController { callback(); }, function generateContract(callback) { - engine.events.request('scaffolding:generate:contract', options, function(files) { + engine.events.request('scaffolding:generate:contract', options, function (files) { files.forEach(file => engine.events.request('config:contractsFiles:add', file)); callback(); }); @@ -714,7 +724,7 @@ class EmbarkController { callback(); }, function deploy(callback) { - engine.events.request('deploy:contracts', function(err) { + engine.events.request('deploy:contracts', function (err) { callback(err); }); }, @@ -723,7 +733,7 @@ class EmbarkController { callback(); }); } - ], function(err) { + ], function (err) { if (err) { engine.logger.error(__("Error generating the UI: ")); engine.logger.error(err.message || err); @@ -805,7 +815,12 @@ class EmbarkController { let _contractsConfig = await engine.events.request2("config:contractsConfig"); let contractsConfig = cloneDeep(_contractsConfig); let [contractsList, contractDependencies] = await engine.events.request2("contracts:build", contractsConfig, compiledContracts); - await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); + try { + await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); + } + catch (err) { + engine.logger.error(err); + } console.dir("deployment done") await engine.events.request2('pipeline:generateAll'); @@ -831,17 +846,17 @@ class EmbarkController { // }); } // function associateToENS(hash, callback) { - // if(!options.ensDomain) { - // return callback(null, hash); - // } - // engine.events.request("storage:ens:associate", - // {name: options.ensDomain, storageHash: hash}, (err) => { - // if (err) { - // return callback(err); - // } - // engine.logger.info(__('ENS association completed for {{hash}} at {{domain}}', {hash, domain: options.ensDomain})); - // callback(); - // }); + // if(!options.ensDomain) { + // return callback(null, hash); + // } + // engine.events.request("storage:ens:associate", + // {name: options.ensDomain, storageHash: hash}, (err) => { + // if (err) { + // return callback(err); + // } + // engine.logger.info(__('ENS association completed for {{hash}} at {{domain}}', {hash, domain: options.ensDomain})); + // callback(); + // }); // } ], function (err) { if (err) { diff --git a/packages/embark/src/lib/modules/ethereum-blockchain-client/index.js b/packages/embark/src/lib/modules/ethereum-blockchain-client/index.js index 6d59df2c3..452dcd2be 100644 --- a/packages/embark/src/lib/modules/ethereum-blockchain-client/index.js +++ b/packages/embark/src/lib/modules/ethereum-blockchain-client/index.js @@ -1,3 +1,5 @@ +import {__} from 'embark-i18n'; + const async = require('async'); const Web3 = require('web3'); const embarkJsUtils = require('embarkjs').Utils; @@ -7,6 +9,7 @@ class EthereumBlockchainClient { constructor(embark, options) { this.embark = embark; this.events = embark.events; + this.logger = embark.logger; this.embark.registerActionForEvent("deployment:contract:deployed", this.addContractJSONToPipeline.bind(this)); this.embark.registerActionForEvent('deployment:contract:beforeDeploy', this.determineArguments.bind(this)); @@ -30,7 +33,7 @@ class EthereumBlockchainClient { console.dir("== ethereum contract deployer") let contractObj = new web3.eth.Contract(contract.abiDefinition, contract.address); // let deployObject = this.blockchain.deployContractObject(contractObject, {arguments: contractParams, data: dataCode}); - let contractObject = contractObj.deploy({ arguments: (contract.args || []), data: ("0x" + contract.code) }); + let contractObject = contractObj.deploy({arguments: (contract.args || []), data: ("0x" + contract.code)}); if (contract.gas === 'auto' || !contract.gas) { let gasValue = await contractObject.estimateGas(); @@ -44,7 +47,7 @@ class EthereumBlockchainClient { } // this.blockchain.deployContractFromObject(deployObject, - console.dir({ arguments: contract.args, data: ("0x" + contract.code) }); + console.dir({arguments: contract.args, data: ("0x" + contract.code)}); console.dir("------- send") embarkJsUtils.secureSend(web3, contractObject, { @@ -52,7 +55,8 @@ class EthereumBlockchainClient { }, true, (err, receipt) => { contract.deployedAddress = receipt.contractAddress; contract.transactionHash = receipt.transactionHash; - done(); + contract.log(`${contract.className.bold.cyan} ${__('deployed at').green} ${receipt.contractAddress.bold.cyan} ${__("using").green} ${receipt.gasUsed} ${__("gas").green} (txHash: ${receipt.transactionHash.bold.cyan})`); + done(err, receipt); }, (hash) => { console.dir('hash is ' + hash); });