#ethereum #dfinity #icp #web3 #rpc-client #ecdsa-signature #rpc

bin+lib ic-web3

IC canisters 的 Ethereum JSON-RPC 客户端

7 个版本

0.1.7 2022年11月30日
0.1.6 2022年10月31日
0.1.2 2022年8月24日

2678神奇豆

Download history 16/week @ 2024-03-11 27/week @ 2024-04-01 9/week @ 2024-04-08 24/week @ 2024-04-15 30/week @ 2024-04-22 21/week @ 2024-04-29 21/week @ 2024-05-06 22/week @ 2024-05-13 52/week @ 2024-05-20 59/week @ 2024-05-27 66/week @ 2024-06-03 53/week @ 2024-06-10 103/week @ 2024-06-17 96/week @ 2024-06-24

329 每月下载量

MIT 许可证

540KB
11K SLoC

ic-web3

通过互联网计算机的阈限 ECDSA 签名和出站 http 调用功能,为访问以太坊网络而设计的互联网计算机 canisters 的 RPC 客户端。

Crates.io

特性

  • 在 canisters 内执行对以太坊网络的 RPC 调用
  • 使用 IC 的阈限 ECDSA 签名消息
  • 在 canisters 内向以太坊网络发送交易
  • 在 canisters 内查询/调用以太坊合约

使用方法

将以下内容添加到您的 Cargo.toml

[dependencies]
ic-web3 = { git = "https://github.com/rocklabs-io/ic-web3" }

示例

注意:您应该有 dfx 0.11.2 或更高版本。

请参阅 示例 以获取完整示例。

use candid::candid_method;
use ic_cdk_macros::{self, update};
use std::str::FromStr;

use ic_web3::transports::ICHttp;
use ic_web3::Web3;
use ic_web3::ic::{get_eth_addr, KeyInfo};
use ic_web3::{
    contract::{Contract, Options},
    ethabi::ethereum_types::{U64, U256},
    types::{Address, TransactionParameters, BlockId, BlockNumber, Block},
};

const URL: &str = "<GOERLI-RPC-URL>";
const CHAIN_ID: u64 = 5;
const KEY_NAME: &str = "dfx_test_key";
const TOKEN_ABI: &[u8] = include_bytes!("../src/contract/res/token.json");

type Result<T, E> = std::result::Result<T, E>;

#[update(name = "get_eth_gas_price")]
#[candid_method(update, rename = "get_eth_gas_price")]
async fn get_eth_gas_price() -> Result<String, String> {
    let w3 = match ICHttp::new(URL, None) {
        Ok(v) => { Web3::new(v) },
        Err(e) => { return Err(e.to_string()) },
    };
    let gas_price = w3.eth().gas_price().await.map_err(|e| format!("get gas price failed: {}", e))?;
    ic_cdk::println!("gas price: {}", gas_price);
    Ok(format!("{}", gas_price))
}

// get canister's ethereum address
#[update(name = "get_canister_addr")]
#[candid_method(update, rename = "get_canister_addr")]
async fn get_canister_addr() -> Result<String, String> {
    match get_eth_addr(None, None, KEY_NAME.to_string()).await {
        Ok(addr) => { Ok(hex::encode(addr)) },
        Err(e) => { Err(e) },
    }
}

// send tx to eth
#[update(name = "send_eth")]
#[candid_method(update, rename = "send_eth")]
async fn send_eth(to: String, value: u64) -> Result<String, String> {
    // ecdsa key info
    let derivation_path = vec![ic_cdk::id().as_slice().to_vec()];
    let key_info = KeyInfo{ derivation_path: derivation_path, key_name: KEY_NAME.to_string() };

    // get canister eth address
    let from_addr = get_eth_addr(None, None, KEY_NAME.to_string())
        .await
        .map_err(|e| format!("get canister eth addr failed: {}", e))?;
    // get canister the address tx count
    let w3 = match ICHttp::new(URL, None) {
        Ok(v) => { Web3::new(v) },
        Err(e) => { return Err(e.to_string()) },
    };
    let tx_count = w3.eth()
        .transaction_count(from_addr, None)
        .await
        .map_err(|e| format!("get tx count error: {}", e))?;
        
    ic_cdk::println!("canister eth address {} tx count: {}", hex::encode(from_addr), tx_count);
    // construct a transaction
    let to = Address::from_str(&to).unwrap();
    let tx = TransactionParameters {
        to: Some(to),
        nonce: Some(tx_count), // remember to fetch nonce first
        value: U256::from(value),
        gas_price: Some(U256::exp10(10)), // 10 gwei
        gas: U256::from(21000),
        ..Default::default()
    };
    // sign the transaction and get serialized transaction + signature
    let signed_tx = w3.accounts()
        .sign_transaction(tx, key_info, CHAIN_ID)
        .await
        .map_err(|e| format!("sign tx error: {}", e))?;
    match w3.eth().send_raw_transaction(signed_tx.raw_transaction).await {
        Ok(txhash) => { 
            ic_cdk::println!("txhash: {}", hex::encode(txhash.0));
            Ok(format!("{}", hex::encode(txhash.0)))
        },
        Err(e) => { Err(e.to_string()) },
    }
}

启动本地副本

dfx start --background --clean --enable-canister-http

部署示例 canister

dfx deploy

致谢

此仓库基于 rust-web3 项目修改。

依赖关系

~9–24MB
~336K SLoC