使用我的feess后端,我想以编程方式编译用户在我的nextjs前端中输入的Solidity智能合约。
由于我使用的是 TypeScript 并且它包含自动编译器版本识别等高级功能,因此我使用的是 ConsenSys 的 solc-typed-ast 包。
编译(看起来)有效,但返回的字节码与我使用 Remix 手动编译合约时收到的字节码略有不同。当我在前端收到结果并尝试在以太坊上部署字节码时,交易似乎已完成,但在浏览器中查看交易已失败,状态为:
Warning! Error encountered during contract execution [invalid opcode: opcode 0x5f not defined]
这个错误的原因可能是什么(但是,看看字节码“几乎正确”的编译?这是我的后端服务进行编译:
import { Application } from '../../declarations';
import { compileSol } from 'solc-typed-ast';
/**
* A mapping from several Smart Contracts' Name to their Solidity Code or ABI
* @typedef {Object} CodeMap
* @property {string} key - The name of the Smart Contract
* @property {string} value - The Solidity Code or ABI of the Smart Contract
* @example
* {
* "Contract1": "contract Contract1 { ... }",
* "Contract2": "contract Contract2 { ... }",
* }
*/
export type CodeMap = { [key: string]: string };
/**
* Solidity Compiler Service
* This service compiles the Solidity code for a given Project using the solc-typed-ast library.
*/
export class SolidityCompiler {
...
async find(params?: Params | undefined): Promise<CompilationResult[]> {
...
// Fetch the code from the database
const codeToCompile = JSON.parse(template.code) as CodeMap;
// Initialize the compileResults array
const compileResults: CompilationResult[] = [];
try {
// Iterate over each contract in the codeToCompile map
for (const [contractName, contractCode] of Object.entries(codeToCompile)) {
// Create a temporary directory and file path for each contract
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'solidity-'));
const filePath = path.join(tempDir, `${contractName}.sol`);
// Write the contract's Solidity code to a file
fs.writeFileSync(filePath, contractCode);
try {
// Compile the Solidity code
const result = await compileSol(filePath, 'auto');
// Extract the compiled contract names
const compiledContractNames = Object.keys(result.data.contracts[filePath]);
// Iterate through compiled contracts
compiledContractNames.forEach((compiledContractName) => {
const compiledContract = result.data.contracts[filePath][compiledContractName];
// Store the result together with the compiled contract name
compileResults.push({
contractName: compiledContractName,
success: true,
abi: compiledContract.abi,
bytecode: compiledContract.evm.bytecode.object,
error: undefined,
compilerVersion: result.compilerVersion,
});
console.log(`Solidity Compiler Service: ${compiledContractName} compiled successfully`);
});
} catch (compileError: any) {
// Store possible errors together with the contract name
compileResults.push({
contractName,
success: false,
abi: undefined,
bytecode: undefined,
error: compileError.message,
});
console.error(`Solidity Compiler Service: ${contractName}`, compileError.message);
}
// Clean up the temporary file and directory
fs.unlinkSync(filePath);
fs.rmdirSync(tempDir);
}
} catch (error: any) {
throw new Error(`Solidity Compilation Error: ${error.message}`);
}
return compileResults;
}
}
template.code
来自我的数据库,看起来像这样:
"{\"ERC20\":\"pragma solidity ^0.5.0;\\n\\n// Flat ERC20\\n// Updated Jun 7 2019\\n\\n/**\\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\\n * checks.\\n *\\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\\n * in bugs, because programmers usually assume that an overflow raises an\\n * error, which is the standard behavior in high level programming languages.\\n *
安全数学restores this intuition by reverting the transaction when an\\n * operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n */\\nlibrary SafeMath {\\n /**\\n * @dev Returns the addition of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's
+
operator.\\n *\\n * Requirements:\\n * - Addition cannot overflow.\\n */\\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\\n uint256 c = a + b;\\n require(c >= a, \\\"SafeMath: addition overflow\\\");\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's
-
operator.\\n *\\n * Requirements:\\n * - Subtraction cannot overflow.\\n */\\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\\n require(b <= a, \\\"SafeMath: subtraction overflow\\\");\\n uint256 c = a - b;\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's
*
operator.\\n *\\n * Requirements:\\n * - Multiplication cannot overflow.\\n */\\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\\n // benefit is lost if 'b' is also tested.\\n