编译合约

命令行编译器工具

Vyper 包含以下用于编译合约的命令行脚本

  • vyper: 将 vyper 合约文件编译成 IR 或字节码

  • vyper-json: 为编译器提供 JSON 接口

注意

使用 --help 标志可以详细了解如何使用这些脚本中的每一个。

vyper

vyper 提供对编译器的命令行访问。它可以生成各种输出,包括简单的二进制文件、AST、接口和源映射。

编译合约

$ vyper yourFileName.vy

使用 -f 标志指定要返回的输出格式。使用 vyper --help 查看完整输出选项列表。

$ vyper -f abi,bytecode,bytecode_runtime,ir,asm,source_map,method_identifiers yourFileName.vy

使用 -p 标志可以设置一个根路径,该路径在搜索要导入的接口文件时使用。如果没有给出,它将默认为当前工作目录。有关更多信息,请参见 搜索接口文件

$ vyper -p yourProject yourProject/yourFileName.vy

存储布局

显示合约的默认存储布局

$ vyper -f layout yourFileName.vy

这将输出一个 JSON 对象,详细说明了由编译器确定的所有状态变量的位置。

覆盖合约的默认存储布局

$ vyper --storage-layout-file storageLayout.json yourFileName.vy

使用 --storage-layout-file 标志的输入必须与 vyper -f layout 命令的 .storage_layout 字段的格式匹配。

vyper-json

vyper-json 为编译器提供 JSON 接口。它需要一个 JSON 格式的输入,并以 JSON 格式的输出 返回编译结果。

通过 stdin 编译 JSON

$ vyper-json

从 JSON 文件编译

$ vyper-json yourProject.json

默认情况下,输出将发送到 stdout。要重定向到文件,请使用 -o 标志

$ vyper-json -o compiled.json

导入接口

vyper-json 按以下顺序搜索导入的接口

  1. 输入 JSON 的 interfaces 字段中定义的接口。

  2. 从输入 JSON 的 sources 字段中的合约生成的派生接口。

  3. (可选) 本地文件系统,如果通过 -p 标志明确声明了根路径。

有关 Vyper 导入系统的更多信息,请参见 搜索接口文件

在线编译器

尝试 VyperLang!

尝试 VyperLang! 是一个由 Vyper 团队托管的 JupterHub 实例,作为在 Vyper 中开发和测试合约的沙箱。它需要 github 进行登录,并支持通过浏览器进行部署。

Remix IDE

Remix IDE 是一个编译器和 JavaScript VM,用于在 Vyper 和 Solidity 中开发和测试合约。

注意

虽然 Remix IDE 编译器的 Vyper 版本会定期更新,但它可能略微落后于存储库 master 分支中的最新版本。确保字节码与本地编译器的输出匹配。

编译器优化模式

vyper CLI 工具接受优化模式 "none""codesize""gas"(默认)。可以使用 --optimize 标志设置它。例如,调用 vyper --optimize codesize MyContract.vy 将编译合约,并优化代码大小。作为对气体和代码大小模式之间差异的粗略总结,在气体优化模式下,编译器将尝试生成尽可能减少气体的字节码(在一定程度上),包括

  • 使用稀疏选择器表,该表优化气体而不是代码大小

  • 内联一些常量,以及

  • 尝试展开一些循环,特别是对于数据复制。

在代码大小优化模式下,编译器将努力通过以下方式最小化代码大小

  • 使用密集选择器表

  • 外联代码,以及

  • 使用更多循环进行数据复制。

设置目标 EVM 版本

编译合约代码时,可以指定要编译的目标以太坊虚拟机版本,以访问或避免特定功能。可以使用源代码 pragma 或编译器选项指定版本。建议在需要灵活性时(例如,跨不同链轻松部署)使用编译器选项,而在需要字节码可重复性时(例如,在区块浏览器上验证代码)使用源代码 pragma。

注意

