yul-log
Version:
A Unified Toolchain for Yul+ Smart Contracts
233 lines (214 loc) • 9.46 kB
JavaScript
#!/usr/bin/env node
const yulp = require('yulp');
const fs = require('fs');
const { format } = require('path');
const { exec } = require('child_process');
const { stderr } = require('process');
const { ethers } = require('ethers');
const path = require('path');
const solc = require('solc');
const OUT_DIR = path.join(process.cwd(), "build");
/*
Yul Log is my own toolchain for compiling Yul+ Contracts capable of handing a full 1 and a half frameworks
Truffle and some very shitty hardhat artifact generation, that hardhat breaks if it sees
Also with soon to be Yul-Log config support it will be able to support dapptools artifact generation as well
Also its handling of the file structure is horrible
*/
//collect the arguments
var args = process.argv.slice(2);
//if arg is init, initialize yul-log
if (args.length==1 && args[0]=="init"){
console.log("Initializing yul-log...")
yulLogInit();
} else if(args.length == 2) {
yulLogFetch()
} else{
yulLog();
}
function yulLogFetch(){
var args = process.argv.slice(2)
if(args[1] == "bytecode") {
var contract = args[0]
fs.readFile(path.join(OUT_DIR, contract + ".json"), 'utf8', function (err,data) {
var artifact = JSON.parse(data)
process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes"], [artifact.bytecode]))
})
}
if(args[1] == "abi") {
var contract = args[0]
fs.readFile(path.join(OUT_DIR, contract + ".json"), 'utf8', function (err,data) {
var artifact = JSON.parse(data)
process.stdout.write(JSON.stringify(artifact.abi))
})
}
}
//initialize the yul-log environment
function yulLogInit(){
//clone the truffle yulp box repo and copy the contents into the root directory
exec("git clone https://github.com/ControlCplusControlV/Truffle-Yulp-Box.git\
&& cd Truffle-Yulp-Box\
&& cp -r ./* ../\
&& cd ../\
&& rm -rf Truffle-Yulp-Box",
function (error, stdout, stderr) {
if (error !== null) {
console.log('exec error: ' + error);
}
});
//compile the test contract
console.log("Compiling example contract...")
exec("yul-log truffle",
function (error, stdout, stderr) {
if (error !== null) {
console.log('exec error: ' + error);
}
console.log('Yul-Log is all set up! You can run "truffle test" to see the example tests in action.')
});
}
//function to compile yulp contracts
function yulLog(){
fs.readdir("./Yul+ Contracts/", (err, files) => {
files.forEach(file => {
fs.readFile("./Yul+ Contracts/" +file, 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
let filename = file.split(".")
let source = yulp.compile(data);
let output = JSON.parse(solc.compile(JSON.stringify({
"language": "Yul",
"sources": { "Target.yul": { "content": yulp.print(source.results) } },
"settings": {
"outputSelection": { "*": { "*": ["*"], "": [ "*" ] } },
"optimizer": {
"enabled": true,
"runs": 0,
"details": {
"yul": true
}
}
}
})));
let contractObjects = Object.keys(output.contracts["Target.yul"])
let abi = source.signatures.map(v => v.abi.slice(4, -1)).concat(source.topics.map(v => v.abi.slice(6, -1)))
let bytecode = "0x" + output.contracts["Target.yul"][contractObjects[0]]["evm"]["bytecode"]["object"];
let deployedBytecode = "0x" + output.contracts["Target.yul"][contractObjects[0]]["evm"]["deployedBytecode"]["object"];
if (process.argv.indexOf('hardhat') > -1){
// if hardhat flag is present
// Still very experimental and breaks a LOT
// But this does technically create a hardhat artifact
// that does work
var hardhatCompiled = {
"_format": "hh-sol-artifact-1",
"contractName" : filename,
"sourceName" : "contracts/" + filename,
"abi" : abi,
"bytecode" : bytecode,
"deployedBytecode" : deployedBytecode,
"linkReferences": {}, // I really don't know what this means
"deployedLinkReferences": {} // This either
}
const data = JSON.stringify(hardhatCompiled);
fs.writeFile("./artifacts/" + "contracts/" + filename[0] + '.json', data, (err) => {
// In case of a error throw err.
if (err) throw err;
})
} else if(process.argv.indexOf('truffle') > -1){
// Write compiled Yul to a Contract
fs.writeFile("./contracts/" + filename[0] + '.yul', yulp.print(source.results), (err) => {
// In case of a error throw err.
if (err) throw err;
})
let abiBlock = ""
for (let i = 0; i < abi.length; i++) {
if (abi[i].indexOf("returns") < -1 && abi[i].substring(0, 5) != "event"){
abiBlock += abi[i] + "external" +";" + "\n"
} else {
if(abi[i].substring(0, 5) != "event"){
//function get() returns (uint256);
splitABI = String(abi[i]).split(" ")
closeIndex = abi[i].indexOf(")")
let funcName = String(abi[i]).substring(0, closeIndex+1) + " external " + String(abi[i]).substring(closeIndex+1, String(abi[i]).length)
abiBlock += funcName +";" + "\n"
} else {
// Handle if its an event
abiBlock += abi[i] + ";"
}
}
}
// Convert abi to a solidity like interface
abi = "pragma solidity ^0.8.10;\n interface " + "YulpInterface" + "{\n" + abiBlock + "\n}"
// Write abi to an interface then compile that
let compiledInterface = JSON.parse(solc.compile(JSON.stringify({
"language": "Solidity",
"sources": { "Target.sol": { "content": abi } },
"settings": {
"outputSelection": { "*": { "*": ["*"], "": [ "*" ] } },
"optimizer": {
"enabled": true,
"runs": 0,
"details": {
"yul": false
}
}
}
})));
let compiledABI = compiledInterface.contracts["Target.sol"]["YulpInterface"]["abi"]
// Next run truffle compile
exec("truffle compile", (error, stdout, stderr) => {
console.log(stdout)
// Next read the created .json file, and inject abi into it
const data = JSON.parse(fs.readFileSync("./build/contracts/" + filename[0] + ".json"))
data.abi = compiledABI
data.deployedBytecode = deployedBytecode
fs.writeFileSync("./build/contracts/" + filename[0] + ".json", JSON.stringify(data, null, 4));
});
}else {
// Default Artifact That Most People can Make use of for web3 Scripts
let abiBlock = ""
for (let i = 0; i < abi.length; i++) {
if (abi[i].indexOf("returns") < -1 && abi[i].substring(0, 5) != "event"){
abiBlock += abi[i] + "external" +";" + "\n"
} else {
if(abi[i].substring(0, 5) != "event"){
//function get() returns (uint256);
splitABI = String(abi[i]).split(" ")
closeIndex = abi[i].indexOf(")")
let funcName = String(abi[i]).substring(0, closeIndex+1) + " external " + String(abi[i]).substring(closeIndex+1, String(abi[i]).length)
abiBlock += funcName +";" + "\n"
} else {
// Handle if its an event
abiBlock += abi[i] + ";"
}
}
}
// Convert abi to a solidity like interface
abi = "pragma solidity ^0.8.10;\n interface " + "YulpInterface" + "{\n" + abiBlock + "\n}"
let compiledInterface = JSON.parse(solc.compile(JSON.stringify({
"language": "Solidity",
"sources": { "Target.sol": { "content": abi } },
"settings": {
"outputSelection": { "*": { "*": ["*"], "": [ "*" ] } },
"optimizer": {
"enabled": true,
"runs": 0,
"details": {
"yul": false
}
}
}
})));
let compiledABI = compiledInterface.contracts["Target.sol"]["YulpInterface"]["abi"]
var defaultYulplusArtifact = {
"_format": "Yul+ Artifact Format v0.0.1",
"contractName" : filename,
"abi" : compiledABI,
"bytecode" : bytecode,
"deployedBytecode" : deployedBytecode,
}
fs.writeFileSync("./build/" + filename[0] + ".json", JSON.stringify(defaultYulplusArtifact, null, 4));
}
});
});
})
}