前言
最近在学习Polkadot底层的substrate框架及moonbeam兼容以太坊的智能合约平台,为了更好的学习熟练Rust的语法编程,整理个Rust以太坊部署调用solidity智能合约的代码。当然正常项目开发还是使用Truffle、Hardhat等框架部署调用测试方便,偶尔使用Java写的单元测试对一些常见的标准合约调用也比rust方便😂
准备
1、使用自己的节点或者使用第三方的公共节点提供RPC地址
2、以太坊地址
3、这里使用BSC币安智能链测试网:https://bsc-testnet.public.blastapi.io
使用Cargo创建项目
cargo new deploy-contract
cd deploy-contract
请自行选用文本编辑器打开 Cargo.toml 文件。需要安装ethers.rs库等。
[package]
name = "deploy-contract"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ethers = "1.0.2"
ethers-solc = "1.0.2"
tokio = { version = "1", features = ["full"] }
serde_json = "1.0.89"
serde = "1.0.149"
项目目录结构
├── Cargo.lock
├── Cargo.toml
├── target
├── src
├── Incrementer.sol
└── main.rs
└── Incrementer_ABI.json
代码示例
main.rs
use ethers::prelude::*;
use ethers::providers::{Http, Provider};
use ethers_solc::Solc;
use std::{path::Path, sync::Arc};
type Client = SignerMiddleware<Provider<Http>, Wallet<k256::ecdsa::SigningKey>>;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let provider: Provider<Http> = Provider::<Http>::try_from("https://data-seed-prebsc-1-s1.binance.org:8545")?; // 替换你的RPC地址 http://127.0.0.1:8545
// 配置私钥,去除“0x”前缀
let wallet: LocalWallet = "a58ac7ea54b74896c6a3129370dea99140c42b6908dff628f9062b9d40da4b7e"
.parse::<LocalWallet>()?;
let client = SignerMiddleware::new(provider.clone(), wallet.clone());
// 部署合约
let addr = compile_deploy_contract(&client).await?;
//读取合约中的number值
read_number(&client, &addr).await?;
// 调用合约增加number值方法
increment_number(&client, &addr).await?;
read_number(&client, &addr).await?;
// 重置合约中的number值
reset(&client, &addr).await?;
read_number(&client, &addr).await?;
Ok(())
}
async fn compile_deploy_contract(client: &Client) -> Result<H160, Box<dyn std::error::Error>> {
// Incrementer.sol source的变量作为托管所有需要编译的智能合约的目录路径,该目录为根目录
let source = Path::new(&env!("CARGO_MANIFEST_DIR")); //设置环境变量,Cargo.toml 文件所在的文件夹路径
// let source = Path::new("/Users/smartstrategy-ios01/IT/Rust/rust-web3/deploy-contract");
println!("source:{}", source.display());
// 编译合约 需要先安装solc-select工具编译所有的智能合约:https://github.com/crytic/solc-select
let compiled = Solc::default()
.compile_source(source)
.expect("Could not compile contracts");
// println!("compiled:{:#?}",compiled);
// 获取合约的abi和Bytecode文件
let (abi, bytecode, _runtime_bytecode) = compiled
.find("Incrementer")
.expect("could not find contract")
.into_parts_or_default();
// let contract = compiled
// .get(
// "/Users/smartstrategy-ios01/IT/Rust/rust-web3/deploy-contract/src/Incrementer.sol",
// "Incrementer",
// )
// .expect("could not find contract");
// println!("abi:{:#?}",abi);
// println!("bytecode:{:#?}",bytecode);
// println!("_runtime_bytecode:{:#?}",_runtime_bytecode);
// 创建将用于部署合约的工厂实例
let factory = ContractFactory::new(abi, bytecode, Arc::new(client.clone()));
// let factory = ContractFactory::new(
// contract.abi.unwrap().clone(),
// contract.bytecode().unwrap().clone(),
// Arc::new(client.clone()),
// );
// let contract = factory
// .deploy(U256::from(5))?
// .confirmations(0usize)
// .legacy()
// .send()
// .await?;
// 部署合约 初始构造参数100
let contract = factory.deploy(U256::from(100))?.legacy().send().await?;
let addr = contract.address(); //返回部署合约后地址
println!("Incrementer.sol has been deployed to {:?}", addr);
Ok(addr)
}
// 为Incrementer智能合约生成类型安全接口
abigen!(
Incrementer,
"./Incrementer_ABI.json",
event_derives(serde::Deserialize, serde::Serialize)
);
async fn read_number(
client: &Client,
contract_addr: &H160,
) -> Result<U256, Box<dyn std::error::Error>> {
// 创建合同实例
let contract = Incrementer::new(contract_addr.clone(), Arc::new(client.clone()));
// 调用合约中的number方法,查询值
let value = contract.number().call().await?;
println!("Incrementer's number is {}", value);
Ok(value)
}
async fn increment_number(
client: &Client,
contract_addr: &H160,
) -> Result<(), Box<dyn std::error::Error>> {
println!("Incrementing number...");
// 创建合同实例
let contract = Incrementer::new(contract_addr.clone(), Arc::new(client.clone()));
// 调用合约increment方法,增加number值
let tx = contract.increment(U256::from(100)).legacy().send().await?.await?;
println!("Transaction Receipt: {}", serde_json::to_string(&tx)?);
Ok(())
}
async fn reset(client: &Client, contract_addr: &H160) -> Result<(), Box<dyn std::error::Error>> {
println!("Resetting number...");
// 创建合同实例
let contract = Incrementer::new(contract_addr.clone(), Arc::new(client.clone()));
// 调用合约reset方法,重置number值
let tx = contract.reset().legacy().send().await?.await?;
println!("Transaction Receipt: {}", serde_json::to_string(&tx)?);
Ok(())
}
Incrementer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Incrementer {
uint256 public number;
constructor(uint256 _initialNumber) {
number = _initialNumber;
}
function increment(uint256 _value) public {
number = number + _value;
}
function reset() public {
number = 0;
}
}
Incrementer_ABI.json
[
{
"inputs": [
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "increment",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "number",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "reset",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
构建并运行 Cargo 项目
使用 cargo run 一步构建并运行项目。
cargo run
控制台输出内容
AY:deploy-contract smartstrategy-ios01$ cargo run
Compiling deploy-contract v0.1.0 (/Users/smartstrategy-ios01/IT/Rust/rust-web3/deploy-contract)
Finished dev [unoptimized + debuginfo] target(s) in 9.54s
Running `target/debug/deploy-contract`
source:/Users/smartstrategy-ios01/IT/Rust/rust-web3/deploy-contract
Incrementer.sol has been deployed to 0x069678ed1155adaf5acb44ad45602fa3ebd7dd78
Incrementer's number is 100
Incrementing number...
Transaction Receipt: {"transactionHash":"0xe6afff8ec5e4e2bdc38ce579ebff1b148f3000953da6af92e0f1215334c1860b","transactionIndex":"0x6","blockHash":"0x3888b24555e43fcb400535985dc9c0b900a7aa9b273f764c00dbc053eac75f8d","blockNumber":"0x1a0c789","from":"0x9295b5ca66cc2d8abfa9024f482038a2d5ff7eaf","to":"0x069678ed1155adaf5acb44ad45602fa3ebd7dd78","cumulativeGasUsed":"0xa33a8","gasUsed":"0x6bc6","contractAddress":null,"logs":[],"status":"0x1","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","type":"0x0","effectiveGasPrice":"0x2540be400"}
Incrementer's number is 200
Resetting number...
Transaction Receipt: {"transactionHash":"0xbe05fcfef36f760a2dbf0979d84b264d4248976d06a96ed934a29c7940f55dff","transactionIndex":"0x7","blockHash":"0x720670bedd5c2c145ecc37a3a6a8fa31a0fa27ce24e1e21f2c5fc7a6f65689bf","blockNumber":"0x1a0c78d","from":"0x9295b5ca66cc2d8abfa9024f482038a2d5ff7eaf","to":"0x069678ed1155adaf5acb44ad45602fa3ebd7dd78","cumulativeGasUsed":"0x12c694","gasUsed":"0x3342","contractAddress":null,"logs":[],"status":"0x1","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","type":"0x0","effectiveGasPrice":"0x2540be400"}
Incrementer's number is 0
区块浏览器
hash:https://testnet.bscscan.com/tx/0x23b1bf144342b5cfcb7ede10e87b90185f499eec4af6877afee7d956e4036145