Rust StarkNet智能合约开发库starknet-contract的使用,支持Cairo合约的部署、交互与测试

Rust StarkNet智能合约开发库starknet-contract的使用,支持Cairo合约的部署、交互与测试

安装

在项目目录中运行以下Cargo命令:

cargo add starknet-contract

或者在Cargo.toml中添加以下行:

starknet-contract = "0.15.0"

智能合约部署和交互的类型

starknet-contract库提供了用于部署和与Cairo智能合约交互的类型支持。下面是一个完整的示例展示如何使用该库部署、交互和测试Cairo合约。

完整示例代码

use starknet_contract::{Contract, ContractFactory};
use starknet_core::types::{FieldElement, FunctionCall};
use starknet_providers::{Provider, SequencerGatewayProvider};

#[tokio::main]
async fn main() {
    // 初始化Provider
    let provider = SequencerGatewayProvider::starknet_alpha_goerli();
    
    // 合约ABI和字节码
    let contract_abi = include_str!("path/to/contract_abi.json");
    let contract_bytecode = include_bytes!("path/to/contract_bytecode.json");
    
    // 创建合约工厂
    let factory = ContractFactory::new(
        contract_bytecode.to_vec(),
        contract_abi.to_string(),
        provider
    );
    
    // 部署合约
    let deployment = factory
        .deploy(vec![FieldElement::from_hex_be("0x123").unwrap()]) // 构造函数参数
        .await
        .unwrap();
    
    println!("Contract deployed at: {:?}", deployment.address());
    
    // 与合约交互
    let contract = Contract::new(
        deployment.address(),
        contract_abi.to_string(),
        provider
    );
    
    // 调用合约方法
    let result = contract
        .call(
            FunctionCall {
                contract_address: deployment.address(),
                entry_point_selector: FieldElement::from_hex_be("0x456").unwrap(), // 方法选择器
                calldata: vec![FieldElement::from_hex_be("0x789").unwrap()], // 方法参数
            }
        )
        .await
        .unwrap();
    
    println!("Call result: {:?}", result);
}

代码说明

  1. Provider初始化 - 使用SequencerGatewayProvider连接到StarkNet测试网
  2. 合约工厂 - ContractFactory用于部署新合约,需要合约字节码和ABI
  3. 合约部署 - 调用deploy方法并传递构造函数参数
  4. 合约交互 - 使用Contract实例与已部署合约交互
  5. 方法调用 - 通过call方法执行合约函数调用

测试合约

#[cfg(test)]
mod tests {
    use super::*;
    use starknet_mock::MockProvider;
    
    #[tokio::test]
    async fn test_contract_interaction() {
        let provider = MockProvider::new();
        
        // 测试部署和调用逻辑...
    }
}

项目信息

这个库提供了完整的工具链来开发、部署和测试StarkNet上的Cairo智能合约,是Rust生态中StarkNet开发的重要工具。

完整示例demo

以下是更完整的示例代码,展示如何部署一个简单的计数器合约并与之交互:

use starknet_contract::{Contract, ContractFactory};
use starknet_core::types::{FieldElement, FunctionCall};
use starknet_providers::{Provider, SequencerGatewayProvider};

// 计数器合约的ABI (简化版)
const COUNTER_ABI: &str = r#"
[
    {
        "inputs": [{"name": "initial_count", "type": "felt"}],
        "name": "constructor",
        "outputs": [],
        "type": "constructor"
    },
    {
        "inputs": [],
        "name": "increment",
        "outputs": [],
        "type": "function"
    },
    {
        "inputs": [],
        "name": "get_count",
        "outputs": [{"name": "count", "type": "felt"}],
        "type": "function"
    }
]
"#;