如果编译器选项指定的 evm 版本与源代码 pragma 冲突,则将引发异常,编译将不会继续。

例如,将以下 pragma 添加到合约中表示它应该为 EVM 的“上海”分叉编译。

#pragma evm-version shanghai

警告

为错误的 EVM 版本编译会导致错误、奇怪或失败的行为。请确保,尤其是在运行私有链时,使用匹配的 EVM 版本。

通过 vyper CLI 编译时,可以使用 --evm-version 标志指定 EVM 版本选项

$ vyper --evm-version [VERSION]

使用 JSON 接口时,可以在 "settings" 字段中包含 "evmVersion"

{
    "settings": {
        "evmVersion": "[VERSION]"
    }
}

目标选项

以下列出了支持的 EVM 版本,以及每个版本引入的编译器更改。每个版本之间不保证向后兼容性。

istanbul
  • 可以通过 chain.id 访问 CHAINID 操作码

  • 使用 SELFBALANCE 操作码调用 self.balance

  • 气体估计更改了 SLOADBALANCE

berlin
  • 气体估计更改了 EXTCODESIZEEXTCODECOPYEXTCODEHASHSLOADSSTORECALLCALLCODEDELEGATECALLSTATICCALL

  • 使用 @nonreentrant 标记的函数使用与针对柏林之前的合约不同的值(3 和 2)进行保护。

  • 可以通过 block.basefee 访问 BASEFEE

paris
  • 弃用 block.difficulty,取而代之的是它的新别名 block.prevrandao

shanghai(default)
  • 编译器会自动生成 PUSH0 操作码,而不是 PUSH1 0

cancun(experimental)
  • 使用 transient 关键字可以声明存在于瞬态存储中的变量

  • 使用 @nonreentrant 标记的函数使用 TLOAD/TSTORE 而不是 SLOAD/SSTORE 进行保护

  • 对于大多数内存操作,MCOPY 操作码将由编译器自动生成。

编译器输入和输出 JSON 描述

尤其是在处理复杂或自动化的设置时,推荐使用 vyper-json 和 JSON 输入输出接口进行编译。

在可能的情况下,Vyper JSON 编译器格式遵循 Solidity 的格式。

输入 JSON 描述

以下示例描述了 vyper-json 的预期输入格式。当然,注释是不允许的,这里仅用于解释目的。

{
    // Required: Source code language. Must be set to "Vyper".
    "language": "Vyper",
    // Required
    // Source codes given here will be compiled.
    "sources": {
        "contracts/foo.vy": {
            // Optional: keccak256 hash of the source file
            "keccak256": "0x234...",
            // Required: literal contents of the source file
            "content": "@external\ndef foo() -> bool:\n    return True"
        }
    },
    // Optional
    // Interfaces given here are made available for import by the sources
    // that are compiled. If the suffix is ".vy", the compiler will expect
    // a contract-as-interface using proper Vyper syntax. If the suffix is
    // "abi" the compiler will expect an ABI object.
    "interfaces": {
        "contracts/bar.vy": {
            "content": ""
        },
        "contracts/baz.json": {
            "abi": []
        }
    },
    // Optional
    "settings": {
        "evmVersion": "shanghai",  // EVM version to compile for. Can be istanbul, berlin, paris, shanghai (default) or cancun (experimental!).
        // optional, optimization mode
        // defaults to "gas". can be one of "gas", "codesize", "none",
        // false  and true (the last two are for backwards compatibility).
        "optimize": "gas",
        // optional, whether or not the bytecode should include Vyper's signature
        // defaults to true
        "bytecodeMetadata": true,
        // The following is used to select desired outputs based on file names.
        // File names are given as keys, a star as a file name matches all files.
        // Outputs can also follow the Solidity format where second level keys
        // denoting contract names - all 2nd level outputs are applied to the file.
        //
        // To select all possible compiler outputs: "outputSelection: { '*': ["*"] }"
        // Note that this might slow down the compilation process needlessly.
        //
        // The available output types are as follows:
        //
        //    abi - The contract ABI
        //    ast - Abstract syntax tree
        //    interface - Derived interface of the contract, in proper Vyper syntax
        //    ir - intermediate representation of the code
        //    userdoc - Natspec user documentation
        //    devdoc - Natspec developer documentation
        //    evm.bytecode.object - Bytecode object
        //    evm.bytecode.opcodes - Opcodes list
        //    evm.deployedBytecode.object - Deployed bytecode object
        //    evm.deployedBytecode.opcodes - Deployed opcodes list
        //    evm.deployedBytecode.sourceMap - Deployed source mapping (useful for debugging)
        //    evm.methodIdentifiers - The list of function hashes
        //
        // Using `evm`, `evm.bytecode`, etc. will select every target part of that output.
        // Additionally, `*` can be used as a wildcard to request everything.
        //
        "outputSelection": {
            "*": ["evm.bytecode", "abi"],  // Enable the abi and bytecode outputs for every single contract
            "contracts/foo.vy": ["ast"]  // Enable the ast output for contracts/foo.vy
        }
    }
}

