diff --git a/Cargo.lock b/Cargo.lock index fff04d48..a9fae74d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1590,7 +1590,7 @@ dependencies = [ "getrandom 0.2.14", "hex", "ic-base-types 0.8.0", - "ic-canister-log 0.2.0 (git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c)", + "ic-canister-log 0.2.0 (git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced)", "ic-canisters-http-types 0.8.0", "ic-cdk", "ic-cdk-macros", @@ -2248,7 +2248,7 @@ dependencies = [ [[package]] name = "ic-base-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "base32", "byte-unit", @@ -2291,7 +2291,7 @@ dependencies = [ [[package]] name = "ic-btc-types-internal" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "candid", "ic-btc-interface", @@ -2304,7 +2304,7 @@ dependencies = [ [[package]] name = "ic-canister-log" version = "0.2.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "serde", ] @@ -2408,7 +2408,7 @@ dependencies = [ [[package]] name = "ic-canisters-http-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "candid", "serde", @@ -2540,7 +2540,7 @@ dependencies = [ [[package]] name = "ic-cketh-minter" version = "0.1.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "askama", "async-trait", @@ -2549,7 +2549,7 @@ dependencies = [ "futures", "hex", "hex-literal 0.4.1", - "ic-canister-log 0.2.0 (git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c)", + "ic-canister-log 0.2.0 (git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced)", "ic-canisters-http-types 0.9.0", "ic-cdk", "ic-cdk-macros", @@ -2675,7 +2675,7 @@ dependencies = [ [[package]] name = "ic-crypto-ecdsa-secp256k1" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "k256", "lazy_static", @@ -2994,7 +2994,7 @@ dependencies = [ [[package]] name = "ic-crypto-internal-sha2" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "sha2 0.10.8", ] @@ -3160,7 +3160,7 @@ dependencies = [ [[package]] name = "ic-crypto-sha2" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "ic-crypto-internal-sha2 0.9.0", ] @@ -3168,7 +3168,7 @@ dependencies = [ [[package]] name = "ic-crypto-sha3" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "sha3 0.9.1", ] @@ -3382,7 +3382,7 @@ dependencies = [ [[package]] name = "ic-error-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "ic-utils 0.9.0", "serde", @@ -3469,7 +3469,7 @@ dependencies = [ [[package]] name = "ic-ic00-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "candid", "ic-base-types 0.9.0", @@ -3759,7 +3759,7 @@ dependencies = [ [[package]] name = "ic-protobuf" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "bincode", "candid", @@ -4081,7 +4081,7 @@ dependencies = [ [[package]] name = "ic-sys" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "hex", "ic-crypto-sha2 0.9.0", @@ -4223,7 +4223,7 @@ dependencies = [ [[package]] name = "ic-utils" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "cvt", "hex", @@ -4240,7 +4240,7 @@ dependencies = [ [[package]] name = "ic-utils-ensure" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" [[package]] name = "ic-utils-lru-cache" @@ -4318,7 +4318,7 @@ dependencies = [ [[package]] name = "icrc-ledger-client" version = "0.1.2" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "async-trait", "candid", @@ -4329,7 +4329,7 @@ dependencies = [ [[package]] name = "icrc-ledger-client-cdk" version = "0.1.2" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "async-trait", "candid", @@ -4355,7 +4355,7 @@ dependencies = [ [[package]] name = "icrc-ledger-types" version = "0.1.4" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "base32", "candid", @@ -5414,7 +5414,7 @@ dependencies = [ [[package]] name = "phantom_newtype" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=2e79e7af9a6fa15633d73af69fb021a0ae74df6c#2e79e7af9a6fa15633d73af69fb021a0ae74df6c" +source = "git+https://github.com/dfinity/ic?rev=0f6a050f402248ee611631ce4855b86ae3414ced#0f6a050f402248ee611631ce4855b86ae3414ced" dependencies = [ "candid", "serde", diff --git a/Cargo.toml b/Cargo.toml index b4794397..96fac6c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,8 @@ ic-stable-structures = { workspace = true } ic-certified-map = { workspace = true } ic-cdk = { workspace = true } ic-cdk-macros = { workspace = true } -ic-canister-log = { git = "https://github.com/dfinity/ic", rev = "2e79e7af9a6fa15633d73af69fb021a0ae74df6c", package = "ic-canister-log" } -cketh-common = { git = "https://github.com/dfinity/ic", rev = "2e79e7af9a6fa15633d73af69fb021a0ae74df6c", package = "ic-cketh-minter" } +ic-canister-log = { git = "https://github.com/dfinity/ic", rev = "0f6a050f402248ee611631ce4855b86ae3414ced", package = "ic-canister-log" } +cketh-common = { git = "https://github.com/dfinity/ic", rev = "0f6a050f402248ee611631ce4855b86ae3414ced", package = "ic-cketh-minter" } maplit = "1.0" num = "0.4" num-traits = "0.2" diff --git a/candid/evm_rpc.did b/candid/evm_rpc.did index fd0fba05..8cd10141 100644 --- a/candid/evm_rpc.did +++ b/candid/evm_rpc.did @@ -37,7 +37,18 @@ type EthMainnetService = variant { Cloudflare; PublicNode; }; -type EthSepoliaService = variant { Alchemy; Ankr; BlockPi; PublicNode }; +type EthSepoliaService = variant { + Alchemy; + Ankr; + BlockPi; + PublicNode; +}; +type L2MainnetService = variant { + Alchemy; + Ankr; + BlockPi; + PublicNode; +}; type FeeHistory = record { reward : vec vec nat; gasUsedRatio : vec float64; @@ -175,19 +186,25 @@ type RpcError = variant { }; type RpcApi = record { url : text; headers : opt vec HttpHeader }; type RpcService = variant { - EthSepolia : EthSepoliaService; - EthMainnet : EthMainnetService; Chain : nat64; Provider : nat64; Custom : RpcApi; + EthSepolia : EthSepoliaService; + EthMainnet : EthMainnetService; + ArbitrumOne : L2MainnetService; + BaseMainnet : L2MainnetService; + OptimismMainnet : L2MainnetService; }; type RpcServices = variant { - EthSepolia : opt vec EthSepoliaService; - EthMainnet : opt vec EthMainnetService; Custom : record { chainId : nat64; services : vec RpcApi; }; + EthSepolia : opt vec EthSepoliaService; + EthMainnet : opt vec EthMainnetService; + ArbitrumOne : opt vec L2MainnetService; + BaseMainnet : opt vec L2MainnetService; + OptimismMainnet : opt vec L2MainnetService; }; type SendRawTransactionStatus = variant { Ok : opt text; @@ -231,29 +248,29 @@ type ValidationError = variant { CredentialHeaderNotAllowed; }; service : (InitArgs) -> { - authorize : (principal, Auth) -> (success: bool); - deauthorize : (principal, Auth) -> (success: bool); + authorize : (principal, Auth) -> (success : bool); + deauthorize : (principal, Auth) -> (success : bool); eth_feeHistory : (RpcServices, opt RpcConfig, FeeHistoryArgs) -> (MultiFeeHistoryResult); eth_getBlockByNumber : (RpcServices, opt RpcConfig, BlockTag) -> (MultiGetBlockByNumberResult); eth_getLogs : (RpcServices, opt RpcConfig, GetLogsArgs) -> (MultiGetLogsResult); eth_getTransactionCount : (RpcServices, opt RpcConfig, GetTransactionCountArgs) -> ( MultiGetTransactionCountResult ); - eth_getTransactionReceipt : (RpcServices, opt RpcConfig, hash: text) -> (MultiGetTransactionReceiptResult); - eth_sendRawTransaction : (RpcServices, opt RpcConfig, rawSignedTransactionHex: text) -> (MultiSendRawTransactionResult); - getAccumulatedCycleCount : (ProviderId) -> (cycles: nat) query; + eth_getTransactionReceipt : (RpcServices, opt RpcConfig, hash : text) -> (MultiGetTransactionReceiptResult); + eth_sendRawTransaction : (RpcServices, opt RpcConfig, rawSignedTransactionHex : text) -> (MultiSendRawTransactionResult); + getAccumulatedCycleCount : (ProviderId) -> (cycles : nat) query; getAuthorized : (Auth) -> (vec principal) query; getMetrics : () -> (Metrics) query; - getNodesInSubnet : () -> (numberOfNodes: nat32) query; - getOpenRpcAccess : () -> (active: bool) query; + getNodesInSubnet : () -> (numberOfNodes : nat32) query; + getOpenRpcAccess : () -> (active : bool) query; getProviders : () -> (vec ProviderView) query; getServiceProviderMap : () -> (vec record { RpcService; nat64 }) query; manageProvider : (ManageProviderArgs) -> (); registerProvider : (RegisterProviderArgs) -> (nat64); - request : (RpcService, json: text, maxResponseBytes: nat64) -> (RequestResult); - requestCost : (RpcService, json: text, maxResponseBytes: nat64) -> (RequestCostResult) query; - setOpenRpcAccess : (active: bool) -> (); + request : (RpcService, json : text, maxResponseBytes : nat64) -> (RequestResult); + requestCost : (RpcService, json : text, maxResponseBytes : nat64) -> (RequestCostResult) query; + setOpenRpcAccess : (active : bool) -> (); unregisterProvider : (ProviderId) -> (bool); updateProvider : (UpdateProviderArgs) -> (); - withdrawAccumulatedCycles : (ProviderId, recipient: principal) -> (); + withdrawAccumulatedCycles : (ProviderId, recipient : principal) -> (); }; diff --git a/e2e/motoko/main.mo b/e2e/motoko/main.mo index 6ca17cc9..26eb7820 100644 --- a/e2e/motoko/main.mo +++ b/e2e/motoko/main.mo @@ -149,7 +149,8 @@ shared ({ caller = installer }) actor class Main() { }; }; - let candidRpcCycles = 100_000_000_000; + // Any unused cycles will be refunded + let candidRpcCycles = 200_000_000_000; let allServices : [(Text, EvmRpc.RpcServices)] = [ ( "Ethereum", @@ -157,23 +158,15 @@ shared ({ caller = installer }) actor class Main() { ), ( "Arbitrum", - #Custom { - chainId = 42161; - services = [{ - url = "https://rpc.ankr.com/arbitrum"; - headers = null; - }]; - }, + #ArbitrumOne(null), ), ( "Base", - #Custom { - chainId = 8453; - services = [{ - url = "https://mainnet.base.org"; - headers = null; - }]; - }, + #BaseMainnet(null), + ), + ( + "Optimism", + #OptimismMainnet(null), ), ]; @@ -181,7 +174,24 @@ shared ({ caller = installer }) actor class Main() { switch (await canister.eth_getBlockByNumber(services, null, #Latest)) { case (#Consistent(#Err(#ProviderError(#TooFewCycles _)))) {}; case result { - addError("Received unexpected result for " # networkName # ": " # debug_show result); + let expected = switch result { + case (#Inconsistent(results)) { + var expected = true; + for (result in results.vals()) { + switch result { + case (_service, #Err(#ProviderError(#TooFewCycles _))) {}; + case _ { + expected := false; + }; + }; + }; + expected; + }; + case _ { false }; + }; + if (not expected) { + addError("Received unexpected `eth_getBlockByNumber` result for " # networkName # ": " # debug_show result); + }; }; }; @@ -246,10 +256,7 @@ shared ({ caller = installer }) actor class Main() { ), ); switch services { - case (#Custom _) { - // Skip sending transaction for custom chains due to chain ID mismatch - }; - case _ { + case (#EthMainnet(_)) { Cycles.add(candidRpcCycles); assertOk( networkName, @@ -261,6 +268,9 @@ shared ({ caller = installer }) actor class Main() { ), ); }; + case _ { + // Skip sending transaction for non-Ethereum chains due to chain ID mismatch + }; }; }; diff --git a/src/candid_rpc.rs b/src/candid_rpc.rs index d32e0d53..9132dd81 100644 --- a/src/candid_rpc.rs +++ b/src/candid_rpc.rs @@ -21,7 +21,8 @@ use crate::{ add_metric, add_metric_entry, auth::is_rpc_allowed, constants::{ - DEFAULT_ETH_MAINNET_SERVICES, DEFAULT_ETH_SEPOLIA_SERVICES, ETH_GET_LOGS_MAX_BLOCKS, + DEFAULT_ETH_MAINNET_SERVICES, DEFAULT_ETH_SEPOLIA_SERVICES, DEFAULT_L2_MAINNET_SERVICES, + ETH_GET_LOGS_MAX_BLOCKS, }, http::do_http_request, providers::resolve_rpc_service, @@ -80,6 +81,16 @@ fn get_rpc_client( return Err(ProviderError::NoPermission.into()); } Ok(match source { + RpcServices::Custom { chain_id, services } => CkEthRpcClient::new( + EthereumNetwork(chain_id), + Some( + check_services(services)? + .into_iter() + .map(RpcService::Custom) + .collect(), + ), + config, + ), RpcServices::EthMainnet(services) => CkEthRpcClient::new( EthereumNetwork::MAINNET, Some( @@ -100,12 +111,32 @@ fn get_rpc_client( ), config, ), - RpcServices::Custom { chain_id, services } => CkEthRpcClient::new( - EthereumNetwork(chain_id), + RpcServices::ArbitrumOne(services) => CkEthRpcClient::new( + EthereumNetwork::ARBITRUM, Some( - check_services(services)? + check_services(services.unwrap_or_else(|| DEFAULT_L2_MAINNET_SERVICES.to_vec()))? .into_iter() - .map(RpcService::Custom) + .map(RpcService::ArbitrumOne) + .collect(), + ), + config, + ), + RpcServices::BaseMainnet(services) => CkEthRpcClient::new( + EthereumNetwork::BASE, + Some( + check_services(services.unwrap_or_else(|| DEFAULT_L2_MAINNET_SERVICES.to_vec()))? + .into_iter() + .map(RpcService::BaseMainnet) + .collect(), + ), + config, + ), + RpcServices::OptimismMainnet(services) => CkEthRpcClient::new( + EthereumNetwork::OPTIMISM, + Some( + check_services(services.unwrap_or_else(|| DEFAULT_L2_MAINNET_SERVICES.to_vec()))? + .into_iter() + .map(RpcService::OptimismMainnet) .collect(), ), config, diff --git a/src/constants.rs b/src/constants.rs index a958872e..5edfd42f 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,4 +1,6 @@ -use cketh_common::eth_rpc_client::providers::{EthMainnetService, EthSepoliaService}; +use cketh_common::eth_rpc_client::providers::{ + EthMainnetService, EthSepoliaService, L2MainnetService, +}; // HTTP outcall cost calculation // See https://internetcomputer.org/docs/current/developer-docs/gas-cost#special-features @@ -45,11 +47,19 @@ pub const DEFAULT_ETH_SEPOLIA_SERVICES: &[EthSepoliaService] = &[ EthSepoliaService::BlockPi, EthSepoliaService::PublicNode, ]; +pub const DEFAULT_L2_MAINNET_SERVICES: &[L2MainnetService] = &[ + L2MainnetService::Ankr, + L2MainnetService::BlockPi, + L2MainnetService::PublicNode, +]; pub const CONTENT_TYPE_HEADER: &str = "Content-Type"; pub const CONTENT_TYPE_VALUE: &str = "application/json"; pub const ETH_MAINNET_CHAIN_ID: u64 = 1; pub const ETH_SEPOLIA_CHAIN_ID: u64 = 11155111; +pub const ARBITRUM_ONE_CHAIN_ID: u64 = 42161; +pub const BASE_MAINNET_CHAIN_ID: u64 = 8453; +pub const OPTIMISM_MAINNET_CHAIN_ID: u64 = 10; pub const SERVICE_HOSTS_BLOCKLIST: &[&str] = &[]; diff --git a/src/providers.rs b/src/providers.rs index 46bab259..8fa26521 100644 --- a/src/providers.rs +++ b/src/providers.rs @@ -1,7 +1,9 @@ use candid::{CandidType, Principal}; use cketh_common::{ eth_rpc::ProviderError, - eth_rpc_client::providers::{EthMainnetService, EthSepoliaService, RpcApi, RpcService}, + eth_rpc_client::providers::{ + EthMainnetService, EthSepoliaService, L2MainnetService, RpcApi, RpcService, + }, logs::INFO, }; use ic_canister_log::log; @@ -9,7 +11,10 @@ use ic_canister_log::log; use crate::{ add_metric, auth::do_deauthorize, - constants::{ETH_MAINNET_CHAIN_ID, ETH_SEPOLIA_CHAIN_ID, MINIMUM_WITHDRAWAL_CYCLES}, + constants::{ + ARBITRUM_ONE_CHAIN_ID, BASE_MAINNET_CHAIN_ID, ETH_MAINNET_CHAIN_ID, ETH_SEPOLIA_CHAIN_ID, + MINIMUM_WITHDRAWAL_CYCLES, OPTIMISM_MAINNET_CHAIN_ID, + }, memory::{METADATA, PROVIDERS, SERVICE_PROVIDER_MAP}, types::{ Auth, ManageProviderArgs, Provider, RegisterProviderArgs, ResolvedRpcService, @@ -27,13 +32,31 @@ pub const BLOCKPI_ETH_SEPOLIA_HOSTNAME: &str = "ethereum-sepolia.blockpi.network pub const PUBLICNODE_ETH_MAINNET_HOSTNAME: &str = "ethereum-rpc.publicnode.com"; pub const PUBLICNODE_ETH_SEPOLIA_HOSTNAME: &str = "ethereum-sepolia-rpc.publicnode.com"; pub const ETH_SEPOLIA_HOSTNAME: &str = "rpc.sepolia.org"; +pub const ALCHEMY_ARBITRUM_ONE_HOSTNAME: &str = "arb-mainnet.g.alchemy.com"; +pub const BLOCKPI_ARBITRUM_ONE_HOSTNAME: &str = "arbitrum.blockpi.network"; +pub const PUBLICNODE_ARBITRUM_ONE_HOSTNAME: &str = "arbitrum-one-rpc.publicnode.com"; +pub const ALCHEMY_BASE_MAINNET_HOSTNAME: &str = "base-mainnet.g.alchemy.com"; +pub const BLOCKPI_BASE_MAINNET_HOSTNAME: &str = "base.blockpi.network"; +pub const PUBLICNODE_BASE_MAINNET_HOSTNAME: &str = "base-rpc.publicnode.com"; +pub const ALCHEMY_OPT_MAINNET_HOSTNAME: &str = "opt-mainnet.g.alchemy.com"; +pub const BLOCKPI_OPTIMISM_MAINNET_HOSTNAME: &str = "optimism.blockpi.network"; +pub const PUBLICNODE_OPTIMISM_MAINNET_HOSTNAME: &str = "optimism-rpc.publicnode.com"; // Limited API credentials for local testing. // Use `dfx canister call evm_rpc updateProvider ...` to pass your own keys. pub const ALCHEMY_ETH_MAINNET_CREDENTIAL: &str = "/v2/zBxaSBUMfuH8XnA-uLIWeXfCx1T8ItkM"; pub const ALCHEMY_ETH_SEPOLIA_CREDENTIAL: &str = "/v2/Mbow19DWsfPXiTpdgvRu4HQq63iYycU-"; +pub const ALCHEMY_ARBITRUM_ONE_CREDENTIAL: &str = "/v2"; +pub const ALCHEMY_BASE_MAINNET_CREDENTIAL: &str = "/v2"; +pub const ALCHEMY_OPTIMISM_MAINNET_CREDENTIAL: &str = "/v2"; pub const BLOCKPI_ETH_MAINNET_CREDENTIAL: &str = "/v1/rpc/0edc81e20be23ddff051f61a97bb457ec7284a58"; pub const BLOCKPI_ETH_SEPOLIA_CREDENTIAL: &str = "/v1/rpc/1fe987fddded17db50862311720ff444991d4dab"; +pub const BLOCKPI_ARBITRUM_ONE_CREDENTIAL: &str = + "/v1/rpc/a8b89a41d2a341e32ee7aefcb20820a7cbb65f35"; +pub const BLOCKPI_BASE_MAINNET_CREDENTIAL: &str = + "/v1/rpc/bd458bf9f28ed45c77823814a937c812d2efd260"; +pub const BLOCKPI_OPTIMISM_MAINNET_CREDENTIAL: &str = + "/v1/rpc/d54bfe59299d56b0cbb8b3c69bd122f4ab5ac654"; pub fn get_default_providers() -> Vec { vec![ @@ -117,6 +140,102 @@ pub fn get_default_providers() -> Vec { cycles_per_call: 0, cycles_per_message_byte: 0, }, + RegisterProviderArgs { + chain_id: ARBITRUM_ONE_CHAIN_ID, + hostname: ANKR_HOSTNAME.to_string(), + credential_path: "/arbitrum".to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: ARBITRUM_ONE_CHAIN_ID, + hostname: ALCHEMY_ARBITRUM_ONE_HOSTNAME.to_string(), + credential_path: ALCHEMY_ARBITRUM_ONE_CREDENTIAL.to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: ARBITRUM_ONE_CHAIN_ID, + hostname: BLOCKPI_ARBITRUM_ONE_HOSTNAME.to_string(), + credential_path: BLOCKPI_ARBITRUM_ONE_CREDENTIAL.to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: ARBITRUM_ONE_CHAIN_ID, + hostname: PUBLICNODE_ARBITRUM_ONE_HOSTNAME.to_string(), + credential_path: "".to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: BASE_MAINNET_CHAIN_ID, + hostname: ANKR_HOSTNAME.to_string(), + credential_path: "/base".to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: BASE_MAINNET_CHAIN_ID, + hostname: ALCHEMY_BASE_MAINNET_HOSTNAME.to_string(), + credential_path: ALCHEMY_BASE_MAINNET_CREDENTIAL.to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: BASE_MAINNET_CHAIN_ID, + hostname: BLOCKPI_BASE_MAINNET_HOSTNAME.to_string(), + credential_path: BLOCKPI_BASE_MAINNET_CREDENTIAL.to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: BASE_MAINNET_CHAIN_ID, + hostname: PUBLICNODE_BASE_MAINNET_HOSTNAME.to_string(), + credential_path: "".to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + hostname: ANKR_HOSTNAME.to_string(), + credential_path: "/optimism".to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + hostname: ALCHEMY_OPT_MAINNET_HOSTNAME.to_string(), + credential_path: ALCHEMY_OPTIMISM_MAINNET_CREDENTIAL.to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + hostname: BLOCKPI_OPTIMISM_MAINNET_HOSTNAME.to_string(), + credential_path: BLOCKPI_OPTIMISM_MAINNET_CREDENTIAL.to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, + RegisterProviderArgs { + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + hostname: PUBLICNODE_OPTIMISM_MAINNET_HOSTNAME.to_string(), + credential_path: "".to_string(), + credential_headers: None, + cycles_per_call: 0, + cycles_per_message_byte: 0, + }, ] } @@ -158,6 +277,54 @@ pub fn get_default_service_provider_hostnames() -> Vec<(RpcService, &'static str RpcService::EthSepolia(EthSepoliaService::PublicNode), PUBLICNODE_ETH_SEPOLIA_HOSTNAME, ), + ( + RpcService::ArbitrumOne(L2MainnetService::Alchemy), + ALCHEMY_ARBITRUM_ONE_HOSTNAME, + ), + ( + RpcService::ArbitrumOne(L2MainnetService::Ankr), + ANKR_HOSTNAME, + ), + ( + RpcService::ArbitrumOne(L2MainnetService::BlockPi), + BLOCKPI_ARBITRUM_ONE_HOSTNAME, + ), + ( + RpcService::ArbitrumOne(L2MainnetService::PublicNode), + PUBLICNODE_ARBITRUM_ONE_HOSTNAME, + ), + ( + RpcService::BaseMainnet(L2MainnetService::Alchemy), + ALCHEMY_BASE_MAINNET_HOSTNAME, + ), + ( + RpcService::BaseMainnet(L2MainnetService::Ankr), + ANKR_HOSTNAME, + ), + ( + RpcService::BaseMainnet(L2MainnetService::BlockPi), + BLOCKPI_BASE_MAINNET_HOSTNAME, + ), + ( + RpcService::BaseMainnet(L2MainnetService::PublicNode), + PUBLICNODE_BASE_MAINNET_HOSTNAME, + ), + ( + RpcService::OptimismMainnet(L2MainnetService::Alchemy), + ALCHEMY_OPT_MAINNET_HOSTNAME, + ), + ( + RpcService::OptimismMainnet(L2MainnetService::Ankr), + ANKR_HOSTNAME, + ), + ( + RpcService::OptimismMainnet(L2MainnetService::BlockPi), + BLOCKPI_OPTIMISM_MAINNET_HOSTNAME, + ), + ( + RpcService::OptimismMainnet(L2MainnetService::PublicNode), + PUBLICNODE_OPTIMISM_MAINNET_HOSTNAME, + ), ] } @@ -187,11 +354,14 @@ fn lookup_provider_for_service(service: &RpcService) -> Result Option { match service { - RpcService::EthMainnet(_) => Some(ETH_MAINNET_CHAIN_ID), - RpcService::EthSepolia(_) => Some(ETH_SEPOLIA_CHAIN_ID), RpcService::Chain(chain_id) => Some(*chain_id), RpcService::Provider(_) => None, RpcService::Custom(_) => None, + RpcService::EthMainnet(_) => Some(ETH_MAINNET_CHAIN_ID), + RpcService::EthSepolia(_) => Some(ETH_SEPOLIA_CHAIN_ID), + RpcService::ArbitrumOne(_) => Some(ARBITRUM_ONE_CHAIN_ID), + RpcService::BaseMainnet(_) => Some(BASE_MAINNET_CHAIN_ID), + RpcService::OptimismMainnet(_) => Some(OPTIMISM_MAINNET_CHAIN_ID), } } @@ -417,12 +587,6 @@ pub fn set_service_provider(service: &RpcService, provider: &Provider) { pub fn resolve_rpc_service(service: RpcService) -> Result { Ok(match service { - RpcService::EthMainnet(service) => ResolvedRpcService::Provider( - lookup_provider_for_service(&RpcService::EthMainnet(service))?, - ), - RpcService::EthSepolia(service) => ResolvedRpcService::Provider( - lookup_provider_for_service(&RpcService::EthSepolia(service))?, - ), RpcService::Chain(id) => ResolvedRpcService::Provider(PROVIDERS.with(|providers| { let providers = providers.borrow(); Ok(providers @@ -443,5 +607,20 @@ pub fn resolve_rpc_service(service: RpcService) -> Result { ResolvedRpcService::Api(RpcApi { url, headers }) } + RpcService::EthMainnet(service) => ResolvedRpcService::Provider( + lookup_provider_for_service(&RpcService::EthMainnet(service))?, + ), + RpcService::EthSepolia(service) => ResolvedRpcService::Provider( + lookup_provider_for_service(&RpcService::EthSepolia(service))?, + ), + RpcService::ArbitrumOne(service) => ResolvedRpcService::Provider( + lookup_provider_for_service(&RpcService::ArbitrumOne(service))?, + ), + RpcService::BaseMainnet(service) => ResolvedRpcService::Provider( + lookup_provider_for_service(&RpcService::BaseMainnet(service))?, + ), + RpcService::OptimismMainnet(service) => ResolvedRpcService::Provider( + lookup_provider_for_service(&RpcService::OptimismMainnet(service))?, + ), }) } diff --git a/src/types.rs b/src/types.rs index 0512f093..b5a344ee 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ use candid::{CandidType, Decode, Deserialize, Encode, Principal}; use cketh_common::eth_rpc::RpcError; use cketh_common::eth_rpc_client::providers::{ - EthMainnetService, EthSepoliaService, RpcApi, RpcService, + EthMainnetService, EthSepoliaService, L2MainnetService, RpcApi, RpcService, }; use ic_cdk::api::management_canister::http_request::HttpHeader; @@ -501,13 +501,16 @@ impl From> for MultiRpcResult { #[derive(Clone, CandidType, Deserialize)] pub enum RpcServices { - EthMainnet(Option>), - EthSepolia(Option>), Custom { #[serde(rename = "chainId")] chain_id: u64, services: Vec, }, + EthMainnet(Option>), + EthSepolia(Option>), + ArbitrumOne(Option>), + BaseMainnet(Option>), + OptimismMainnet(Option>), } pub mod candid_types { diff --git a/tests/tests.rs b/tests/tests.rs index 4c419209..3d206bdc 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -62,6 +62,14 @@ const MOCK_TRANSACTION: &str="0xf86c098504a817c800825208943535353535353535353535 const MOCK_TRANSACTION_HASH: &str = "0x33469b22e9f636356c4160a87eb19df52b7412e8eac32a4a55ffe88ea8350788"; +const RPC_SERVICES: &[RpcServices] = &[ + RpcServices::EthMainnet(None), + RpcServices::EthSepolia(None), + RpcServices::ArbitrumOne(None), + RpcServices::BaseMainnet(None), + RpcServices::OptimismMainnet(None), +]; + fn evm_rpc_wasm() -> Vec { load_wasm(std::env::var("CARGO_MANIFEST_DIR").unwrap(), "evm_rpc", &[]) } @@ -974,10 +982,11 @@ fn should_decode_transaction_receipt() { #[test] fn eth_get_logs_should_succeed() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - let response = setup + for source in RPC_SERVICES { + let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let response = setup .eth_get_logs( - RpcServices::EthMainnet(None), + source.clone(), None, candid_types::GetLogsArgs { addresses: vec!["0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string()], @@ -990,89 +999,93 @@ fn eth_get_logs_should_succeed() { .wait() .expect_consistent() .unwrap(); - assert_eq!( - response, - vec![LogEntry { - address: Address::from_str("0xdac17f958d2ee523a2206206994597c13d831ec7").unwrap(), - topics: vec![ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x000000000000000000000000a9d1e08c7793af67e9d92fe308d5697fb81d3e43", - "0x00000000000000000000000078cccfb3d517cd4ed6d045e263e134712288ace2" - ] - .into_iter() - .map(|hex| FixedSizeData::from_str(hex).unwrap()) - .collect(), - data: Data( - hex::decode("000000000000000000000000000000000000000000000000000000003b9c6433") + assert_eq!( + response, + vec![LogEntry { + address: Address::from_str("0xdac17f958d2ee523a2206206994597c13d831ec7").unwrap(), + topics: vec![ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000a9d1e08c7793af67e9d92fe308d5697fb81d3e43", + "0x00000000000000000000000078cccfb3d517cd4ed6d045e263e134712288ace2" + ] + .into_iter() + .map(|hex| FixedSizeData::from_str(hex).unwrap()) + .collect(), + data: Data( + hex::decode("000000000000000000000000000000000000000000000000000000003b9c6433") + .unwrap() + ), + block_number: Some(CheckedAmountOf::new(0x11dc77e)), + transaction_hash: Some( + Hash::from_str( + "0xf3ed91a03ddf964281ac7a24351573efd535b80fc460a5c2ad2b9d23153ec678" + ) .unwrap() - ), - block_number: Some(CheckedAmountOf::new(0x11dc77e)), - transaction_hash: Some( - Hash::from_str( - "0xf3ed91a03ddf964281ac7a24351573efd535b80fc460a5c2ad2b9d23153ec678" - ) - .unwrap() - ), - transaction_index: Some(CheckedAmountOf::new(0x65)), - block_hash: Some( - Hash::from_str( - "0xd5c72ad752b2f0144a878594faf8bd9f570f2f72af8e7f0940d3545a6388f629" - ) - .unwrap() - ), - log_index: Some(CheckedAmountOf::new(0xe8)), - removed: false - }] - ); + ), + transaction_index: Some(CheckedAmountOf::new(0x65)), + block_hash: Some( + Hash::from_str( + "0xd5c72ad752b2f0144a878594faf8bd9f570f2f72af8e7f0940d3545a6388f629" + ) + .unwrap() + ), + log_index: Some(CheckedAmountOf::new(0xe8)), + removed: false + }] + ); + } } #[test] fn eth_get_block_by_number_should_succeed() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - let response = setup - .eth_get_block_by_number( - RpcServices::EthMainnet(None), - None, - candid_types::BlockTag::Latest, - ) - .mock_http(MockOutcallBuilder::new(200, r#"{"jsonrpc":"2.0","result":{"baseFeePerGas":"0xd7232aa34","difficulty":"0x0","extraData":"0x546974616e2028746974616e6275696c6465722e78797a29","gasLimit":"0x1c9c380","gasUsed":"0xa768c4","hash":"0xc3674be7b9d95580d7f23c03d32e946f2b453679ee6505e3a778f003c5a3cfae","logsBloom":"0x3e6b8420e1a13038902c24d6c2a9720a7ad4860cdc870cd5c0490011e43631134f608935bd83171247407da2c15d85014f9984608c03684c74aad48b20bc24022134cdca5f2e9d2dee3b502a8ccd39eff8040b1d96601c460e119c408c620b44fa14053013220847045556ea70484e67ec012c322830cf56ef75e09bd0db28a00f238adfa587c9f80d7e30d3aba2863e63a5cad78954555966b1055a4936643366a0bb0b1bac68d0e6267fc5bf8304d404b0c69041125219aa70562e6a5a6362331a414a96d0716990a10161b87dd9568046a742d4280014975e232b6001a0360970e569d54404b27807d7a44c949ac507879d9d41ec8842122da6772101bc8b","miner":"0x388c818ca8b9251b393131c08a736a67ccb19297","mixHash":"0x516a58424d4883a3614da00a9c6f18cd5cd54335a08388229a993a8ecf05042f","nonce":"0x0000000000000000","number":"0x11db01d","parentHash":"0x43325027f6adf9befb223f8ae80db057daddcd7b48e41f60cd94bfa8877181ae","receiptsRoot":"0x66934c3fd9c547036fe0e56ad01bc43c84b170be7c4030a86805ddcdab149929","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0xcd35","stateRoot":"0x13552447dd62f11ad885f21a583c4fa34144efe923c7e35fb018d6710f06b2b6","timestamp":"0x656f96f3","totalDifficulty":"0xc70d815d562d3cfa955","withdrawalsRoot":"0xecae44b2c53871003c5cc75285995764034c9b5978a904229d36c1280b141d48"},"id":0}"#)) - .wait() - .expect_consistent() - .unwrap(); - assert_eq!( - response, - Block { - base_fee_per_gas: Wei::new(57_750_497_844), - difficulty: CheckedAmountOf::new(0), - extra_data: "0x546974616e2028746974616e6275696c6465722e78797a29".to_string(), - gas_limit: CheckedAmountOf::new(0x1c9c380), - gas_used: CheckedAmountOf::new(0xa768c4), - hash: "0xc3674be7b9d95580d7f23c03d32e946f2b453679ee6505e3a778f003c5a3cfae".to_string(), - logs_bloom: "0x3e6b8420e1a13038902c24d6c2a9720a7ad4860cdc870cd5c0490011e43631134f608935bd83171247407da2c15d85014f9984608c03684c74aad48b20bc24022134cdca5f2e9d2dee3b502a8ccd39eff8040b1d96601c460e119c408c620b44fa14053013220847045556ea70484e67ec012c322830cf56ef75e09bd0db28a00f238adfa587c9f80d7e30d3aba2863e63a5cad78954555966b1055a4936643366a0bb0b1bac68d0e6267fc5bf8304d404b0c69041125219aa70562e6a5a6362331a414a96d0716990a10161b87dd9568046a742d4280014975e232b6001a0360970e569d54404b27807d7a44c949ac507879d9d41ec8842122da6772101bc8b".to_string(), - miner: "0x388c818ca8b9251b393131c08a736a67ccb19297".to_string(), - mix_hash: "0x516a58424d4883a3614da00a9c6f18cd5cd54335a08388229a993a8ecf05042f".to_string(), - nonce: CheckedAmountOf::new(0), - number: BlockNumber::new(18_722_845), - parent_hash: "0x43325027f6adf9befb223f8ae80db057daddcd7b48e41f60cd94bfa8877181ae".to_string(), - receipts_root: "0x66934c3fd9c547036fe0e56ad01bc43c84b170be7c4030a86805ddcdab149929".to_string(), - sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".to_string(), - size: CheckedAmountOf::new(0xcd35), - state_root: "0x13552447dd62f11ad885f21a583c4fa34144efe923c7e35fb018d6710f06b2b6".to_string(), - timestamp: CheckedAmountOf::new(0x656f96f3), - total_difficulty: CheckedAmountOf::new(0xc70d815d562d3cfa955), - transactions: vec![], - transactions_root: None, - uncles: vec![], - } - ); + for source in RPC_SERVICES { + let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let response = setup + .eth_get_block_by_number( + source.clone(), + None, + candid_types::BlockTag::Latest, + ) + .mock_http(MockOutcallBuilder::new(200, r#"{"jsonrpc":"2.0","result":{"baseFeePerGas":"0xd7232aa34","difficulty":"0x0","extraData":"0x546974616e2028746974616e6275696c6465722e78797a29","gasLimit":"0x1c9c380","gasUsed":"0xa768c4","hash":"0xc3674be7b9d95580d7f23c03d32e946f2b453679ee6505e3a778f003c5a3cfae","logsBloom":"0x3e6b8420e1a13038902c24d6c2a9720a7ad4860cdc870cd5c0490011e43631134f608935bd83171247407da2c15d85014f9984608c03684c74aad48b20bc24022134cdca5f2e9d2dee3b502a8ccd39eff8040b1d96601c460e119c408c620b44fa14053013220847045556ea70484e67ec012c322830cf56ef75e09bd0db28a00f238adfa587c9f80d7e30d3aba2863e63a5cad78954555966b1055a4936643366a0bb0b1bac68d0e6267fc5bf8304d404b0c69041125219aa70562e6a5a6362331a414a96d0716990a10161b87dd9568046a742d4280014975e232b6001a0360970e569d54404b27807d7a44c949ac507879d9d41ec8842122da6772101bc8b","miner":"0x388c818ca8b9251b393131c08a736a67ccb19297","mixHash":"0x516a58424d4883a3614da00a9c6f18cd5cd54335a08388229a993a8ecf05042f","nonce":"0x0000000000000000","number":"0x11db01d","parentHash":"0x43325027f6adf9befb223f8ae80db057daddcd7b48e41f60cd94bfa8877181ae","receiptsRoot":"0x66934c3fd9c547036fe0e56ad01bc43c84b170be7c4030a86805ddcdab149929","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0xcd35","stateRoot":"0x13552447dd62f11ad885f21a583c4fa34144efe923c7e35fb018d6710f06b2b6","timestamp":"0x656f96f3","totalDifficulty":"0xc70d815d562d3cfa955","withdrawalsRoot":"0xecae44b2c53871003c5cc75285995764034c9b5978a904229d36c1280b141d48"},"id":0}"#)) + .wait() + .expect_consistent() + .unwrap(); + assert_eq!( + response, + Block { + base_fee_per_gas: Wei::new(57_750_497_844), + difficulty: CheckedAmountOf::new(0), + extra_data: "0x546974616e2028746974616e6275696c6465722e78797a29".to_string(), + gas_limit: CheckedAmountOf::new(0x1c9c380), + gas_used: CheckedAmountOf::new(0xa768c4), + hash: "0xc3674be7b9d95580d7f23c03d32e946f2b453679ee6505e3a778f003c5a3cfae".to_string(), + logs_bloom: "0x3e6b8420e1a13038902c24d6c2a9720a7ad4860cdc870cd5c0490011e43631134f608935bd83171247407da2c15d85014f9984608c03684c74aad48b20bc24022134cdca5f2e9d2dee3b502a8ccd39eff8040b1d96601c460e119c408c620b44fa14053013220847045556ea70484e67ec012c322830cf56ef75e09bd0db28a00f238adfa587c9f80d7e30d3aba2863e63a5cad78954555966b1055a4936643366a0bb0b1bac68d0e6267fc5bf8304d404b0c69041125219aa70562e6a5a6362331a414a96d0716990a10161b87dd9568046a742d4280014975e232b6001a0360970e569d54404b27807d7a44c949ac507879d9d41ec8842122da6772101bc8b".to_string(), + miner: "0x388c818ca8b9251b393131c08a736a67ccb19297".to_string(), + mix_hash: "0x516a58424d4883a3614da00a9c6f18cd5cd54335a08388229a993a8ecf05042f".to_string(), + nonce: CheckedAmountOf::new(0), + number: BlockNumber::new(18_722_845), + parent_hash: "0x43325027f6adf9befb223f8ae80db057daddcd7b48e41f60cd94bfa8877181ae".to_string(), + receipts_root: "0x66934c3fd9c547036fe0e56ad01bc43c84b170be7c4030a86805ddcdab149929".to_string(), + sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".to_string(), + size: CheckedAmountOf::new(0xcd35), + state_root: "0x13552447dd62f11ad885f21a583c4fa34144efe923c7e35fb018d6710f06b2b6".to_string(), + timestamp: CheckedAmountOf::new(0x656f96f3), + total_difficulty: CheckedAmountOf::new(0xc70d815d562d3cfa955), + transactions: vec![], + transactions_root: None, + uncles: vec![], + } + ); + } } #[test] fn eth_get_transaction_receipt_should_succeed() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - let response = setup + for source in RPC_SERVICES { + let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let response = setup .eth_get_transaction_receipt( - RpcServices::EthMainnet(None), + source.clone(), None, "0xdd5d4b18923d7aae953c7996d791118102e889bea37b48a651157a4890e4746f", ) @@ -1080,7 +1093,7 @@ fn eth_get_transaction_receipt_should_succeed() { .wait() .expect_consistent() .unwrap(); - assert_eq!( + assert_eq!( response, Some(candid_types::TransactionReceipt { status: 0x1.into(), @@ -1098,36 +1111,40 @@ fn eth_get_transaction_receipt_should_succeed() { r#type: "0x2".to_string(), }) ); + } } #[test] fn eth_get_transaction_count_should_succeed() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - let response = setup - .eth_get_transaction_count( - RpcServices::EthMainnet(None), - None, - candid_types::GetTransactionCountArgs { - address: "0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string(), - block: candid_types::BlockTag::Latest, - }, - ) - .mock_http(MockOutcallBuilder::new( - 200, - r#"{"jsonrpc":"2.0","id":0,"result":"0x1"}"#, - )) - .wait() - .expect_consistent() - .unwrap(); - assert_eq!(response, 1); + for source in RPC_SERVICES { + let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let response = setup + .eth_get_transaction_count( + source.clone(), + None, + candid_types::GetTransactionCountArgs { + address: "0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string(), + block: candid_types::BlockTag::Latest, + }, + ) + .mock_http(MockOutcallBuilder::new( + 200, + r#"{"jsonrpc":"2.0","id":0,"result":"0x1"}"#, + )) + .wait() + .expect_consistent() + .unwrap(); + assert_eq!(response, 1); + } } #[test] fn eth_fee_history_should_succeed() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - let response = setup + for source in RPC_SERVICES { + let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let response = setup .eth_fee_history( - RpcServices::EthMainnet(None), + source.clone(), None, candid_types::FeeHistoryArgs { block_count: 3, @@ -1142,38 +1159,41 @@ fn eth_fee_history_should_succeed() { .wait() .expect_consistent() .unwrap(); - assert_eq!( - response, - Some(FeeHistory { - oldest_block: CheckedAmountOf::new(0x11e57f5), - base_fee_per_gas: vec!["0x9cf6c61b9", "0x97d853982", "0x9ba55a0b0", "0x9543bf98d"] - .into_iter() - .map(|hex| CheckedAmountOf::from_str_hex(hex).unwrap()) - .collect(), - gas_used_ratio: vec![], - reward: vec![vec![CheckedAmountOf::new(0x0123)]], - }) - ); + assert_eq!( + response, + Some(FeeHistory { + oldest_block: CheckedAmountOf::new(0x11e57f5), + base_fee_per_gas: vec!["0x9cf6c61b9", "0x97d853982", "0x9ba55a0b0", "0x9543bf98d"] + .into_iter() + .map(|hex| CheckedAmountOf::from_str_hex(hex).unwrap()) + .collect(), + gas_used_ratio: vec![], + reward: vec![vec![CheckedAmountOf::new(0x0123)]], + }) + ); + } } #[test] fn eth_send_raw_transaction_should_succeed() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - let response = setup - .eth_send_raw_transaction(RpcServices::EthMainnet(None), None, MOCK_TRANSACTION) - .mock_http(MockOutcallBuilder::new( - 200, - r#"{"id":0,"jsonrpc":"2.0","result":"Ok"}"#, - )) - .wait() - .expect_consistent() - .unwrap(); - assert_eq!( - response, - candid_types::SendRawTransactionStatus::Ok(Some( - Hash::from_str(MOCK_TRANSACTION_HASH).unwrap() - )) - ); + for source in RPC_SERVICES { + let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let response = setup + .eth_send_raw_transaction(source.clone(), None, MOCK_TRANSACTION) + .mock_http(MockOutcallBuilder::new( + 200, + r#"{"id":0,"jsonrpc":"2.0","result":"Ok"}"#, + )) + .wait() + .expect_consistent() + .unwrap(); + assert_eq!( + response, + candid_types::SendRawTransactionStatus::Ok(Some( + Hash::from_str(MOCK_TRANSACTION_HASH).unwrap() + )) + ); + } } #[test]