ic-web3-rs

RPC client for canisters on the Internet Computer to access Ethereum networks, powered by the Internet Computer's threshold ECDSA signature and outbound http call features.

This is a fork of rocklabs-io/ic-web3.

Features

Usage

Add the following to your Cargo.toml:

[dependencies] ic-web3-rs = { git = "https://github.com/horizonx-tech/ic-web3-rs" }

Examples

Note: you should have dfx 0.11.2 or above.

Please refer to example for the complete example.

```rust use candid::candidmethod; use iccdk_macros::{self, update}; use std::str::FromStr;

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

const URL: &str = ""; const CHAINID: u64 = 5; const KEYNAME: &str = "dfxtestkey"; const TOKENABI: &[u8] = includebytes!("../src/contract/res/token.json");

type Result = std::result::Result;

[update(name = "getethgas_price")]

[candidmethod(update, rename = "getethgasprice")]

async fn getethgasprice() -> Result { let w3 = match ICHttp::new(URL, None) { Ok(v) => { Web3::new(v) }, Err(e) => { return Err(e.tostring()) }, }; let gasprice = w3.eth().gasprice().await.maperr(|e| format!("get gas price failed: {}", e))?; iccdk::println!("gas price: {}", gasprice); Ok(format!("{}", gasprice)) }

// get canister's ethereum address

[update(name = "getcanisteraddr")]

[candidmethod(update, rename = "getcanister_addr")]

async fn getcanisteraddr() -> Result { match getethaddr(None, None, KEYNAME.tostring()).await { Ok(addr) => { Ok(hex::encode(addr)) }, Err(e) => { Err(e) }, } }

// send tx to eth

[update(name = "send_eth")]

[candidmethod(update, rename = "sendeth")]

async fn sendeth(to: String, value: u64) -> Result { // ecdsa key info let derivationpath = vec![iccdk::id().asslice().tovec()]; let keyinfo = KeyInfo{ derivationpath: derivationpath, keyname: KEYNAME.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()) },
}

} ```

Start a local replica:

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

Deploy the example canister:

dfx deploy

Endpoint Canister

The public endpoint canister is deployed at: 3ondx-siaaa-aaaam-abf3q-cai, code. You can access Ethereum Mainnet data by passing RPC calls to the endpoint canister.

Acknowledgment

This repo is modified from the ic-web3 project.