Merge pull request #363 from embark-framework/features/vyper-plugin

Add Vyper contract support
This commit is contained in:
Iuri Matias 2018-04-13 16:25:53 -04:00 committed by GitHub
commit 1bd0b9931c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 104 additions and 7 deletions

View File

@ -160,13 +160,18 @@ Solidity/Serpent files in the contracts directory will automatically be deployed
Libraries and languages available
======
Embark can build and deploy contracts coded in Solidity. It will make them available on the client side using EmbarkJS and Web3.js.
Embark can build and deploy contracts coded in Solidity and now also in Vyper. It will make them available on the client side using EmbarkJS and Web3.js.
Further documentation for these can be found below:
* Smart Contracts: [Solidity](https://solidity.readthedocs.io/en/develop/) and [Serpent](https://github.com/ethereum/wiki/wiki/Serpent)
* Smart Contracts:
* [Solidity](https://solidity.readthedocs.io/en/develop/)
* [Vyper](https://vyper.readthedocs.io/en/latest/index.html)
* [Serpent](https://github.com/ethereum/wiki/wiki/Serpent)
* Client Side: [Web3.js](https://github.com/ethereum/wiki/wiki/JavaScript-API) and [EmbarkJS](#embarkjs)
However, to use Vyper, you need to have Vyper installed on you computer beforehand. Meaning that doing `vyper contract.v.py` is possible.
Using Contracts
======
Embark will automatically take care of deployment for you and set all needed JS bindings. For example, the contract below:

View File

@ -7,9 +7,10 @@ class Compiler {
}
compile_contracts(contractFiles, cb) {
const self = this;
let available_compilers = {};
let pluginCompilers = this.plugins.getPluginsProperty('compilers', 'compilers');
let pluginCompilers = self.plugins.getPluginsProperty('compilers', 'compilers');
pluginCompilers.forEach(function (compilerObject) {
available_compilers[compilerObject.extension] = compilerObject.cb;
});
@ -18,10 +19,13 @@ class Compiler {
async.eachObject(available_compilers,
function (extension, compiler, callback) {
// TODO: warn about files it doesn't know how to compile
let matchingFiles = contractFiles.filter(function (file) {
let fileMatch = file.filename.match(/\.[0-9a-z]+$/);
return (fileMatch && (fileMatch[0] === extension));
if (fileMatch && (fileMatch[0] === extension)) {
file.compiled = true;
return true;
}
return false;
});
compiler.call(compiler, matchingFiles || [], function (err, compileResult) {
@ -30,6 +34,12 @@ class Compiler {
});
},
function (err) {
contractFiles.forEach(file => {
if (!file.compiled) {
self.logger.warn(`${file.filename} doesn't have a compatible contract compiler. Maybe a plugin exists for it.`);
}
});
cb(err, compiledObject);
}
);

View File

@ -305,7 +305,8 @@ class Deploy {
let contractObject = new self.web3.eth.Contract(contract.abiDefinition);
try {
deployObject = contractObject.deploy({arguments: contractParams, data: "0x" + contractCode});
const dataCode = contractCode.startsWith('0x') ? contractCode : "0x" + contractCode;
deployObject = contractObject.deploy({arguments: contractParams, data: dataCode});
} catch(e) {
if (e.message.indexOf('Invalid number of parameters for "undefined"') >= 0) {
return next(new Error("attempted to deploy " + contract.className + " without specifying parameters"));

View File

@ -137,6 +137,9 @@ class Engine {
this.registerModule('solidity', {
contractDirectories: self.config.contractDirectories
});
this.registerModule('vyper', {
contractDirectories: self.config.contractDirectories
});
this.contractsManager = new ContractsManager({
contractFiles: this.config.contractsFiles,

View File

@ -49,7 +49,7 @@ class Solidity {
});
},
function compileContracts(callback) {
self.logger.info("compiling contracts...");
self.logger.info("compiling solidity contracts...");
let jsonObj = {
language: 'Solidity',
sources: input,

View File

@ -0,0 +1,78 @@
let async = require('../../utils/async_extend.js');
const shelljs = require('shelljs');
const path = require('path');
class Vyper {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.contractDirectories = options.contractDirectories;
embark.registerCompiler(".py", this.compile_vyper.bind(this));
}
compile_vyper(contractFiles, cb) {
let self = this;
async.waterfall([
function compileContracts(callback) {
self.logger.info("compiling vyper contracts...");
const compiled_object = {};
async.each(contractFiles,
function (file, fileCb) {
const className = path.basename(file.filename).split('.')[0];
compiled_object[className] = {};
async.parallel([
function getByteCode(paraCb) {
shelljs.exec(`vyper ${file.filename}`, {silent: true}, (code, stdout, stderr) => {
if (stderr) {
return paraCb(stderr);
}
if (code !== 0) {
return paraCb(`Vyper exited with error code ${code}`);
}
if (!stdout) {
return paraCb('Execution returned no bytecode');
}
const byteCode = stdout.replace(/\n/g, '');
compiled_object[className].runtimeBytecode = byteCode;
compiled_object[className].realRuntimeBytecode = byteCode;
compiled_object[className].code = byteCode;
paraCb();
});
},
function getABI(paraCb) {
shelljs.exec(`vyper -f json ${file.filename}`, {silent: true}, (code, stdout, stderr) => {
if (stderr) {
return paraCb(stderr);
}
if (code !== 0) {
return paraCb(`Vyper exited with error code ${code}`);
}
if (!stdout) {
return paraCb('Execution returned no ABI');
}
let ABI = [];
try {
ABI = JSON.parse(stdout.replace(/\n/g, ''));
} catch (e) {
return paraCb('ABI is not valid JSON');
}
compiled_object[className].abiDefinition = ABI;
paraCb();
});
}
], fileCb);
},
function (err) {
callback(err, compiled_object);
});
}
], function (err, result) {
cb(err, result);
});
}
}
module.exports = Vyper;