#[tokio::main]
async fn main() {
    // 1. 初始化Provider - 连接到StarkNet测试网
    let provider = SequencerGatewayProvider::starknet_alpha_goerli();
    
    // 2. 准备合约字节码 (实际项目中应该从文件加载)
    let contract_bytecode = vec![
        FieldElement::from_hex_be("0x123456").unwrap(), // 示例字节码
        // 这里应该是实际的合约字节码
    ];
    
    // 3. 创建合约工厂
    let factory = ContractFactory::new(
        contract_bytecode,
        COUNTER_ABI.to_string(),
        provider.clone()
    );
    
    // 4. 部署合约 - 初始计数器值为10
    let initial_count = FieldElement::from(10u64);
    let deployment = factory
        .deploy(vec![initial_count]) // 传递构造函数参数
        .await
        .expect("Failed to deploy contract");
    
    println!("Contract deployed at: 0x{:x}", deployment.address());
    
    // 5. 创建合约实例
    let contract = Contract::new(
        deployment.address(),
        COUNTER_ABI.to_string(),
        provider
    );
    
    // 6. 调用increment方法
    let increment_result = contract
        .call(
            FunctionCall {
                contract_address: deployment.address(),
                entry_point_selector: FieldElement::from_hex_be(
                    "0x1e1d184bb1b1ae1e3a73e8a1e5a6e2a0e1a3e2a8e1a1e1a7e2a8e1a1e1a7" // increment方法选择器
                ).unwrap(),
                calldata: vec![], // 无参数
            }
        )
        .await
        .expect("Failed to call increment");
    
    println!("Increment transaction: {:?}", increment_result);
    
    // 7. 调用get_count方法
    let count_result = contract
        .call(
            FunctionCall {
                contract_address: deployment.address(),
                entry_point_selector: FieldElement::from_hex_be(
                    "0x2e2d284cc2c2ce2e3b73e8b2e6b3e1a1e2b3e2a8e2b2e2b7e3a8e2b2e2b7" // get_count方法选择器
                ).unwrap(),
                calldata: vec![], // 无参数
            }
        )
        .await
        .expect("Failed to call get_count");
    
    println!("Current count: {:?}", count_result);
}

测试用例示例

#[cfg(test)]
mod tests {
    use super::*;
    use starknet_mock::MockProvider;
    use starknet_core::types::MaybeUnknownErrorCode;
    
    #[tokio::test]
    async fn test_counter_contract() {
        // 1. 创建模拟Provider
        let provider = MockProvider::new();
        
        // 2. 准备测试用的合约字节码
        let bytecode = vec![FieldElement::from(1u64)];
        
        // 3. 创建合约工厂
        let factory = ContractFactory::new(
            bytecode,
            COUNTER_ABI.to_string(),
            provider.clone()
        );
        
        // 4. 测试部署
        let deployment = factory
            .deploy(vec![FieldElement::from(5u64)]) // 初始值5
            .await
            .unwrap();
        
        // 5. 创建合约实例
        let contract = Contract::new(
            deployment.address(),
            COUNTER_ABI.to_string(),
            provider
        );
        
        // 6. 测试方法调用可以在这里添加...
    }
}

1 回复

Rust StarkNet智能合约开发库starknet-contract使用指南

starknet-contract是一个用于StarkNet智能合约开发的Rust库,支持Cairo合约的部署、交互与测试。它为开发者提供了与StarkNet网络交互的便捷接口。

主要功能

  1. 部署Cairo智能合约到StarkNet网络
  2. 与已部署合约进行交互(调用和查询)
  3. 测试StarkNet智能合约
  4. 支持StarkNet主网和测试网

安装方法

在Cargo.toml中添加依赖:

[dependencies]
starknet-contract = "0.1.0"
tokio = { version = "1.0", features = ["full"] }

基本使用方法

1. 初始化合约客户端

use starknet_contract::Contract;
use starknet_providers::{Provider, SequencerGatewayProvider};

#[tokio::main]
async fn main() {
    // 使用Goerli测试网提供者
    let provider = SequencerGatewayProvider::starknet_alpha_goerli();
    // 合约地址
    let contract_address = "0x123...".parse().unwrap();
    // 创建合约实例
    let contract = Contract::new(contract_address, provider);
}

2. 部署合约

use starknet_contract::ContractFactory;
use starknet_providers::SequencerGatewayProvider;

#[tokio::main]
async fn main() {
    // 初始化提供者
    let provider = SequencerGatewayProvider::starknet_alpha_goerli();
    // 创建合约工厂
    let contract_factory = ContractFactory::new(
        "path/to/compiled_contract.json",  // 编译后的合约路径
        provider
    );
    
    // 部署合约,传入构造函数参数
    let deployment = contract_factory
        .deploy(vec!["0x123".into()]) 
        .await
        .unwrap();
    
    println!("合约部署地址: {}", deployment.address);
}

3. 调用合约函数

use starknet_contract::Contract;
use starknet_providers::SequencerGatewayProvider;

#[tokio::main]
async fn main() {
    let provider = SequencerGatewayProvider::starknet_alpha_goerli();
    let contract_address = "0x123...".parse().unwrap();
    let contract = Contract::new(contract_address, provider);
    
    // 调用view函数(只读)
    let result = contract
        .call("get_balance", vec![])  // 函数名和参数
        .await
        .unwrap();
    
    println!("余额: {}", result[0]);
    
    // 调用状态修改函数
    let tx_hash = contract
        .invoke("transfer", vec!["0x456".into(), "1000".into()])  // 转账目标和金额
        .await
        .unwrap();
    
    println!("交易哈希: {}", tx_hash);
}

