mirror of
https://github.com/status-im/embark-area-51.git
synced 2025-02-09 05:47:07 +00:00
f92d18d624
Handle use cases: 1) handle case when temp.sol doesn’t exist - due to the new saga updates, the response from retreiving a temp fiddle from the filesystem (even if it doesn’t exist) is forward to the compilation endpoint, which returns an ENOENT, and this is handled in the selector. 2) delete all code - shouldn’t return last fiddle and should compile an empty string. 3) Switch to different tab (ie contracts) then back to fiddle - previous fiddle should remain. Also fixed an issue (most likely due to latest rebase) when deploying fiddle contracts. The gasLimit was not being specified. Handled issue where entities stored in the state were not being put in the correct order, so a timestamp was sent with the requests and then sorted when the response was returned.
227 lines
7.6 KiB
JavaScript
227 lines
7.6 KiB
JavaScript
let async = require('../../utils/async_extend.js');
|
|
let SolcW = require('./solcW.js');
|
|
const fs = require('../../core/fs');
|
|
|
|
class Solidity {
|
|
|
|
constructor(embark, options) {
|
|
this.logger = embark.logger;
|
|
this.events = embark.events;
|
|
this.ipc = options.ipc;
|
|
this.contractDirectories = embark.config.contractDirectories;
|
|
this.solcAlreadyLoaded = false;
|
|
this.solcW = null;
|
|
this.useDashboard = options.useDashboard;
|
|
this.options = embark.config.embarkConfig.options.solc;
|
|
|
|
embark.registerCompiler(".sol", this.compile_solidity.bind(this));
|
|
|
|
embark.registerAPICall(
|
|
'post',
|
|
'/embark-api/contract/compile',
|
|
(req, res) => {
|
|
if(typeof req.body.codeToCompile !== 'string'){
|
|
return res.send({error: 'Body parameter \'codeToCompile\' must be a string'});
|
|
}
|
|
const input = {'fiddle': {content: req.body.codeToCompile.replace(/\r\n/g, '\n')}};
|
|
this.compile_solidity_code(input, {}, true, (errors, compilationResult) => {
|
|
// write code to filesystem so we can view the source after page refresh
|
|
const className = !compilationResult ? 'temp' : Object.keys(compilationResult).join('_');
|
|
this._writeFiddleToFile(req.body.codeToCompile, className, Boolean(compilationResult), (err) => {
|
|
if(err) this.logger.trace('Error writing fiddle to filesystem: ', err);
|
|
}); // async, do not need to wait
|
|
|
|
const responseData = {errors: errors, compilationResult: compilationResult};
|
|
this.logger.trace(`POST response /embark-api/contract/compile:\n ${JSON.stringify(responseData)}`);
|
|
res.send(responseData);
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
_writeFiddleToFile(code, className, isCompiled, cb){
|
|
fs.mkdirp('.embark/fiddles', (err) => {
|
|
if(err) return cb(err);
|
|
|
|
// always write to temp.sol file
|
|
const filePath = Solidity._getFiddlePath('temp');
|
|
fs.writeFile(filePath, code, 'utf8', cb);
|
|
|
|
// if it's compiled, also write to [classname].sol
|
|
if(isCompiled){
|
|
const filePath = Solidity._getFiddlePath(className);
|
|
fs.writeFile(filePath, code, 'utf8', cb);
|
|
}
|
|
});
|
|
}
|
|
|
|
static _getFiddlePath(className){
|
|
return fs.dappPath(`.embark/fiddles/${className}.sol`);
|
|
}
|
|
|
|
_compile(jsonObj, returnAllErrors, callback) {
|
|
const self = this;
|
|
self.solcW.compile(jsonObj, function (err, output) {
|
|
self.events.emit('contracts:compile:solc', jsonObj);
|
|
|
|
if(err){
|
|
return callback(err);
|
|
}
|
|
if (output.errors && returnAllErrors) {
|
|
return callback(output.errors);
|
|
}
|
|
|
|
if (output.errors) {
|
|
for (let i=0; i<output.errors.length; i++) {
|
|
if (output.errors[i].type === 'Warning') {
|
|
self.logger.warn(output.errors[i].formattedMessage);
|
|
}
|
|
if (output.errors[i].type === 'Error' || output.errors[i].severity === 'error') {
|
|
return callback(new Error("Solidity errors: " + output.errors[i].formattedMessage).message);
|
|
}
|
|
}
|
|
}
|
|
|
|
self.events.emit('contracts:compiled:solc', output);
|
|
callback(null, output);
|
|
});
|
|
}
|
|
|
|
compile_solidity_code(codeInputs, originalFilepaths, returnAllErrors, cb) {
|
|
const self = this;
|
|
|
|
async.waterfall([
|
|
function loadCompiler(callback) {
|
|
if (self.solcAlreadyLoaded) {
|
|
return callback();
|
|
}
|
|
self.solcW = new SolcW({logger: self.logger, events: self.events, ipc: self.ipc, useDashboard: self.useDashboard});
|
|
|
|
self.logger.info(__("loading solc compiler") + "..");
|
|
self.solcW.load_compiler(function (err) {
|
|
self.solcAlreadyLoaded = true;
|
|
callback(err);
|
|
});
|
|
},
|
|
function compileContracts(callback) {
|
|
self.logger.info(__("compiling solidity contracts") + "...");
|
|
let jsonObj = {
|
|
language: 'Solidity',
|
|
sources: codeInputs,
|
|
settings: {
|
|
optimizer: {
|
|
enabled: self.options.optimize,
|
|
runs: self.options["optimize-runs"]
|
|
},
|
|
outputSelection: {
|
|
'*': {
|
|
'': [
|
|
'ast',
|
|
'legacyAST'
|
|
],
|
|
'*': [
|
|
'abi',
|
|
'devdoc',
|
|
'evm.bytecode',
|
|
'evm.deployedBytecode',
|
|
'evm.gasEstimates',
|
|
'evm.legacyAssembly',
|
|
'evm.methodIdentifiers',
|
|
'metadata',
|
|
'userdoc'
|
|
]
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
self._compile(jsonObj, returnAllErrors, callback);
|
|
},
|
|
function createCompiledObject(output, callback) {
|
|
let json = output.contracts;
|
|
|
|
if (!output || !output.contracts) {
|
|
return callback(new Error(__("error compiling for unknown reasons")));
|
|
}
|
|
|
|
if (Object.keys(output.contracts).length === 0 && output.sourceList && output.sourceList.length > 0) {
|
|
return callback(new Error(__("error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code")).message);
|
|
}
|
|
|
|
let compiled_object = {};
|
|
|
|
for (let contractFile in json) {
|
|
for (let contractName in json[contractFile]) {
|
|
let contract = json[contractFile][contractName];
|
|
|
|
const className = contractName;
|
|
let filename = contractFile;
|
|
if(filename === 'fiddle') filename = Solidity._getFiddlePath(className);
|
|
|
|
compiled_object[className] = {};
|
|
compiled_object[className].code = contract.evm.bytecode.object;
|
|
compiled_object[className].runtimeBytecode = contract.evm.deployedBytecode.object;
|
|
compiled_object[className].realRuntimeBytecode = contract.evm.deployedBytecode.object.slice(0, -68);
|
|
compiled_object[className].swarmHash = contract.evm.deployedBytecode.object.slice(-68).slice(0, 64);
|
|
compiled_object[className].gasEstimates = contract.evm.gasEstimates;
|
|
compiled_object[className].functionHashes = contract.evm.methodIdentifiers;
|
|
compiled_object[className].abiDefinition = contract.abi;
|
|
compiled_object[className].filename = filename;
|
|
compiled_object[className].originalFilename = originalFilepaths[filename];
|
|
}
|
|
}
|
|
|
|
callback(null, compiled_object);
|
|
}
|
|
], function (err, result) {
|
|
cb(err, result);
|
|
});
|
|
}
|
|
|
|
compile_solidity(contractFiles, cb) {
|
|
if (!contractFiles.length) {
|
|
return cb();
|
|
}
|
|
let self = this;
|
|
let input = {};
|
|
let originalFilepath = {};
|
|
|
|
async.waterfall([
|
|
function prepareInput(callback) {
|
|
async.each(contractFiles,
|
|
function (file, fileCb) {
|
|
let filename = file.filename;
|
|
|
|
for (let directory of self.contractDirectories) {
|
|
let match = new RegExp("^" + directory);
|
|
filename = filename.replace(match, '');
|
|
}
|
|
|
|
originalFilepath[filename] = file.filename;
|
|
|
|
file.content(function (fileContent) {
|
|
if (!fileContent) {
|
|
self.logger.error(__('Error while loading the content of ') + filename);
|
|
return fileCb();
|
|
}
|
|
input[filename] = {content: fileContent.replace(/\r\n/g, '\n')};
|
|
fileCb();
|
|
});
|
|
},
|
|
function (err) {
|
|
callback(err);
|
|
}
|
|
);
|
|
},
|
|
function compile(callback) {
|
|
self.compile_solidity_code(input, originalFilepath, false, callback);
|
|
}
|
|
], function (err, result) {
|
|
cb(err, result);
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = Solidity;
|