Contract-Deployment

使用 solc 編譯導入本地 OpenZeppelin 合約的合約

  • July 30, 2021

我正在嘗試使用 Node.js 編譯和部署基於 OpenZeppelin 的契約

app.get("/", (req, res) => {
 const input = fs.readFileSync("./BaseContract.sol", "utf8");
 const compiledInput = solc.compile(input);
 ... 
});

但是,當呼叫此函式時,出現以下錯誤

ParserError: Source \"node_modules/@openzeppelin/contracts/token/ERC721/ERC721.sol\" not found: File not supplied initially.\nimport \"./node_modules/@openzeppelin/contracts/token/ERC721/ERC721.sol\";

這是我的契約

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./node_modules/@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Base is ERC721 {
   constructor() ERC721("Base", "BAS") {
   }

   function mint(
     address _to,
     uint256 _tokenId
   )
     external
   {
     _safeMint(_to, _tokenId);
   }
}

我已經使用 將@openzeppelin 安裝到 node_modules 中yarn add @openzeppelin/contracts,相關文件位於 node_modules 文件夾中。我知道 solc 無法通過網路獲取,但文件是在本地安裝的,所以這不應該是問題。

我可以做些什麼來解決這裡問題?

我遇到了這個錯誤,因為我沒有指定 solc 的所有相關來源(版本 ^0.8.0)。我通過編寫一個遞歸函式來解決這個問題,該函式以正確的格式編譯所有依賴項。

const fs = require("fs");
const solc = require("solc");

// returns a contract object compiled using solc
// baseContractPath: relative path of the base contract, i.e. "./BaseContract.sol"
const instantiateContract = (baseContractPath) => {
 const sources = {};
 compileImports(baseContractPath, sources);

 var input = {
   language: "Solidity",
   sources: sources,
   settings: {
     outputSelection: {
       "*": {
         "*": ["*"],
       },
     },
   },
 };

 const output = solc.compile(JSON.stringify(input));
 const contract = JSON.parse(output);
 const bytecode =
   "0x" + contract.contracts[baseContractPath]["Base"].evm.bytecode.object;
 const abi = contract.contracts[baseContractPath]["Base"].abi;
 return {
   bytecode: bytecode,
   abi: abi,
 };
};

// returns sources: { "Contract.sol": { content: fs.readFileSync("pathName.sol",utf8)...}}
// using recursion
const compileImports = (root, sources) => {
 sources[root] = { content: fs.readFileSync(root, "utf8") };
 const imports = getNeededImports(root);
 for (let i = 0; i < imports.length; i++) {
   compileImports(imports[i], sources);
 }
};

// returns all the import paths in absolute path
const getNeededImports = (path) => {
 const file = fs.readFileSync(path, "utf8");
 const files = new Array();
 file
   .toString()
   .split("\n")
   .forEach(function (line, index, arr) {
     if (
       (index === arr.length - 1 && line === "") ||
       !line.trim().startsWith("import")
     ) {
       return;
     }
     // the import is legit
     const relativePath = line.substring(8, line.length - 2);
     const fullPath = buildFullPath(path, relativePath);
     files.push(fullPath);
   });
 return files;
};

// parent: node_modules/.../ERC721/ERC721.sol
// returns absolute path of a relative one using the parent path
const buildFullPath = (parent, path) => {
 let curDir = parent.substr(0, parent.lastIndexOf("/")); //i.e. ./node/.../ERC721
 if (path.startsWith("./")) {
   return curDir + "/" + path.substr(2);
 }

 while (path.startsWith("../")) {
   curDir = curDir.substr(0, curDir.lastIndexOf("/"));
   path = path.substr(3);
 }

 return curDir + "/" + path;
};

module.exports = {
 instantiateContract,
};

--allow-path作為 solc 中的一項安全措施,您不能編譯來自您所在目錄的父文件夾或相鄰文件夾的合約。您可以使用如下方式逐一添加要允許編譯的路徑:

--allow-path "/path/to/folderOne" --allow-path "/path/to/folderTwo"

更好的選擇是使用truffle compile,建構一個新的 truffle 項目或將您的契約添加到現有項目並使用該truffle compile命令,它將自動添加路徑,沒有任何麻煩。

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