diff --git a/demo/app/contracts/simple_storage.sol b/demo/app/contracts/simple_storage.sol index 0b12e43db..5203fb638 100644 --- a/demo/app/contracts/simple_storage.sol +++ b/demo/app/contracts/simple_storage.sol @@ -6,9 +6,13 @@ contract SimpleStorage { } function set(uint x) { - storedData = x; + for (uint same3=0; same3 < 3; same3++) { + storedData = same3; + } + //storedData = x; } function get() constant returns (uint retVal) { return storedData; } + } diff --git a/demo/app/contracts/token.sol b/demo/app/contracts/token.sol new file mode 100644 index 000000000..09a4083a1 --- /dev/null +++ b/demo/app/contracts/token.sol @@ -0,0 +1,19 @@ +contract token { + mapping (address => uint) public coinBalanceOf; + event CoinTransfer(address sender, address receiver, uint amount); + + /* Initializes contract with initial supply tokens to the creator of the contract */ + function token(uint supply) { + if (supply == 0) supply = 10000; + coinBalanceOf[msg.sender] = supply; + } + + /* Very simple trade function */ + function sendCoin(address receiver, uint amount) returns(bool sufficient) { + if (coinBalanceOf[msg.sender] < amount) return false; + coinBalanceOf[msg.sender] -= amount; + coinBalanceOf[receiver] += amount; + CoinTransfer(msg.sender, receiver, amount); + return true; + } +} diff --git a/demo/config/contracts.json b/demo/config/contracts.json index 45f32dc59..b75501ef5 100644 --- a/demo/config/contracts.json +++ b/demo/config/contracts.json @@ -1,5 +1,7 @@ { "default": { + "gasLimit": 500000, + "gasPrice": 10000000000000, "contracts": { "SimpleStorage": { "args": [ diff --git a/lib/abi.js b/lib/abi.js new file mode 100644 index 000000000..58df204f8 --- /dev/null +++ b/lib/abi.js @@ -0,0 +1,39 @@ + +var ABIGenerator = function(contractsManager) { + this.contractsManager = contractsManager; + this.rpcHost = 'localhost'; + this.rpcPort = '8101'; +}; + +ABIGenerator.prototype.generateProvider = function() { + var result = ""; + + result += "\nif (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {"; + result += '\n\tweb3 = new Web3(web3.currentProvider);'; + result += "\n} else if (typeof Web3 !== 'undefined') {"; + result += '\n\tweb3 = new Web3(new Web3.providers.HttpProvider("http://' + this.rpcHost + ':' + this.rpcPort + '"));'; + result += '\n}'; + result += "\nweb3.eth.defaultAccount = web3.eth.accounts[0];"; + + return result; +}; + +ABIGenerator.prototype.generateContracts = function() { + var result = "\n"; + + for(var className in this.contractsManager.contracts) { + var contract = this.contractsManager.contracts[className]; + + var abi = JSON.stringify(contract.abiDefinition); + + console.log('address is ' + contract.deployedAddress); + + result += "\n" + className + "Abi = " + abi + ";"; + result += "\n" + className + "Contract = web3.eth.contract(" + className + "Abi);"; + result += "\n" + className + " = " + className + "Contract.at('" + contract.deployedAddress + "');"; + } + + return result; +}; + +module.exports = ABIGenerator; diff --git a/lib/compiler.js b/lib/compiler.js new file mode 100644 index 000000000..4391a2af2 --- /dev/null +++ b/lib/compiler.js @@ -0,0 +1,106 @@ +var shelljs = require('shelljs'); +var shelljs_global = require('shelljs/global'); +var fs = require('fs'); +var solc = require('solc'); + +var Compiler = function() { +}; + +Compiler.prototype.compile_solidity = function(contractFiles) { + var input = {}; + + for (var i = 0; i < contractFiles.length; i++){ + // TODO: this depends on the config + var filename = contractFiles[i].replace('app/contracts/',''); + input[filename] = fs.readFileSync(contractFiles[i]).toString(); + } + + var output = solc.compile({sources: input}, 1); + + if (output.errors) { + throw new Error ("Solidity errors: " + output.errors); + } + + var json = output.contracts; + + compiled_object = {}; + + for (var className in json) { + var contract = json[className]; + + compiled_object[className] = {}; + compiled_object[className].code = contract.bytecode; + compiled_object[className].runtimeBytecode = contract.runtimeBytecode; + compiled_object[className].gasEstimates = contract.gasEstimates; + compiled_object[className].functionHashes = contract.functionHashes; + compiled_object[className].abiDefinition = JSON.parse(contract.interface); + } + + return compiled_object; +}; + +Compiler.prototype.compile_serpent = function(contractFiles) { + var cmd, result, output, json, compiled_object; + + //TODO: figure out how to compile multiple files and get the correct json + var contractFile = contractFiles[0]; + + cmd = "serpent compile " + contractFile; + + result = exec(cmd, {silent: true}); + code = result.output; + + if (result.code === 1) { + throw new Error(result.output); + } + + cmd = "serpent mk_full_signature " + contractFile; + result = exec(cmd, {silent: true}); + + if (result.code === 1) { + throw new Error(result.output); + } + + json = JSON.parse(result.output.trim()); + className = contractFile.split('.')[0].split("/").pop(); + + for (var i=0; i < json.length; i++) { + var elem = json[i]; + + if (elem.outputs.length > 0) { + elem.constant = true; + } + } + + compiled_object = {}; + compiled_object[className] = {}; + compiled_object[className].code = code.trim(); + compiled_object[className].info = {}; + compiled_object[className].abiDefinition = json; + + return compiled_object; +}; + +Compiler.prototype.compile = function(contractFiles) { + var solidity = [], serpent = []; + + for (var i = 0; i < contractFiles.length; i++) { + var contractParts = contractFiles[i].split('.'), + extension = contractParts[contractParts.length-1]; + + if (extension === 'sol') { + solidity.push(contractFiles[i]); + } + else if (extension === 'se') { + serpent.push(contractFiles[i]); + } + else { + throw new Error("extension not known, got " + extension); + } + } + //TODO: Get these compiling and returning together...problem might come with the JSON objects + if (solidity.length > 0) return this.compile_solidity(solidity); + if (serpent.length > 0) return this.compile_serpent(serpent); +}; + +module.exports = Compiler; diff --git a/lib/contracts.js b/lib/contracts.js new file mode 100644 index 000000000..a1a2f7b46 --- /dev/null +++ b/lib/contracts.js @@ -0,0 +1,59 @@ +var fs = require('fs'); +var grunt = require('grunt'); +var Compiler = require('./compiler.js'); + +var ContractsManager = function(configDir, contractFiles, env) { + this.contractFiles = grunt.file.expand(contractFiles); + this.configDir = configDir; + this.env = env; + this.contracts = {}; +}; + +ContractsManager.prototype.init = function() { + this.contractsConfig = this.loadConfigFiles(); + this.compiledContracts = this.compileContracts(); +}; + +ContractsManager.prototype.loadConfigFiles = function() { + var defaultContractsConfig = JSON.parse(fs.readFileSync(this.configDir + "contracts.json"))['default']; + //var envContractsConfig = JSON.parse(fs.readFileSync(this.configDir + this.env + "/contracts.json"))[this.env]; + + //merge.recursive(defaultContractsConfig, envContractsConfig); + return defaultContractsConfig; +}; + +ContractsManager.prototype.compileContracts = function() { + var compiler = new Compiler(); + return compiler.compile_solidity(this.contractFiles); +}; + +ContractsManager.prototype.build = function() { + for(var className in this.compiledContracts) { + var contract = this.compiledContracts[className]; + var contractConfig = this.contractsConfig[className]; + + contract.gasLimit = this.contractsConfig.gasLimit; + contract.gasPrice = this.contractsConfig.gasPrice; + + if (contractConfig === undefined) { + contract.args = []; + } else { + contract.args = contractConfig.args || []; + } + + contract.className = className; + this.contracts[className] = contract; + } +}; + +ContractsManager.prototype.listContracts = function() { + var contracts = []; + for(var className in this.compiledContracts) { + var contract = this.compiledContracts[className]; + contracts.push(contract); + } + return contracts; +}; + +module.exports = ContractsManager; + diff --git a/lib/deploy.js b/lib/deploy.js index 0f48f1579..5b5cf2fe5 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -1,33 +1,54 @@ var async = require('async'); +var Compiler = require('./compiler.js'); -// needs: -// compile the contract -> file location -// gas -> config -contractObject = { - compiledCode, - abiDefinition, - gas, - gasPrice -} +var Deploy = function(web3, contractsManager) { + this.web3 = web3; + this.contractsManager = contractsManager; +}; -function deployContract(web3, contractObject, params) { - var contractObject = web3.eth.contract(contract.compiled.info.abiDefinition); +Deploy.prototype.deployContract = function(contract, params, callback) { + var contractObject = this.web3.eth.contract(contract.abiDefinition); + + var contractParams = params || contract.args; - var contractParams = params; contractParams.push({ - from: primaryAddress, - data: contract.compiled.code, + from: this.web3.eth.coinbase, + data: contract.code, gas: contract.gasLimit, gasPrice: contract.gasPrice }); - contractParams.push(callback); + + contractParams.push(function(err, transaction) { + if (err) { + console.log("error"); + callback(new Error(err)); + } else if (transaction.address !== undefined) { + console.log("address contract: " + transaction.address); + contract.deployedAddress = transaction.address; + callback(null, transaction.address); + } + }); contractObject["new"].apply(contractObject, contractParams); }; -function buildContractObject(contractCode, gas, gasPrice) { - var compiledContract = compiler.compile(contractCode); - return { - } -} +Deploy.prototype.deployAll = function(done) { + var self = this; + console.log("deployAll"); + + async.eachOfSeries(this.contractsManager.listContracts(), + function(contract, key, callback) { + console.log(arguments); + self.deployContract(contract, null, callback); + }, + function(err, results) { + console.log("finished"); + console.log(arguments); + done(); + } + ); + +}; + +module.exports = Deploy; diff --git a/lib/index.js b/lib/index.js index 6fc81efbf..5eaa148b5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,6 +1,44 @@ var async = require('async'); +var Web3 = require('web3'); + +var Deploy = require('./deploy.js'); +var ContractsManager = require('./contracts.js'); +var ABIGenerator = require('./abi.js'); var Embark = { + initConfig: function(configDir, files, env) { + this.contractsManager = new ContractsManager(configDir, files, env); + this.contractsManager.init(); + return this.contractsManager; + } }; -module.exports = Embark; +//module.exports = Embark; + +async.waterfall([ + function loadConfig(callback) { + var contractsManager = Embark.initConfig('config/', 'app/contracts/**/*.sol', 'development'); + callback(null, contractsManager); + }, + function buildContracts(contractsManager, callback) { + contractsManager.build(); + callback(null, contractsManager); + }, + function deployContracts(contractsManager, callback) { + var web3 = new Web3(); + web3.setProvider(new web3.providers.HttpProvider('http://localhost:8101')); + var deploy = new Deploy(web3, contractsManager); + deploy.deployAll(function() { + callback(null, contractsManager); + }); + }, + function generateABI(contractsManager, callback) { + var abiGenerator = new ABIGenerator(contractsManager); + console.log(abiGenerator.generateProvider()); + console.log(abiGenerator.generateContracts()); + callback(null, 'done'); + }, +], function(err, result) { + console.log(arguments); +}); + diff --git a/package.json b/package.json index 87471d624..5fbc10768 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "mkdirp": "^0.5.1", "read-yaml": "^1.0.0", "shelljs": "^0.5.0", - "solc": "^0.4.1", "toposort": "^0.2.10", "web3": "^0.15.0", "wrench": "^1.5.8"