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);
}
代码说明
- Provider初始化 - 使用
SequencerGatewayProvider
连接到StarkNet测试网 - 合约工厂 -
ContractFactory
用于部署新合约,需要合约字节码和ABI - 合约部署 - 调用
deploy
方法并传递构造函数参数 - 合约交互 - 使用
Contract
实例与已部署合约交互 - 方法调用 - 通过
call
方法执行合约函数调用
测试合约
#[cfg(test)]
mod tests {
use super::*;
use starknet_mock::MockProvider;
#[tokio::test]
async fn test_contract_interaction() {
let provider = MockProvider::new();
// 测试部署和调用逻辑...
}
}
项目信息
- 版本: 0.15.0
- 许可证: MIT OR Apache-2.0
- 主页: starknet.rs
- 仓库: github.com/xJonathanLEI/starknet-rs
这个库提供了完整的工具链来开发、部署和测试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网络交互的便捷接口。
主要功能
- 部署Cairo智能合约到StarkNet网络
- 与已部署合约进行交互(调用和查询)
- 测试StarkNet智能合约
- 支持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);
}
}
注意事项
- 使用前需要确保Cairo合约已正确编译
- 主网操作需要真实STARK代币支付gas费
- 测试网合约地址和主网不同
- 大额交易或重要操作建议先测试再上主网
这个库为Rust开发者提供了与StarkNet交互的便捷方式,使得在Rust生态中开发StarkNet智能合约变得更加容易。