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-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 = {};
|
2016-09-22 22:24:01 +00:00
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
|
|
|
ContractsManager.prototype.build = function() {
|
2016-10-02 15:07:56 +00:00
|
|
|
this.compiledContracts = this.compileContracts();
|
2016-09-27 04:55:35 +00:00
|
|
|
|
2016-10-02 15:07:56 +00:00
|
|
|
// go through config file first
|
|
|
|
for(var className in this.contractsConfig.contracts) {
|
|
|
|
var contract = this.contractsConfig.contracts[className];
|
|
|
|
|
|
|
|
contract.className = className;
|
|
|
|
contract.args = contract.args || [];
|
|
|
|
|
|
|
|
this.contracts[className] = contract;
|
|
|
|
}
|
2016-09-27 04:55:35 +00:00
|
|
|
|
2016-10-02 15:07:56 +00:00
|
|
|
// compile contracts
|
2016-08-14 12:04:34 +00:00
|
|
|
for(var className in this.compiledContracts) {
|
2016-10-02 15:07:56 +00:00
|
|
|
var compiledContract = this.compiledContracts[className];
|
|
|
|
var contractConfig = this.contractsConfig.contracts[className];
|
|
|
|
|
|
|
|
var contract = this.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;
|
2016-10-22 13:42:52 +00:00
|
|
|
contract.gas = (contractConfig && contractConfig.gas) || this.contractsConfig.gas;
|
2016-08-14 12:04:34 +00:00
|
|
|
|
2016-10-22 13:42:52 +00:00
|
|
|
if (contract.deploy === undefined) {
|
|
|
|
contract.deploy = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (contract.gas === 'auto') {
|
2016-10-21 03:22:31 +00:00
|
|
|
var maxGas;
|
|
|
|
if (contract.deploy) {
|
|
|
|
maxGas = Math.max(contract.gasEstimates.creation[0], contract.gasEstimates.creation[1], 500000);
|
|
|
|
} else {
|
|
|
|
maxGas = 500000;
|
|
|
|
}
|
2016-10-22 13:42:52 +00:00
|
|
|
// TODO: put a check so it doesn't go over the block limit
|
|
|
|
var adjustedGas = Math.round(maxGas * 1.40);
|
2016-09-23 09:33:38 +00:00
|
|
|
contract.gas = adjustedGas;
|
|
|
|
}
|
2016-10-22 13:42:52 +00:00
|
|
|
contract.gasPrice = contract.gasPrice || this.contractsConfig.gasPrice;
|
2016-09-28 01:04:40 +00:00
|
|
|
contract.type = 'file';
|
2016-08-14 12:04:34 +00:00
|
|
|
contract.className = className;
|
2016-09-28 01:04:40 +00:00
|
|
|
|
2016-08-14 12:04:34 +00:00
|
|
|
this.contracts[className] = contract;
|
|
|
|
}
|
2016-09-28 01:04:40 +00:00
|
|
|
|
2016-10-02 15:07:56 +00:00
|
|
|
// deal with special configs
|
|
|
|
for(var className in this.contracts) {
|
|
|
|
var contract = this.contracts[className];
|
|
|
|
|
|
|
|
// if deploy intention is not specified default is true
|
|
|
|
if (contract.deploy === undefined) {
|
2016-09-28 01:04:40 +00:00
|
|
|
contract.deploy = true;
|
2016-10-02 15:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (contract.instanceOf !== undefined) {
|
|
|
|
var parentContractName = contract.instanceOf;
|
|
|
|
var parentContract = this.contracts[parentContractName];
|
|
|
|
|
|
|
|
if (parentContract === className) {
|
|
|
|
this.logger.error(className + ": instanceOf is set to itself");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parentContract === undefined) {
|
|
|
|
this.logger.error(className + ": couldn't find instanceOf contract " + parentContractName);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parentContract.args && parentContract.args.length > 0 && contract.args === []) {
|
|
|
|
contract.args = parentContract.args;
|
|
|
|
}
|
2016-09-28 01:04:40 +00:00
|
|
|
|
2016-10-02 15:07:56 +00:00
|
|
|
if (contract.code !== undefined) {
|
|
|
|
this.logger.error(className + " has code associated to it but it's configured as an instanceOf " + parentContractName);
|
|
|
|
}
|
|
|
|
|
|
|
|
contract.code = parentContract.code;
|
|
|
|
contract.runtimeBytecode = parentContract.runtimeBytecode;
|
|
|
|
contract.gasEstimates = parentContract.gasEstimates;
|
|
|
|
contract.functionHashes = parentContract.functionHashes;
|
|
|
|
contract.abiDefinition = parentContract.abiDefinition;
|
|
|
|
|
|
|
|
contract.gas = contract.gas || parentContract.gas;
|
|
|
|
contract.gasPrice = contract.gasPrice || parentContract.gasPrice;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove contracts that don't have code
|
|
|
|
for(var className in this.contracts) {
|
|
|
|
var contract = this.contracts[className];
|
|
|
|
|
|
|
|
if (contract.code === undefined) {
|
|
|
|
this.logger.error(className + " has no code associated");
|
|
|
|
delete this.contracts[className];
|
2016-09-28 01:04:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-02 15:07:56 +00:00
|
|
|
this.logger.trace(this.contracts);
|
|
|
|
|
2016-09-28 01:04:40 +00:00
|
|
|
// determine dependencies
|
2016-10-02 15:07:56 +00:00
|
|
|
for(var className in this.contracts) {
|
|
|
|
var contract = this.contracts[className];
|
2016-09-28 01:04:40 +00:00
|
|
|
|
2016-10-02 15:07:56 +00:00
|
|
|
if (contract.args === []) continue;
|
2016-09-28 01:04:40 +00:00
|
|
|
|
2016-10-02 15:07:56 +00:00
|
|
|
var ref = contract.args;
|
2016-09-28 01:04:40 +00:00
|
|
|
for (var j = 0; j < ref.length; j++) {
|
|
|
|
var arg = ref[j];
|
|
|
|
if (arg[0] === "$") {
|
|
|
|
if (this.contractDependencies[className] === void 0) {
|
|
|
|
this.contractDependencies[className] = [];
|
|
|
|
}
|
|
|
|
this.contractDependencies[className].push(arg.substr(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-14 12:04:34 +00:00
|
|
|
};
|
|
|
|
|
2016-09-27 04:55:35 +00:00
|
|
|
ContractsManager.prototype.getContract = function(className) {
|
|
|
|
return this.compiledContracts[className];
|
|
|
|
};
|
|
|
|
|
|
|
|
ContractsManager.prototype.sortContracts = function(contractList) {
|
|
|
|
var converted_dependencies = [], i;
|
|
|
|
|
|
|
|
for(var contract in this.contractDependencies) {
|
|
|
|
var dependencies = this.contractDependencies[contract];
|
|
|
|
for(var i=0; i < dependencies.length; i++) {
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2016-09-22 22:24:01 +00:00
|
|
|
// 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
|
|
|
};
|
|
|
|
|
2016-09-22 22:24:01 +00:00
|
|
|
ContractsManager.prototype.contractsState = function() {
|
|
|
|
var data = [];
|
|
|
|
|
|
|
|
for(var className in this.contracts) {
|
|
|
|
var contract = this.contracts[className];
|
|
|
|
|
2016-10-21 03:31:42 +00:00
|
|
|
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-21 03:31:42 +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-21 03:31:42 +00:00
|
|
|
} else {
|
|
|
|
contractData = [
|
|
|
|
className.green,
|
|
|
|
(contract.deployedAddress || '...').green,
|
|
|
|
((contract.deployedAddress !== undefined) ? "\t\tDeployed".green : "\t\tPending".magenta)
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
data.push(contractData);
|
2016-09-22 22:24:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
};
|
|
|
|
|
2016-08-14 12:04:34 +00:00
|
|
|
module.exports = ContractsManager;
|
|
|
|
|