Solidity

尋找優雅的 solc 編譯器解決方案

  • July 27, 2021

我首先在我的契約中遇到了 import 語句的問題,我發現 solc 編譯器沒有處理 import 語句,我們需要自己製作。在一些好人的幫助下,我將這個腳本作為我自己的編譯器,並帶有一個處理導入語句的內置函式:

"use-strict";

const path = require("path");
const solc = require("solc"); //don"t forget to install the right solc version !
const fs = require("fs-extra");

// test folder
const ERC20SourceCode = fs.readFileSync("./test/ERC20.sol");

// interfaces folder
const IERC20SourceCode = fs.readFileSync("./interfaces/IERC20.sol");
const IUniswapV2CalleeSourceCode = fs.readFileSync("./interfaces/IUniswapV2Callee.sol");
const IUniswapV2ERC20SourceCode = fs.readFileSync("./interfaces/IUniswapV2ERC20.sol");
const IUniswapV2FactorySourceCode = fs.readFileSync("./interfaces/IUniswapV2Factory.sol");
const IUniswapV2PairSourceCode = fs.readFileSync("./interfaces/IUniswapV2Pair.sol");

// libraries folder
const MathSourceCode = fs.readFileSync("./libraries/Math.sol");
const UQ112x112SourceCode = fs.readFileSync("./libraries/UQ112x112.sol");
const SafeMathSourceCode = fs.readFileSync("./libraries/SafeMath.sol");

// core folder
const UniswapV2PairSourceCode = fs.readFileSync("./UniswapV2Pair.sol");
const UniswapV2ERC20SourceCode = fs.readFileSync("./UniswapV2ERC20.sol");
const UniswapV2FactorySourceCode = fs.readFileSync("./UniswapV2Factory.sol");

const buildPath = path.resolve(__dirname, "build");

fs.removeSync(buildPath);

function compileContract(Contract) {
   const contractPath = path.resolve(__dirname, ...Contract);  
   
   const contractSourceCode = fs.readFileSync(contractPath, "utf8");
   
   fs.ensureDirSync(buildPath);

   var input = {
       language: "Solidity",
       sources: {
           Contract: {
               content: contractSourceCode
           }
       },
       settings: {
           optimizer: {
               enabled: true
           },
           outputSelection: {
               "*": {
                   "*": [ "*" ]
               }
           }
       }
   };

   function findImports(path) {
       if (path === "test/ERC20.sol") return { contents: `${ERC20SourceCode}` };
       if (path === "interfaces/IUniswapV2Factory.sol") return { contents: `${IUniswapV2FactorySourceCode}` };
       if (path === "interfaces/IERC20.sol") return { contents: `${IERC20SourceCode}` };
       if (path === "interfaces/IUniswapV2Callee.sol") return { contents: `${IUniswapV2CalleeSourceCode}` };
       if (path === "interfaces/IUniswapV2ERC20.sol") return { contents: `${IUniswapV2ERC20SourceCode}` };
       if (path === "interfaces/IUniswapV2Pair.sol") return { contents: `${IUniswapV2PairSourceCode}` };
       if (path === "libraries/Math.sol") return { contents: `${MathSourceCode}` };
       if (path === "libraries/UQ112x112.sol") return { contents: `${UQ112x112SourceCode}` };
       if (path === "libraries/SafeMath.sol") return { contents: `${SafeMathSourceCode}` };
       if (path === "UniswapV2Pair.sol") return { contents: `${UniswapV2PairSourceCode}` };
       if (path === "UniswapV2ERC20.sol") return { contents: `${UniswapV2ERC20SourceCode}` };
       if (path === "UniswapV2Factory.sol") return { contents: `${UniswapV2FactorySourceCode}` };
       else return { error: "File not found" };
     }

   let output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports }));

   for(let contractName in output.contracts.Contract) {
       fs.outputJsonSync(
           path.resolve(buildPath, `${contractName}.json`),
           output.contracts.Contract[contractName]
       );
   }    
}

compileContract(["./", "UniswapV2Factory.sol"]);
compileContract(["./", "UniswapV2Pair.sol"]);
compileContract(["./", "UniswapV2ERC20.sol"]);
compileContract(["./", "libraries", "Math.sol"]);
compileContract(["./", "libraries", "SafeMath.sol"]);
compileContract(["./", "libraries", "UQ112x112.sol"]);
compileContract(["./", "interfaces", "IUniswapV2Factory.sol"]);
compileContract(["./", "interfaces", "IERC20.sol"]);
compileContract(["./", "interfaces", "IUniswapV2Callee.sol"]);
compileContract(["./", "interfaces", "IUniswapV2ERC20.sol"]);
compileContract(["./", "interfaces", "IUniswapV2Pair.sol"]);
compileContract(["./", "test", "ERC20.sol"]);

我的問題是我是開發領域的新手,我希望有更多有經驗的人對這個解決方案的優雅提出意見。我是否正確使用了findImports功能?

我有很多if語句,這使得解決方案可能不是最優的。我還必須為我的契約中的每個導入語句儲存每個契約的原始碼……你自己會如何解決這個問題?

PS:這是我用來做這個的文件

我不知道為什麼我把它弄得這麼複雜,但是,是的,這是一個更優雅的版本!

   function findImports(path) {
       const importSourceCode = fs.readFileSync(`./${path}`);
       return { contents: `${importSourceCode}` }
     }

如果有人有更好的答案,請隨時分享!但是這個滿足我現在的小技能(當路徑變得更複雜時,它仍然會變得更複雜一點,進入 node_modules 或其他文件夾)。

編輯:這是更複雜根的另一種解決方案。此功能適用於 uniswap v2 合約的外圍文件夾。基本上它處理:

  • 從 node_module 導入
  • 與已編譯合約位於同一文件夾中的合約(即無法使用已編譯合約的導入路徑從 compile.js 腳本訪問)
  • 導入需要單向向上的路徑
  • 根目錄中的導入路徑

這是功能:

   function findImports(path) {
       let sourceCodeToImport;
       if(path[0] === "@") { // directly into node_ module
           sourceCodeToImport = fs.readFileSync(`../../../node_modules/${path}`);
           return { contents: `${sourceCodeToImport}` };
       } 
       if (arrayContractPath.length === 2) { // array contract path is "./" + contract.sol, i.e simple import in the same folder as the compile.js
           sourceCodeToImport = fs.readFileSync(`./${path}`);
           return { contents: `${sourceCodeToImport}` };
       }
       if(!path.includes("/")) { // === contract to import is in the same folder as the contract we are compiling i.e the import path from the contract fiel doesn't include "/"
           sourceCodeToImport = fs.readFileSync(`./${intermediateFoldersOfCurrentContract}/${path}`);
           return { contents: `${sourceCodeToImport}` };
       }
       else { // if neither of these, contract must be (in my case) accessible from the compile.js, i.e no need to change the path
           sourceCodeToImport = fs.readFileSync(`./${path}`);
           return { contents: `${sourceCodeToImport}` }
       }
     }

從 compile.js 文件中很難知道導入的合約是否在同一個文件夾中!因為路徑參數將與它以“./”或“../”開頭的參數相同……這使得它很難。但它工作:)

隨意接受並改進它。如果你確實改進它,請通知我。

引用自:https://ethereum.stackexchange.com/questions/104052