Merge branch 'beforeDeploy' into develop

This commit is contained in:
hodlbank 2018-01-17 23:08:22 +00:00
commit ffe9af74bb
4 changed files with 124 additions and 55 deletions

View File

@ -3,7 +3,7 @@ Extending functionality with plugins
**To add a plugin to embark:** **To add a plugin to embark:**
1. Add the npm package to package.json 1. Add the npm package to package.json
e.g ``npm install embark-babel --save`` e.g ``npm install embark-babel --save``
2. Then add the package to ``plugins:`` in embark.json 2. Then add the package to ``plugins:`` in embark.json
e.g ``"plugins": { "embark-babel": {} }`` e.g ``"plugins": { "embark-babel": {} }``
@ -29,6 +29,7 @@ The ``embark`` object then provides an api to extend different functionality of
* plugin to add standard contracts or a contract framework (``embark.registerContractConfiguration`` and ``embark.addContractFile``) * plugin to add standard contracts or a contract framework (``embark.registerContractConfiguration`` and ``embark.addContractFile``)
* plugin to make some contracts available in all environments for use by other contracts or the dapp itself e.g a Token, a DAO, ENS, etc.. (``embark.registerContractConfiguration`` and ``embark.addContractFile``) * plugin to make some contracts available in all environments for use by other contracts or the dapp itself e.g a Token, a DAO, ENS, etc.. (``embark.registerContractConfiguration`` and ``embark.addContractFile``)
* plugin to add a libraries such as react or boostrap (``embark.addFileToPipeline``) * plugin to add a libraries such as react or boostrap (``embark.addFileToPipeline``)
* plugin to process contract's binary code before deployment (``embark.beforeDeploy``)
* plugin to specify a particular web3 initialization for special provider uses (``embark.registerClientWeb3Provider``) * plugin to specify a particular web3 initialization for special provider uses (``embark.registerClientWeb3Provider``)
* plugin to create a different contract wrapper (``embark.registerContractsGeneration``) * plugin to create a different contract wrapper (``embark.registerContractsGeneration``)
* plugin to add new console commands (``embark.registerConsoleCommand``) * plugin to add new console commands (``embark.registerConsoleCommand``)
@ -137,6 +138,30 @@ This call is used to add a file to the pipeline so it's included with the dapp o
embark.addFileToPipeline("./jquery.js", {skipPipeline: true}); embark.addFileToPipeline("./jquery.js", {skipPipeline: true});
} }
**embark.registerBeforeDeploy(callback(options))**
This call can be used to add handler to process contract code after it was generated, but immediately before it is going to be deployed.
It is useful to replace placeholders with dynamic values.
options available:
* embarkDeploy - instance of Deploy class. Has following fields: web3, contractsManager, logger, env, chainConfig, gasLimit.
* pluginConfig - plugin configuration object from embark.json
* deploymentAccount - address of account which will be used to deploy this contract
* contract - contract object.
* callback - callback function that handler must call with result object as the only argument. Result object must have field contractCode with (un)modified code from contract.code
expected return: ignored
example:
.. code:: javascript
module.exports = function(embark) {
embark.registerBeforeDeploy(function(options) {
return options.contract.code.replace(/deaddeaddeaddeaddeaddeaddeaddeaddeaddead/ig, 'c0dec0dec0dec0dec0dec0dec0dec0dec0dec0de');
});
}
**embark.registerClientWeb3Provider(callback(options))** **embark.registerClientWeb3Provider(callback(options))**
@ -165,7 +190,7 @@ example:
By default Embark will use EmbarkJS to declare contracts in the dapp. You can override and use your own client side library. By default Embark will use EmbarkJS to declare contracts in the dapp. You can override and use your own client side library.
options available: options available:
* contracts - Hash of objects containing all the deployed contracts. (key: contractName, value: contract object) * contracts - Hash of objects containing all the deployed contracts. (key: contractName, value: contract object)
* abiDefinition * abiDefinition
* code * code
* deployedAddress * deployedAddress
@ -210,18 +235,15 @@ expected return: ``string`` (output to print in console) or ``boolean`` (skip co
expected doneCallback arguments: ``err`` and ``hash`` of compiled contracts expected doneCallback arguments: ``err`` and ``hash`` of compiled contracts
* Hash of objects containing the compiled contracts. (key: contractName, value: contract object) * Hash of objects containing the compiled contracts. (key: contractName, value: contract object)
* code - contract bytecode (string) * code - contract bytecode (string)
* runtimeBytecode - contract runtimeBytecode (string) * runtimeBytecode - contract runtimeBytecode (string)
* gasEstimates - gas estimates for constructor and methods (hash) * gasEstimates - gas estimates for constructor and methods (hash)
* e.g ``{"creation":[20131,38200],"external":{"get()":269,"set(uint256)":20163,"storedData()":224},"internal":{}}`` * e.g ``{"creation":[20131,38200],"external":{"get()":269,"set(uint256)":20163,"storedData()":224},"internal":{}}``
* functionHashes - object with methods and their corresponding hash identifier (hash) * functionHashes - object with methods and their corresponding hash identifier (hash)
* e.g ``{"get()":"6d4ce63c","set(uint256)":"60fe47b1","storedData()":"2a1afcd9"}`` * e.g ``{"get()":"6d4ce63c","set(uint256)":"60fe47b1","storedData()":"2a1afcd9"}``
* abiDefinition - contract abi (array of objects) * abiDefinition - contract abi (array of objects)
* e.g ``[{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}, etc...`` * e.g ``[{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}, etc...``
below a possible implementation of a solcjs plugin: below a possible implementation of a solcjs plugin:
@ -271,15 +293,12 @@ e.g ``embark.logger.info("hello")``
This call is used to listen and react to events that happen in Embark such as contract deployment This call is used to listen and react to events that happen in Embark such as contract deployment
* eventName - name of event to listen to * eventName - name of event to listen to
* available events: * available events:
* "contractsDeployed" - triggered when contracts have been deployed * "contractsDeployed" - triggered when contracts have been deployed
* "file-add", "file-change", "file-remove", "file-event" - triggered on * "file-add", "file-change", "file-remove", "file-event" - triggered on a file change, args is (filetype, path)
a file change, args is (filetype, path) * "abi", "abi-vanila", "abi-contracts-vanila" - triggered when contracts have been deployed and returns the generated JS code
* "abi", "abi-vanila", "abi-contracts-vanila" - triggered when contracts * "outputDone" - triggered when dapp is (re)generated
have been deployed and returns the generated JS code * "firstDeploymentDone" - triggered when the dapp is deployed and generated for the first time
* "outputDone" - triggered when dapp is (re)generated
* "firstDeploymentDone" - triggered when the dapp is deployed and generated
for the first time
.. code:: javascript .. code:: javascript
@ -293,4 +312,3 @@ This call is used to listen and react to events that happen in Embark such as co
} }
}); });
} }

