Rust智能合约编译工具foundry-compilers的使用,支持Solidity和Vyper的编译与ABI生成

Rust智能合约编译工具foundry-compilers的使用,支持Solidity和Vyper的编译与ABI生成

概述

Foundry Compilers最初是ethers-rs的一部分,名为ethers-solc,现在是Foundry的编译后端。

ethers-rsethers-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。


1 回复

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开发者可以方便地将智能合约编译集成到自己的应用中,为区块链开发提供更强大的工具支持。

回到顶部