输出 JSON 描述

以下示例描述了 vyper-json 的输出格式。当然,注释是不允许的,这里仅用于解释目的。

{
    // The compiler version used to generate the JSON
    "compiler": "vyper-0.1.0b12",
    // Optional: not present if no errors/warnings were encountered
    "errors": [
        {
        // Optional: Location within the source file.
        "sourceLocation": {
            "file": "source_file.vy",
            "lineno": 5,
            "col_offset": 11
        },
        // Mandatory: Exception type, such as "JSONError", "StructureException", etc.
        "type": "TypeMismatch",
        // Mandatory: Component where the error originated, such as "json", "compiler", "vyper", etc.
        "component": "compiler",
        // Mandatory ("error" or "warning")
        "severity": "error",
        // Mandatory
        "message": "Unsupported type conversion: int128 to bool"
        // Optional: the message formatted with source location
        "formattedMessage": "line 5:11 Unsupported type conversion: int128 to bool"
        }
    ],
    // This contains the file-level outputs. Can be limited/filtered by the outputSelection settings.
    "sources": {
        "source_file.vy": {
            // Identifier of the source (used in source maps)
            "id": 0,
            // The AST object
            "ast": {},
        }
    },
    // This contains the contract-level outputs. Can be limited/filtered by the outputSelection settings.
    "contracts": {
        "source_file.vy": {
            // The contract name will always be the file name without a suffix
            "source_file": {
                // The Ethereum Contract ABI.
                // See https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
                "abi": [],
                // Natspec developer documentation
                "devdoc": {},
                // Intermediate representation (string)
                "ir": "",
                // Natspec developer documentation
                "userdoc": {},
                // EVM-related outputs
                "evm": {
                    "bytecode": {
                        // The bytecode as a hex string.
                        "object": "00fe",
                        // Opcodes list (string)
                        "opcodes": ""
                    },
                    "deployedBytecode": {
                        // The deployed bytecode as a hex string.
                        "object": "00fe",
                        // Deployed opcodes list (string)
                        "opcodes": "",
                        // The deployed source mapping as a string.
                        "sourceMap": ""
                    },
                    // The list of function hashes
                    "methodIdentifiers": {
                        "delegate(address)": "5c19a95c"
                    }
                }
            }
        }
    }
}

错误

每个错误都包含一个 component 字段,指示错误发生的阶段。

  • json: 解析输入 JSON 时发生的错误。通常是由于无效的 JSON 或缺少必需的值导致的。

  • parser: 解析合约时发生的错误。通常是由于无效的 Vyper 语法导致的。

  • compiler: 编译合约时发生的错误。

  • vyper: Vyper 内部发生的意外错误。如果您遇到此类型的错误,请提交问题。

您也可以使用 --traceback 标志,在遇到错误时获得标准的 Python 追溯。