Rust智能合约编译工具foundry-compilers的使用,支持Solidity和Vyper的编译与ABI生成
Rust智能合约编译工具foundry-compilers的使用,支持Solidity和Vyper的编译与ABI生成
概述
Foundry Compilers最初是ethers-rs
的一部分,名为ethers-solc
,现在是Foundry的编译后端。
ethers-rs
的ethers-solc
目前处于维护模式,任何修复也会反映在Foundry Compilers上。开发者目前不需要采取任何行动,但我们强烈建议使用Foundry Compilers而不是ethers-solc
。
安装
要安装,只需将foundry-compilers
添加到你的cargo依赖中:
[dependencies]
foundry-compilers = "0.18.0"
示例用法
以下是使用foundry-compilers编译Solidity/Vyper合约并生成ABI的完整示例:
use foundry_compilers::{Project, ProjectPathsConfig};
use std::path::Path;
fn main() {
// 配置项目路径、solc、缓存等
let project = Project::builder()
.paths(ProjectPathsConfig::hardhat(Path::new(env!("CARGO_MANIFEST_DIR"))?)
.build(Default::default())?;
// 编译项目
let output = project.compile()?;
// 如果源代码文件发生变化,告诉Cargo重新运行此构建脚本
project.rerun_if_sources_changed();
// 处理编译输出
if output.has_compiler_errors() {
eprintln!("Compilation failed:");
for error in output.output().errors {
eprintln!("{}", error);
}
std::process::exit(1);
}
// 获取所有合约的ABI
for (contract_name, contract) in output.contracts().iter() {
println!("Contract: {}", contract_name);
if let Some(abi) = contract.abi {
println!("ABI: {}", abi.to_string());
}
}
Ok(())
}
完整示例demo
以下是一个更完整的示例,展示如何使用foundry-compilers编译Solidity合约并处理编译结果:
use foundry_compilers::{artifacts::output_selection::OutputSelection, Project, ProjectPathsConfig};
use std::{path::Path, error::Error};
fn main() -> Result<(), Box<dyn Error>> {
// 1. 配置项目路径
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
let paths = ProjectPathsConfig::builder()
.root(root)
.sources(root.join("contracts")) // Solidity/Vyper合约目录
.build()?;
// 2. 创建项目配置
let project = Project::builder()
.paths(paths)
.set_compiler(Default::default()) // 使用默认编译器设置
.output_selection(OutputSelection::default()) // 输出选择配置
.build()?;
// 3. 编译项目
let output = project.compile()?;
// 4. 处理编译结果
if output.has_compiler_errors() {
eprintln!("Compilation failed with errors:");
for error in output.output().errors.iter() {
eprintln!("- {}", error);
}
return Err("Compilation failed".into());
}
// 5. 输出编译信息
println!("Compilation successful!");
println!("Contracts compiled: {}", output.contracts().count());
// 6. 遍历所有合约并输出ABI和字节码
for (name, contract) in output.contracts().iter() {
println!("\nContract: {}", name);
if let Some(abi) = &contract.abi {
println!("ABI length: {}", abi.len());
}
if let Some(bytecode) = &contract.bytecode {
println!("Bytecode length: {}", bytecode.len());
}
if let Some(deployed_bytecode) = &contract.deployed_bytecode {
println!("Deployed bytecode length: {}", deployed_bytecode.len());
}
}
Ok(())
}
支持的Rust版本
当前支持的最低Rust版本(MSRV)是1.88。
注意,MSRV不会自动增加,只会在次要版本发布时更新。
贡献
感谢您帮助改进项目!我们很高兴有您的参与!我们有一个贡献指南来帮助您参与Foundry Compilers项目。
除非CI通过,否则不会合并拉取请求,因此请确保您的贡献遵循linting规则并通过clippy。
Rust智能合约编译工具foundry-compilers使用指南
foundry-compilers 是一个用于编译智能合约的Rust库,支持Solidity和Vyper语言的编译,并能生成合约ABI。它是Foundry工具链的一部分,提供了强大的智能合约编译功能。
安装
在Cargo.toml中添加依赖:
[dependencies]
foundry-compilers = "0.2"
基本用法
1. 编译Solidity合约
use foundry_compilers::{CompilerInput, Solc};
async fn compile_solidity() {
// 初始化Solc编译器
let solc = Solc::default();
// 准备合约源代码
let source = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleStorage {
uint256 storedData;
function set(uint256 x) public {
storedData = x;
}
function get() public view returns (uint256) {
return storedData;
}
}
"#;
// 创建编译器输入
let input = CompilerInput::new()
.add_source("SimpleStorage.sol", source);
// 执行编译
let output = solc.compile(&input).unwrap();
// 获取编译结果
if let Some(contract) = output.contracts.get("SimpleStorage.sol:SimpleStorage") {
println!("ABI: {}", contract.abi.as_ref().unwrap());
println!("Bytecode: {}", contract.evm.as_ref().unwrap().bytecode.object);
}
}
2. 编译Vyper合约
use foundry_compilers::{CompilerInput, Vyper};
async fn compile_vyper() {
// 初始化Vyper编译器
let vyper = Vyper::default();
// 准备合约源代码
let source = r#"
# @version ^0.3.3
stored_data: uint256
@external
def set(x: uint256):
self.stored_data = x
@external
@view
def get() -> uint256:
return self.stored_data
"#;
// 创建编译器输入
let input = CompilerInput::new()
.add_source("simple_storage.vy", source);
// 执行编译
let output = vyper.compile(&input).unwrap();
// 获取编译结果
if let Some(contract) = output.contracts.get("simple_storage.vy:simple_storage") {
println!("ABI: {}", contract.abi.as_ref().unwrap());
println!("Bytecode: {}", contract.evm.as_ref().unwrap().bytecode.object);
}
}
高级功能
1. 使用特定版本的编译器
use foundry_compilers::{Solc, SolcVersion};
async fn use_specific_version() {
// 指定Solc版本
let version = SolcVersion::new("0.8.19").unwrap();
let solc = Solc::with_version(version).unwrap();
// ...其余编译代码
}
2. 从文件系统编译多个合约
use foundry_compilers::{Project, ProjectPathsConfig};
async fn compile_project() {
// 配置项目路径
let paths = ProjectPathsConfig::builder()
.root("./contracts")
.sources("./contracts/src")
.build()
.unwrap();
// 创建项目
let project = Project::builder()
.paths(paths)
.build()
.unwrap();
// 编译项目
let compiled = project.compile().unwrap();
// 处理编译结果
for (name, artifact) in compiled.into_artifacts() {
println!("Contract: {}", name);
println!("ABI: {:?}", artifact.abi);
println!("Bytecode: {}", artifact.bytecode);
}
}
3. 生成类型安全的Rust绑定
use foundry_compilers::Artifact;
fn generate_rust_bindings() {
let artifact: Artifact = /* 从编译结果获取 */;
let bindings = artifact.generate_bindings().unwrap();
// 将绑定写入文件
std::fs::write("bindings.rs", bindings.to_string()).unwrap();
}
错误处理
use foundry_compilers::{error::SolcError, Solc};
async fn handle_errors() {
let solc = Solc::default();
let input = CompilerInput::new()
.add_source("Invalid.sol", "invalid solidity code");
match solc.compile(&input) {
Ok(output) => {
// 处理成功情况
}
Err(SolcError::CompilationFailed(errors)) => {
eprintln!("Compilation failed with errors:");
for error in errors {
eprintln!("- {}", error);
}
}
Err(e) => {
eprintln!("Other error occurred: {}", e);
}
}
}
完整示例代码
下面是一个完整的foundry-compilers使用示例,包含Solidity和Vyper合约的编译:
use foundry_compilers::{CompilerInput, Project, ProjectPathsConfig, Solc, Vyper};
use std::path::Path;
#[tokio::main]
async fn main() {
// 示例1: 编译Solidity合约
compile_solidity_contract().await;
// 示例2: 编译Vyper合约
compile_vyper_contract().await;
// 示例3: 编译项目目录中的合约
compile_project_directory().await;
}
async fn compile_solidity_contract() {
println!("=== 编译Solidity合约示例 ===");
let solc = Solc::default();
let source = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleStorage {
uint256 storedData;
function set(uint256 x) public {
storedData = x;
}
function get() public view returns (uint256) {
return storedData;
}
}
"#;
let input = CompilerInput::new()
.add_source("SimpleStorage.sol", source);
let output = solc.compile(&input).unwrap();
if let Some(contract) = output.contracts.get("SimpleStorage.sol:SimpleStorage") {
println!("ABI: {}", contract.abi.as_ref().unwrap());
println!("Bytecode: {}", contract.evm.as_ref().unwrap().bytecode.object);
}
}
async fn compile_vyper_contract() {
println!("\n=== 编译Vyper合约示例 ===");
let vyper = Vyper::default();
let source = r#"
# @version ^0.3.3
stored_data: uint256
@external
def set(x: uint256):
self.stored_data = x
@external
@view
def get() -> uint256:
return self.stored_data
"#;
let input = CompilerInput::new()
.add_source("simple_storage.vy", source);
let output = vyper.compile(&input).unwrap();
if let Some(contract) = output.contracts.get("simple_storage.vy:simple_storage") {
println!("ABI: {}", contract.abi.as_ref().unwrap());
println!("Bytecode: {}", contract.evm.as_ref().unwrap().bytecode.object);
}
}
async fn compile_project_directory() {
println!("\n=== 编译项目目录示例 ===");
// 创建临时目录用于测试
let temp_dir = tempfile::tempdir().unwrap();
let src_dir = temp_dir.path().join("src");
std::fs::create_dir(&src_dir).unwrap();
// 写入测试合约文件
std::fs::write(
src_dir.join("Greeter.sol"),
r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Greeter {
string greeting;
constructor(string memory _greeting) {
greeting = _greeting;
}
function greet() public view returns (string memory) {
return greeting;
}
}
"#,
).unwrap();
// 配置项目路径
let paths = ProjectPathsConfig::builder()
.root(temp_dir.path())
.sources(src_dir)
.build()
.unwrap();
// 创建并编译项目
let project = Project::builder()
.paths(paths)
.build()
.unwrap();
let compiled = project.compile().unwrap();
// 输出编译结果
for (name, artifact) in compiled.into_artifacts() {
println!("\nContract: {}", name);
println!("ABI: {:?}", artifact.abi);
println!("Bytecode: {}", artifact.bytecode);
}
}
总结
foundry-compilers 提供了强大的智能合约编译功能,主要特点包括:
- 支持 Solidity 和 Vyper 语言
- 可指定编译器版本
- 支持项目级别的编译
- 能生成合约ABI和字节码
- 可生成类型安全的Rust绑定
- 良好的错误处理机制
通过这个库,Rust开发者可以方便地将智能合约编译集成到自己的应用中,为区块链开发提供更强大的工具支持。