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.
Add the following to your Cargo.toml
:
[dependencies]
ic-web3-rs = { git = "https://github.com/horizonx-tech/ic-web3-rs" }
This supports custom HTTP transformation, which is useful to avoid no consensus was reached
errors.
This helps when to use the same canister to send multiple kinds of requests to Ethereum networks, such as eth_getTransactionCount
and eth_getBalance
, so that the canister must transform different types of responses.
To use this feature, you need to implement the TransformContext
trait and pass it as CallOptions
.
```rust use icweb3::{ contract::Options, ethabi::Address, transforms::processors, transforms::transform::TransformProcessor, transports::ichttp_client::CallOptionsBuilder, }; ...
fn transformrequest(args: TransformArgs) -> HttpResponse { processors::getfilterchangesprocessor().transform(args) }
fn calloptions() -> Options { let calloptions = CallOptionsBuilder::default() .transform(Some(TransformContext { function: TransformFunc(candid::Func { principal: iccdk::api::id(), method: "transformrequest".tostring(), }), context: vec![], })) .maxresp(None) .cycles(None) .build() .unwrap(); let mut opts = Options::default(); opts.calloptions = Some(calloptions); opts }
async fn set_value(symbol: String, value: WrappedU256) { struct Dist { nw: SupportedNetwork, addr: Address, }
for d in ORACLE_ADDRESSES.with(|addresses| {
addresses
.borrow()
.iter()
.map(|(&k, &v)| Dist { nw: k, addr: v })
.collect::<Vec<Dist>>()
}) {
let context = ctx(d.nw).unwrap();
let oracle = IPriceOracle::new(d.addr.clone(), &context);
let res = match oracle
.set_price(
symbol.to_string().clone(),
value.value(),
Some(call_options()),
)
.await
{
Ok(v) => ic_cdk::println!("set_value: {:?}", v),
Err(e) => {
ic_cdk::println!("set_value error: {:?}. retry", e);
oracle
.set_price(
symbol.to_string().clone(),
value.value(),
Some(call_options()), // This is the custom HTTP transformation
)
.await;
}
};
ic_cdk::println!("set_value: {:?}", res);
}
} ```
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 = "
type Result
async fn getethgasprice() -> Result
// get canister's ethereum address
async fn getcanisteraddr() -> Result
// send tx to eth
async fn sendeth(to: String, value: u64) -> Result
// 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
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.
This repo is modified from the ic-web3 project.