embark/lib/contracts.js

242 lines
7.5 KiB
JavaScript
Raw Normal View History

2016-08-14 12:04:34 +00:00
var Compiler = require('./compiler.js');
2016-09-27 04:55:35 +00:00
var toposort = require('toposort');
2016-10-29 16:02:07 +00:00
var async = require('async');
2016-08-14 12:04:34 +00:00
2016-09-28 01:04:40 +00:00
// TODO: create a contract object
2016-08-22 03:40:05 +00:00
var ContractsManager = function(options) {
this.contractFiles = options.contractFiles;
this.contractsConfig = options.contractsConfig;
2016-08-14 12:04:34 +00:00
this.contracts = {};
this.logger = options.logger;
2016-09-27 04:55:35 +00:00
this.contractDependencies = {};
2016-08-14 12:04:34 +00:00
};
ContractsManager.prototype.compileContracts = function() {
var compiler = new Compiler();
return compiler.compile_solidity(this.contractFiles);
};
2016-10-29 16:02:07 +00:00
ContractsManager.prototype.build = function(done) {
var self = this;
async.waterfall([
function compileContracts(callback) {
var compiler = new Compiler();
try {
self.compiledContracts = compiler.compile_solidity(self.contractFiles);
callback();
} catch(err) {
callback(new Error(err.message));
}
2016-10-29 16:02:07 +00:00
},
function prepareContractsFromConfig(callback) {
var className, contract;
for(className in self.contractsConfig.contracts) {
contract = self.contractsConfig.contracts[className];
2016-09-28 01:04:40 +00:00
2016-10-29 16:02:07 +00:00
contract.className = className;
contract.args = contract.args || [];
2016-10-02 15:07:56 +00:00
2016-10-29 16:02:07 +00:00
self.contracts[className] = contract;
2016-10-02 15:07:56 +00:00
}
2016-10-29 16:02:07 +00:00
callback();
},
function prepareContractsFromCompilation(callback) {
var className, compiledContract, contractConfig, contract;
var maxGas, adjustedGas;
for(className in self.compiledContracts) {
compiledContract = self.compiledContracts[className];
contractConfig = self.contractsConfig.contracts[className];
contract = self.contracts[className] || {className: className, args: []};
contract.code = compiledContract.code;
contract.runtimeBytecode = compiledContract.runtimeBytecode;
contract.gasEstimates = compiledContract.gasEstimates;
contract.functionHashes = compiledContract.functionHashes;
contract.abiDefinition = compiledContract.abiDefinition;
contract.gas = (contractConfig && contractConfig.gas) || self.contractsConfig.gas;
if (contract.deploy === undefined) {
contract.deploy = true;
}
2016-10-02 15:07:56 +00:00
2016-10-29 16:02:07 +00:00
if (contract.gas === 'auto') {
if (contract.deploy) {
maxGas = Math.max(contract.gasEstimates.creation[0], contract.gasEstimates.creation[1], 500000);
} else {
maxGas = 500000;
}
// TODO: put a check so it doesn't go over the block limit
adjustedGas = Math.round(maxGas * 1.40);
contract.gas = adjustedGas;
}
contract.gasPrice = contract.gasPrice || self.contractsConfig.gasPrice;
contract.type = 'file';
contract.className = className;
2016-10-02 15:07:56 +00:00
2016-10-29 16:02:07 +00:00
self.contracts[className] = contract;
2016-10-02 15:07:56 +00:00
}
2016-10-29 16:02:07 +00:00
callback();
},
function dealWithSpecialConfigs(callback) {
var className, contract, parentContractName, parentContract;
2016-09-28 01:04:40 +00:00
2016-10-29 16:02:07 +00:00
for(className in self.contracts) {
contract = self.contracts[className];
2016-10-02 15:07:56 +00:00
2016-10-29 16:02:07 +00:00
// if deploy intention is not specified default is true
if (contract.deploy === undefined) {
contract.deploy = true;
}
2016-10-02 15:07:56 +00:00
2016-10-29 16:02:07 +00:00
if (contract.instanceOf !== undefined) {
parentContractName = contract.instanceOf;
parentContract = self.contracts[parentContractName];
2016-10-02 15:07:56 +00:00
2016-10-29 16:02:07 +00:00
if (parentContract === className) {
self.logger.error(className + ": instanceOf is set to itself");
continue;
}
2016-10-02 15:07:56 +00:00
2016-10-29 16:02:07 +00:00
if (parentContract === undefined) {
slef.logger.error(className + ": couldn't find instanceOf contract " + parentContractName);
continue;
}
2016-09-28 01:04:40 +00:00
2016-10-29 16:02:07 +00:00
if (parentContract.args && parentContract.args.length > 0 && contract.args === []) {
contract.args = parentContract.args;
}
2016-10-02 15:07:56 +00:00
2016-10-29 16:02:07 +00:00
if (contract.code !== undefined) {
self.logger.error(className + " has code associated to it but it's configured as an instanceOf " + parentContractName);
}
2016-09-28 01:04:40 +00:00
2016-10-29 16:02:07 +00:00
contract.code = parentContract.code;
contract.runtimeBytecode = parentContract.runtimeBytecode;
contract.gasEstimates = parentContract.gasEstimates;
contract.functionHashes = parentContract.functionHashes;
contract.abiDefinition = parentContract.abiDefinition;
2016-09-28 01:04:40 +00:00
2016-10-29 16:02:07 +00:00
contract.gas = contract.gas || parentContract.gas;
contract.gasPrice = contract.gasPrice || parentContract.gasPrice;
2016-09-28 01:04:40 +00:00
}
}
2016-10-29 16:02:07 +00:00
callback();
},
function removeContractsWithNoCode(callback) {
var className, contract;
for(className in self.contracts) {
contract = self.contracts[className];
if (contract.code === undefined) {
self.logger.error(className + " has no code associated");
delete self.contracts[className];
}
}
self.logger.trace(self.contracts);
callback();
},
function determineDependencies(callback) {
var className, contract;
for(className in self.contracts) {
contract = self.contracts[className];
if (contract.args === []) continue;
var ref = contract.args;
for (var j = 0; j < ref.length; j++) {
var arg = ref[j];
if (arg[0] === "$") {
if (self.contractDependencies[className] === void 0) {
self.contractDependencies[className] = [];
}
self.contractDependencies[className].push(arg.substr(1));
}
}
}
callback();
2016-09-28 01:04:40 +00:00
}
2016-10-29 16:02:07 +00:00
], function(err, result) {
self.logger.trace("finished".underline);
if (err) {
//self.logger.debug(err.stack);
done(err);
} else {
done(null, self);
}
});
2016-08-14 12:04:34 +00:00
};
2016-09-27 04:55:35 +00:00
ContractsManager.prototype.getContract = function(className) {
return this.contracts[className];
2016-09-27 04:55:35 +00:00
};
ContractsManager.prototype.sortContracts = function(contractList) {
var converted_dependencies = [], i;
for(var contract in this.contractDependencies) {
var dependencies = this.contractDependencies[contract];
2016-10-22 19:29:41 +00:00
for(i=0; i < dependencies.length; i++) {
2016-09-27 04:55:35 +00:00
converted_dependencies.push([contract, dependencies[i]]);
}
}
var orderedDependencies = toposort(converted_dependencies).reverse();
var newList = contractList.sort(function(a,b) {
var order_a = orderedDependencies.indexOf(a.className);
var order_b = orderedDependencies.indexOf(b.className);
return order_a - order_b;
});
return newList;
};
// TODO: should be built contracts
2016-08-14 12:04:34 +00:00
ContractsManager.prototype.listContracts = function() {
var contracts = [];
2016-09-28 01:04:40 +00:00
for(var className in this.contracts) {
var contract = this.contracts[className];
2016-08-14 12:04:34 +00:00
contracts.push(contract);
}
2016-09-27 04:55:35 +00:00
return this.sortContracts(contracts);
2016-08-14 12:04:34 +00:00
};
ContractsManager.prototype.contractsState = function() {
var data = [];
for(var className in this.contracts) {
var contract = this.contracts[className];
var contractData;
if (contract.deploy === false) {
contractData = [
className.green,
'Interface or set to not deploy'.green,
2016-10-21 03:34:31 +00:00
"\t\tn/a".green
2016-10-22 19:29:41 +00:00
];
2016-10-21 11:16:15 +00:00
} else if (contract.error) {
contractData = [
className.green,
(contract.error).red,
'\t\tError'.red
2016-10-22 19:29:41 +00:00
];
} else {
contractData = [
className.green,
(contract.deployedAddress || '...').green,
((contract.deployedAddress !== undefined) ? "\t\tDeployed".green : "\t\tPending".magenta)
2016-10-22 19:29:41 +00:00
];
}
data.push(contractData);
}
return data;
};
2016-08-14 12:04:34 +00:00
module.exports = ContractsManager;