View File

@ -13,6 +13,7 @@ class Deploy {
this.logger = options.logger; this.logger = options.logger;
this.env = options.env; this.env = options.env;
this.chainConfig = options.chainConfig; this.chainConfig = options.chainConfig;
this.plugins = options.plugins;
this.gasLimit = options.gasLimit; this.gasLimit = options.gasLimit;
} }
@ -182,6 +183,8 @@ class Deploy {
} }
let contractCode = contract.code; let contractCode = contract.code;
// Applying linked contracts
let contractsList = self.contractsManager.listContracts(); let contractsList = self.contractsManager.listContracts();
for (let contractObj of contractsList) { for (let contractObj of contractsList) {
let filename = contractObj.filename; let filename = contractObj.filename;
@ -204,47 +207,88 @@ class Deploy {
contractCode = contractCode.replace(new RegExp(toReplace, "g"), deployedAddress); contractCode = contractCode.replace(new RegExp(toReplace, "g"), deployedAddress);
} }
let contractObject = new self.web3.eth.Contract(contract.abiDefinition); // saving code changes back to contract object
let deployObject; contract.code = contractCode;
try { // selected in deploy_manager
deployObject = contractObject.deploy({arguments: contractParams, data: "0x" + contractCode}); let deploymentAccount = self.web3.eth.defaultAccount || accounts[0];
} catch(e) { let beforeDeployPlugins = self.plugins.getPluginsFor('beforeDeploy');
if (e.indexOf('Invalid number of parameters for "undefined"') >= 0) {
return callback(new Error("attempted to deploy " + contractObject.className + " without specifying parameters"));
} else {
return callback(new Error(e));
}
}
self.logger.info("deploying " + contract.className.bold.cyan + " with ".green + contract.gas + " gas".green); async.waterfall([
(asyncCallback)=>{
//self.logger.info("applying beforeDeploy plugins...", beforeDeployPlugins.length);
async.eachSeries(beforeDeployPlugins, (plugin, eachPluginCb)=>{
self.logger.info("running beforeDeploy plugin " + plugin.name + " .");
deployObject.send({ // calling each beforeDeploy handler declared by the plugin
from: accounts[0], async.eachSeries(plugin.beforeDeploy, (beforeDeployFn, eachCb)=>{
gas: contract.gas, beforeDeployFn({
gasPrice: contract.gasPrice embarkDeploy: self,
}).on('receipt', function(receipt) { pluginConfig: plugin.pluginConfig,
if (err) { deploymentAccount: deploymentAccount,
self.logger.error("error deploying contract: " + contract.className.cyan); contract: contract,
let errMsg = err.toString(); callback:
if (errMsg === 'Error: The contract code couldn\'t be stored, please check your gas amount.') { (function(resObj){
errMsg = 'The contract code couldn\'t be stored, out of gas or constructor error'; contract.code = resObj.contractCode;
eachCb();
})
});
}, ()=>{
//self.logger.info('All beforeDeploy handlers of the plugin has processed.');
eachPluginCb();
});
}, ()=>{
//self.logger.info('All beforeDeploy plugins has been processed.');
contractCode = contract.code;
asyncCallback();
});
},
(asyncCallback)=>{
let contractObject = new self.web3.eth.Contract(contract.abiDefinition);
let deployObject;
try {
deployObject = contractObject.deploy({arguments: contractParams, data: "0x" + contractCode});
} catch(e) {
if (e.indexOf('Invalid number of parameters for "undefined"') >= 0) {
return callback(new Error("attempted to deploy " + contractObject.className + " without specifying parameters"));
} else {
return callback(new Error(e));
}
} }
self.logger.error(errMsg);
contract.error = errMsg; self.logger.info("deploying " + contract.className.bold.cyan + " with ".green + contract.gas + " gas".green);
self.logger.contractsState(self.contractsManager.contractsState());
return callback(new Error(err)); deployObject.send({
} else if (receipt.contractAddress !== undefined) { from: deploymentAccount,
self.logger.info(contract.className.bold.cyan + " deployed at ".green + receipt.contractAddress.bold.cyan); gas: contract.gas,
contract.deployedAddress = receipt.contractAddress; gasPrice: contract.gasPrice
contract.transactionHash = receipt.transactionHash; }).on('receipt', function(receipt) {
self.logger.contractsState(self.contractsManager.contractsState()); if (err) {
return callback(null, receipt.contractAddress); self.logger.error("error deploying contract: " + contract.className.cyan);
let errMsg = err.toString();
if (errMsg === 'Error: The contract code couldn\'t be stored, please check your gas amount.') {
errMsg = 'The contract code couldn\'t be stored, out of gas or constructor error';
}
self.logger.error(errMsg);
contract.error = errMsg;
self.logger.contractsState(self.contractsManager.contractsState());
return callback(new Error(err));
} else if (receipt.contractAddress !== undefined) {
self.logger.info(contract.className.bold.cyan + " deployed at ".green + receipt.contractAddress.bold.cyan);
contract.deployedAddress = receipt.contractAddress;
contract.transactionHash = receipt.transactionHash;
self.logger.contractsState(self.contractsManager.contractsState());
return callback(null, receipt.contractAddress);
}
self.logger.contractsState(self.contractsManager.contractsState());
}).on('error', function(error) {
return callback(new Error("error deploying =" + contract.className + "= due to error: " + error.message));
});
} }
self.logger.contractsState(self.contractsManager.contractsState()); ]); // end of async.waterfall
}).on('error', function(error) {
return callback(new Error("error deploying =" + contract.className + "= due to error: " + error.message));
});
}); });
} }