4. 测试合约

use starknet_contract::test_utils::{MockProvider, MockContract};
use starknet_contract::Contract;

#[tokio::test]
async fn test_contract_interaction() {
    // 创建mock提供者和合约
    let mock_provider = MockProvider::new();
    let mock_contract = MockContract::new("path/to/contract_abi.json");
    
    // 添加mock合约到提供者
    mock_provider.add_contract(mock_contract.address(), mock_contract);
    
    // 创建测试合约实例
    let contract = Contract::new(mock_contract.address(), mock_provider);
    
    // 设置mock响应
    mock_contract
        .expect_call("get_balance", vec![])
        .returns(vec!["1000".into()]);
    
    // 测试调用
    let balance = contract.call("get_balance", vec![]).await.unwrap();
    assert_eq!(balance[0], "1000");
}

高级功能

事件监听

use starknet_contract::Contract;
use starknet_providers::SequencerGatewayProvider;

#[tokio::main]
async fn main() {
    let provider = SequencerGatewayProvider::starknet_alpha_goerli();
    let contract_address = "0x123...".parse().unwrap();
    let contract = Contract::new(contract_address, provider);
    
    // 查询特定区块范围内的事件
    let events = contract
        .events()
        .from_block(12345)  // 起始区块
        .to_block(12350)    // 结束区块
        .get_events()
        .await
        .unwrap();
    
    // 打印事件
    for event in events {
        println!("事件: {:?}", event);
    }
}

批量交易

use starknet_contract::Contract;
use starknet_providers::SequencerGatewayProvider;

#[tokio::main]
async fn main() {
    let provider = SequencerGatewayProvider::starknet_alpha_goerli();
    let contract_address = "0x123...".parse().unwrap();
    let contract = Contract::new(contract_address, provider);
    
    // 创建批量交易
    let batch = contract
        .batch()
        .add_call("approve", vec!["0x456".into(), "1000".into()])  // 授权
        .add_call("transfer", vec!["0x789".into(), "500".into()]);  // 转账
    
    // 发送批量交易
    let tx_hashes = batch.send().await.unwrap();
    
    // 打印交易哈希
    for tx_hash in tx_hashes {
        println!("已执行交易: {}", tx_hash);
    }
}

完整示例

下面是一个完整的StarkNet合约交互示例,展示了从部署到调用的全过程:

use starknet_contract::{Contract, ContractFactory};
use starknet_providers::SequencerGatewayProvider;
use std::sync::Arc;

#[tokio::main]
async fn main() {
    // 1. 初始化提供者
    let provider = Arc::new(SequencerGatewayProvider::starknet_alpha_goerli());
    
    // 2. 部署合约
    let factory = ContractFactory::new(
        "artifacts/my_contract.json",  // 编译后的合约
        provider.clone()
    );
    
    println!("正在部署合约...");
    let deployment = factory
        .deploy(vec!["0x1".into()])  // 构造函数参数
        .await
        .unwrap();
    
    println!("合约已部署在地址: {}", deployment.address);
    
    // 3. 与合约交互
    let contract = Contract::new(deployment.address, provider.clone());
    
    // 调用view函数
    println!("查询合约状态...");
    let balance = contract
        .call("get_balance", vec![])
        .await
        .unwrap();
    println!("当前余额: {}", balance[0]);
    
    // 调用状态修改函数
    println!("执行转账操作...");
    let tx_hash = contract
        .invoke("transfer", vec!["0xabc".into(), "100".into()])
        .await
        .unwrap();
    println!("交易已提交,哈希: {}", tx_hash);
    
    // 4. 监听事件
    println!("监听合约事件...");
    let events = contract
        .events()
        .from_block("latest")
        .get_events()
        .await
        .unwrap();
    
    for event in events {
        println!("捕获到事件: {:?}", event);
    }
}

注意事项

  1. 使用前需要确保Cairo合约已正确编译
  2. 主网操作需要真实STARK代币支付gas费
  3. 测试网合约地址和主网不同
  4. 大额交易或重要操作建议先测试再上主网

这个库为Rust开发者提供了与StarkNet交互的便捷方式,使得在Rust生态中开发StarkNet智能合约变得更加容易。

回到顶部