Rust 部署调用solidity智能合约

前言

最近在学习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

16766248129915.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容