View File

@ -77,6 +77,7 @@ class DeployManager {
logger: self.logger, logger: self.logger,
chainConfig: self.chainConfig, chainConfig: self.chainConfig,
env: self.config.env, env: self.config.env,
plugins: self.plugins,
gasLimit: self.gasLimit gasLimit: self.gasLimit
}); });

View File

@ -11,6 +11,7 @@ var Plugin = function(options) {
this.pluginConfig = options.pluginConfig; this.pluginConfig = options.pluginConfig;
this.shouldInterceptLogs = options.interceptLogs; this.shouldInterceptLogs = options.interceptLogs;
this.clientWeb3Providers = []; this.clientWeb3Providers = [];
this.beforeDeploy = [];
this.contractsGenerators = []; this.contractsGenerators = [];
this.pipeline = []; this.pipeline = [];
this.pipelineFiles = []; this.pipelineFiles = [];
@ -85,6 +86,11 @@ Plugin.prototype.registerClientWeb3Provider = function(cb) {
this.pluginTypes.push('clientWeb3Provider'); this.pluginTypes.push('clientWeb3Provider');
}; };
Plugin.prototype.registerBeforeDeploy = function(cb) {
this.beforeDeploy.push(cb);
this.pluginTypes.push('beforeDeploy');
};
Plugin.prototype.registerContractsGeneration = function(cb) { Plugin.prototype.registerContractsGeneration = function(cb) {
this.contractsGenerators.push(cb); this.contractsGenerators.push(cb);
this.pluginTypes.push('contractGeneration'); this.pluginTypes.push('contractGeneration');