7个版本
0.1.7 | 2024年5月22日 |
---|---|
0.1.6 | 2024年4月30日 |
0.1.4 | 2024年1月17日 |
0.1.3 | 2023年12月5日 |
0.1.1 | 2023年6月9日 |
#433 in 魔法豆
66 每月下载量
用于 3 crates
545KB
11K SLoC
ic-web3-rs
用于访问以太坊网络的Internet Computer canisters的RPC客户端,由Internet Computer的阈值ECDSA签名和出站http调用功能提供支持。
这是rocklabs-io/ic-web3的分支。
功能
- 在canisters中执行对以太坊网络的RPC调用
- 使用IC的阈值ECDSA签名消息
- 在canisters中向以太坊网络发送交易
- 在canisters中查询/调用以太坊合约
用法
将以下内容添加到您的 Cargo.toml
[dependencies]
ic-web3-rs = { git = "https://github.com/horizonx-tech/ic-web3-rs" }
自定义HTTP转换
这支持自定义HTTP转换,这对于避免 no consensus was reached
错误很有用。当使用同一个canister向以太坊网络发送多种请求时,例如 eth_getTransactionCount
和 eth_getBalance
,因此canister必须转换不同类型的响应。要使用此功能,您需要实现 TransformContext
特性并将其作为 CallOptions
传递。
use ic_web3::{
contract::Options, ethabi::Address, transforms::processors,
transforms::transform::TransformProcessor, transports::ic_http_client::CallOptionsBuilder,
};
...
#[query]
#[candid_method(query)]
fn transform_request(args: TransformArgs) -> HttpResponse {
processors::get_filter_changes_processor().transform(args)
}
fn call_options() -> Options {
let call_options = CallOptionsBuilder::default()
.transform(Some(TransformContext {
function: TransformFunc(candid::Func {
principal: ic_cdk::api::id(),
method: "transform_request".to_string(),
}),
context: vec![],
}))
.max_resp(None)
.cycles(None)
.build()
.unwrap();
let mut opts = Options::default();
opts.call_options = Some(call_options);
opts
}
#[update]
#[candid_method(update)]
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);
}
}
示例
注意:您应该有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
端点Canister
公共端点容器部署在: 3ondx-siaaa-aaaam-abf3q-cai
, 代码。您可以通过将RPC调用传递到端点容器来访问以太坊主网数据。
致谢
此仓库是基于ic-web3项目修改的。
依赖项
~6-24MB
~304K SLoC