mirror of
https://github.com/status-im/embark-area-51.git
synced 2025-01-22 13:19:06 +00:00
Adding scaffold option to generate contract from scratch
This commit is contained in:
parent
b815ea4d44
commit
d6de374ce7
18
cmd/cmd.js
18
cmd/cmd.js
@ -326,19 +326,29 @@ class Cmd {
|
|||||||
|
|
||||||
scaffold() {
|
scaffold() {
|
||||||
program
|
program
|
||||||
.command('scaffold [contract] [environment]')
|
.command('scaffold [contract] [fields...]')
|
||||||
.option('--framework <framework>', 'UI framework to use. (default: react)')
|
.option('--framework <framework>', 'UI framework to use. (default: react)')
|
||||||
|
.option('--contract-language <language>', 'Language used for the smart contract generation (default: solidity)')
|
||||||
.option('--overwrite', 'Overwrite existing files. (default: false)')
|
.option('--overwrite', 'Overwrite existing files. (default: false)')
|
||||||
|
|
||||||
.action(function(contract, env, options){
|
.action(function(contract, fields, options){
|
||||||
if(contract === undefined){
|
if(contract === undefined){
|
||||||
console.log("contract name is required");
|
console.log("contract name is required");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fieldMapping = {};
|
||||||
|
if(fields.length > 0){
|
||||||
|
// TODO: validate fields
|
||||||
|
fields.forEach(curr => {
|
||||||
|
const c = curr.split(':');
|
||||||
|
fieldMapping[c[0]] = c[1];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
checkDeps();
|
checkDeps();
|
||||||
i18n.setOrDetectLocale(options.locale);
|
i18n.setOrDetectLocale(options.locale);
|
||||||
options.env = env || 'development';
|
options.env = 'development';
|
||||||
options.logFile = options.logfile; // fix casing
|
options.logFile = options.logfile; // fix casing
|
||||||
options.logLevel = options.loglevel; // fix casing
|
options.logLevel = options.loglevel; // fix casing
|
||||||
options.onlyCompile = options.contracts;
|
options.onlyCompile = options.contracts;
|
||||||
@ -346,7 +356,9 @@ class Cmd {
|
|||||||
options.webpackConfigName = options.pipeline || 'development';
|
options.webpackConfigName = options.pipeline || 'development';
|
||||||
options.contract = contract;
|
options.contract = contract;
|
||||||
options.framework = options.framework || 'react';
|
options.framework = options.framework || 'react';
|
||||||
|
options.contractLanguage = options.contractLanguage || 'solidity';
|
||||||
options.overwrite = options.overwrite || false;
|
options.overwrite = options.overwrite || false;
|
||||||
|
options.fields = fieldMapping;
|
||||||
|
|
||||||
embark.scaffold(options);
|
embark.scaffold(options);
|
||||||
});
|
});
|
||||||
|
@ -437,50 +437,67 @@ class EmbarkController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scaffold(options) {
|
scaffold(options) {
|
||||||
|
|
||||||
this.context = options.context || [constants.contexts.scaffold];
|
this.context = options.context || [constants.contexts.scaffold];
|
||||||
options.onlyCompile = true;
|
|
||||||
|
|
||||||
const Engine = require('../lib/core/engine.js');
|
const Engine = require('../lib/core/engine.js');
|
||||||
const engine = new Engine({
|
const engine = new Engine({
|
||||||
env: options.env,
|
env: options.env,
|
||||||
|
client: options.client,
|
||||||
|
locale: options.locale,
|
||||||
version: this.version,
|
version: this.version,
|
||||||
embarkConfig: options.embarkConfig || 'embark.json',
|
embarkConfig: 'embark.json',
|
||||||
|
interceptLogs: false,
|
||||||
logFile: options.logFile,
|
logFile: options.logFile,
|
||||||
context: this.context
|
logLevel: options.logLevel,
|
||||||
|
events: options.events,
|
||||||
|
logger: options.logger,
|
||||||
|
config: options.config,
|
||||||
|
plugins: options.plugins,
|
||||||
|
context: this.context,
|
||||||
|
webpackConfigName: options.webpackConfigName
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (callback) {
|
function initEngine(callback) {
|
||||||
engine.init({}, callback);
|
engine.init({}, callback);
|
||||||
},
|
},
|
||||||
function (callback) {
|
function startServices(callback) {
|
||||||
|
engine.startService("scaffolding");
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
function generateContract(callback){
|
||||||
|
engine.events.request('scaffolding:generate:contract', options, function(){
|
||||||
|
// Engine is re-initiated to be able to see the new contract file
|
||||||
|
engine.init({}, callback);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function initEngineServices(callback){
|
||||||
let pluginList = engine.plugins.listPlugins();
|
let pluginList = engine.plugins.listPlugins();
|
||||||
if (pluginList.length > 0) {
|
if (pluginList.length > 0) {
|
||||||
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
|
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.startService("processManager");
|
engine.startService("processManager");
|
||||||
engine.startService("serviceMonitor");
|
|
||||||
engine.startService("libraryManager");
|
engine.startService("libraryManager");
|
||||||
engine.startService("pipeline");
|
engine.startService("codeRunner");
|
||||||
engine.startService("deployment", {onlyCompile: true});
|
|
||||||
engine.startService("web3");
|
engine.startService("web3");
|
||||||
engine.startService("scaffolding");
|
engine.startService("deployment", {onlyCompile: true});
|
||||||
|
|
||||||
engine.events.request('deploy:contracts', callback);
|
callback();
|
||||||
}
|
},
|
||||||
], (err) => {
|
function deploy(callback) {
|
||||||
if (err) {
|
engine.events.request('deploy:contracts', function (err) {
|
||||||
engine.logger.error(err.message);
|
callback(err);
|
||||||
engine.logger.info(err.stack);
|
});
|
||||||
} else {
|
},
|
||||||
|
function generateUI(callback){
|
||||||
engine.events.request("scaffolding:generate", options, () => {
|
engine.events.request("scaffolding:generate", options, () => {
|
||||||
engine.logger.info(__("finished generating the UI").underline);
|
|
||||||
process.exit();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
], function (_err) {
|
||||||
|
engine.logger.info(__("finished generating the UI").underline);
|
||||||
|
process.exit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class {{capitalize name}}Form{{@index}} extends Component {
|
|||||||
{{#if inputs.length}}
|
{{#if inputs.length}}
|
||||||
input: {
|
input: {
|
||||||
{{#each inputs}}
|
{{#each inputs}}
|
||||||
{{name}}: {{#ifeq type 'bool'}}false{{else}}''{{/ifeq}}{{#unless @last}},{{/unless}}
|
{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}: {{#ifeq type 'bool'}}false{{else}}''{{/ifeq}}{{#unless @last}},{{/unless}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
},
|
},
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -50,7 +50,7 @@ class {{capitalize name}}Form{{@index}} extends Component {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
{{#ifview stateMutability}}
|
{{#ifview stateMutability}}
|
||||||
const result = await {{../contractName}}.methods{{methodname ../functions name inputs}}({{#each inputs}}input.{{name}}{{#unless @last}}, {{/unless}}{{/each}}).call()
|
const result = await {{../contractName}}.methods{{methodname ../functions name inputs}}({{#each inputs}}input.{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}{{#unless @last}}, {{/unless}}{{/each}}).call()
|
||||||
{{#iflengthgt outputs 1}}
|
{{#iflengthgt outputs 1}}
|
||||||
this.setState({output: {
|
this.setState({output: {
|
||||||
{{#each outputs}}
|
{{#each outputs}}
|
||||||
@ -61,7 +61,7 @@ class {{capitalize name}}Form{{@index}} extends Component {
|
|||||||
this.setState({output: result});
|
this.setState({output: result});
|
||||||
{{/iflengthgt}}
|
{{/iflengthgt}}
|
||||||
{{else}}
|
{{else}}
|
||||||
const toSend = {{../contractName}}.methods{{methodname ../functions name inputs}}({{#each inputs}}input.{{name}}{{#unless @last}}, {{/unless}}{{/each}});
|
const toSend = {{../contractName}}.methods{{methodname ../functions name inputs}}({{#each inputs}}input.{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}{{#unless @last}}, {{/unless}}{{/each}});
|
||||||
|
|
||||||
const estimatedGas = await toSend.estimateGas({from: web3.eth.defaultAccount});
|
const estimatedGas = await toSend.estimateGas({from: web3.eth.defaultAccount});
|
||||||
|
|
||||||
@ -92,17 +92,17 @@ class {{capitalize name}}Form{{@index}} extends Component {
|
|||||||
{{#if inputs.length}}
|
{{#if inputs.length}}
|
||||||
{{#each inputs}}
|
{{#each inputs}}
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<ControlLabel>{{name}}</ControlLabel>
|
<ControlLabel>{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}</ControlLabel>
|
||||||
{{#ifeq type 'bool'}}
|
{{#ifeq type 'bool'}}
|
||||||
<Checkbox
|
<Checkbox
|
||||||
onClick={(e) => this.handleCheckbox(e, '{{name}}')}
|
onClick={(e) => this.handleCheckbox(e, '{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}')}
|
||||||
/>
|
/>
|
||||||
{{else}}
|
{{else}}
|
||||||
<FormControl
|
<FormControl
|
||||||
type="text"
|
type="text"
|
||||||
defaultValue={ input.{{name}} }
|
defaultValue={ input.{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}} }
|
||||||
placeholder="{{type}}"
|
placeholder="{{type}}"
|
||||||
onChange={(e) => this.handleChange(e, '{{name}}')}
|
onChange={(e) => this.handleChange(e, '{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}')}
|
||||||
/>
|
/>
|
||||||
{{/ifeq}}
|
{{/ifeq}}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
50
lib/modules/scaffolding-solidity/index.js
Normal file
50
lib/modules/scaffolding-solidity/index.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
const Handlebars = require('handlebars');
|
||||||
|
const fs = require('../../core/fs');
|
||||||
|
|
||||||
|
class ScaffoldingSolidity {
|
||||||
|
constructor(embark, options){
|
||||||
|
this.embark = embark;
|
||||||
|
this.options = options;
|
||||||
|
this.embark.registerDappGenerator('solidity', this.build.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateFile(contract, templateFilename, extension, data, overwrite){
|
||||||
|
const filename = contract.className.toLowerCase() + '.' + extension;
|
||||||
|
const filePath = './contracts/' + filename;
|
||||||
|
if (!overwrite && fs.existsSync(filePath)){
|
||||||
|
throw new Error("file '" + filePath + "' already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
const templatePath = fs.embarkPath('lib/modules/scaffolding-solidity/templates/' + templateFilename);
|
||||||
|
const source = fs.readFileSync(templatePath).toString();
|
||||||
|
const template = Handlebars.compile(source);
|
||||||
|
|
||||||
|
// Write template
|
||||||
|
const result = template(data);
|
||||||
|
fs.writeFileSync(filePath, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
build(contractDetails, overwrite, cb){
|
||||||
|
const {contract, fields} = contractDetails;
|
||||||
|
try {
|
||||||
|
const filename = contract.className.toLowerCase();
|
||||||
|
|
||||||
|
this._generateFile(contract, 'contract.sol.tpl', 'sol',
|
||||||
|
{
|
||||||
|
'contractName': contract.className,
|
||||||
|
'structName': contract.className + "Struct",
|
||||||
|
'fields': Object.keys(fields).map(f => { return {name:f, type:fields[f]}; })
|
||||||
|
}, overwrite);
|
||||||
|
this.embark.logger.info("contracts/" + filename + ".sol generated");
|
||||||
|
|
||||||
|
cb();
|
||||||
|
|
||||||
|
} catch(error){
|
||||||
|
this.embark.logger.error(error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ScaffoldingSolidity;
|
45
lib/modules/scaffolding-solidity/templates/contract.sol.tpl
Normal file
45
lib/modules/scaffolding-solidity/templates/contract.sol.tpl
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
|
contract {{contractName}} {
|
||||||
|
|
||||||
|
struct {{structName}} {
|
||||||
|
{{#each fields}}
|
||||||
|
{{type}} {{name}};
|
||||||
|
{{/each}}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{structName}}[] public items;
|
||||||
|
|
||||||
|
event ItemCreated(uint id, address createdBy);
|
||||||
|
event ItemDeleted(uint id, address deletedBy);
|
||||||
|
event ItemUpdated(uint id, address updatedBy);
|
||||||
|
|
||||||
|
function add({{#each fields}}{{type}} _{{name}}{{#unless @last}}, {{/unless}}{{/each}}) public {
|
||||||
|
uint id = items.length++;
|
||||||
|
items[id] = {{structName}}({
|
||||||
|
{{#each fields}}
|
||||||
|
{{name}}: _{{name}}{{#unless @last}},{{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
});
|
||||||
|
|
||||||
|
emit ItemCreated(id, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(uint _id, {{#each fields}}{{type}} _{{name}}{{#unless @last}}, {{/unless}}{{/each}}) public {
|
||||||
|
require(_id < items.length, "Invalid {{structName}} id");
|
||||||
|
|
||||||
|
{{#each fields}}
|
||||||
|
items[_id].{{name}} = _{{name}};
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
emit ItemUpdated(_id, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(uint _id) public {
|
||||||
|
require(_id < items.length, "Invalid {{structName}} id");
|
||||||
|
|
||||||
|
delete items[_id];
|
||||||
|
emit ItemDeleted(_id, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,7 +6,14 @@ class Scaffolding {
|
|||||||
|
|
||||||
engine.events.setCommandHandler("scaffolding:generate", (options, cb) => {
|
engine.events.setCommandHandler("scaffolding:generate", (options, cb) => {
|
||||||
this.framework = options.framework;
|
this.framework = options.framework;
|
||||||
this.generate(options.contract, options.overwrite, cb);
|
this.fields = options.fields;
|
||||||
|
this.generate(options.contract, options.overwrite, false, cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
engine.events.setCommandHandler("scaffolding:generate:contract", (options, cb) => {
|
||||||
|
this.framework = options.contractLanguage;
|
||||||
|
this.fields = options.fields;
|
||||||
|
this.generate(options.contract, options.overwrite, true, cb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,32 +21,47 @@ class Scaffolding {
|
|||||||
return this.engine.config.contractsConfig.contracts[contractName] !== undefined;
|
return this.engine.config.contractsConfig.contracts[contractName] !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
generate(contractName, overwrite, cb){
|
getScaffoldPlugin(framework){
|
||||||
if(this.framework === 'react'){
|
|
||||||
this.plugins.loadInternalPlugin('scaffolding-react', this.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
let dappGenerators = this.plugins.getPluginsFor('dappGenerator');
|
let dappGenerators = this.plugins.getPluginsFor('dappGenerator');
|
||||||
|
let builder;
|
||||||
let build = null;
|
|
||||||
dappGenerators.forEach((plugin) => {
|
dappGenerators.forEach((plugin) => {
|
||||||
plugin.dappGenerators.forEach((d) => {
|
plugin.dappGenerators.forEach((d) => {
|
||||||
if(d.framework === this.framework){
|
if(d.framework === framework){
|
||||||
build = d.cb;
|
builder = d.cb;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
if(build === null){
|
generate(contractName, overwrite, preDeployment, cb){
|
||||||
|
|
||||||
|
switch(this.framework){
|
||||||
|
case 'react':
|
||||||
|
this.plugins.loadInternalPlugin('scaffolding-react', this.options);
|
||||||
|
break;
|
||||||
|
case 'solidity':
|
||||||
|
this.plugins.loadInternalPlugin('scaffolding-solidity', this.options);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = this.fields;
|
||||||
|
|
||||||
|
let build = this.getScaffoldPlugin(this.framework);
|
||||||
|
if(!build){
|
||||||
this.engine.logger.error("Could not find plugin for framework '" + this.framework + "'");
|
this.engine.logger.error("Could not find plugin for framework '" + this.framework + "'");
|
||||||
|
process.exit();
|
||||||
cb();
|
cb();
|
||||||
} else if(!this.isContract(contractName)){
|
} else if(!this.isContract(contractName) && Object.getOwnPropertyNames(this.fields).length === 0){
|
||||||
this.engine.logger.error("contract '" + contractName + "' does not exist");
|
this.engine.logger.error("contract '" + contractName + "' does not exist");
|
||||||
cb();
|
cb();
|
||||||
|
} else if(preDeployment) {
|
||||||
|
build({contract: {className: contractName}, fields}, overwrite, cb);
|
||||||
} else {
|
} else {
|
||||||
|
// Contract exists
|
||||||
this.engine.events.request("contracts:list", (_err, contractsList) => {
|
this.engine.events.request("contracts:list", (_err, contractsList) => {
|
||||||
if(_err) throw new Error(_err);
|
if(_err) throw new Error(_err);
|
||||||
|
|
||||||
const contract = contractsList.find(x => x.className === contractName);
|
const contract = contractsList.find(x => x.className === contractName);
|
||||||
try {
|
try {
|
||||||
build(contract, overwrite, cb);
|
build(contract, overwrite, cb);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user