From 2a8184c2daa7aad9b4f2250f04aab6298956e972 Mon Sep 17 00:00:00 2001 From: rvanasa Date: Fri, 20 Oct 2023 17:51:35 -0600 Subject: [PATCH] Implement 'eth_get_transaction_count' method --- candid/evm_rpc.did | 25 +++++++++++++++++-------- lib/cketh/src/eth_rpc_client/mod.rs | 16 +++++++++------- lib/motoko/declarations/evm_rpc.mo | 26 ++++++++++++++++++-------- src/main.rs | 19 +++++++++++++++++++ src/types.rs | 22 +++++++++++++++++++++- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/candid/evm_rpc.did b/candid/evm_rpc.did index 16f3938a..9fbee77c 100644 --- a/candid/evm_rpc.did +++ b/candid/evm_rpc.did @@ -27,6 +27,7 @@ type GetLogsArgs = record { to_block : opt BlockSpec; from_block : opt BlockSpec; }; +type GetTransactionCountArgs = record { address : text; block : BlockSpec }; type HttpOutcallError = variant { IcError : record { code : RejectionCode; message : text }; InvalidHttpJsonRpcResponse : record { @@ -60,6 +61,10 @@ type MultiRpcResult_2 = variant { Consistent : Result_3; Inconsistent : vec record { RpcNodeProvider; Result_3 }; }; +type MultiRpcResult_3 = variant { + Consistent : Result_4; + Inconsistent : vec record { RpcNodeProvider; Result_4 }; +}; type MultiSource = variant { Ethereum : opt vec EthereumProvider; Sepolia : opt vec SepoliaProvider; @@ -99,10 +104,11 @@ type RejectionCode = variant { type Result = variant { Ok : opt FeeHistory; Err : RpcError }; type Result_1 = variant { Ok : Block; Err : RpcError }; type Result_2 = variant { Ok : vec LogEntry; Err : RpcError }; -type Result_3 = variant { Ok : opt TransactionReceipt; Err : RpcError }; -type Result_4 = variant { Ok : SendRawTransactionResult; Err : RpcError }; -type Result_5 = variant { Ok : text; Err : RpcError }; -type Result_6 = variant { Ok : nat; Err : RpcError }; +type Result_3 = variant { Ok : nat; Err : RpcError }; +type Result_4 = variant { Ok : opt TransactionReceipt; Err : RpcError }; +type Result_5 = variant { Ok : SendRawTransactionResult; Err : RpcError }; +type Result_6 = variant { Ok : text; Err : RpcError }; +type Result_7 = variant { Ok : nat; Err : RpcError }; type RpcError = variant { JsonRpcError : JsonRpcError; ProviderError : ProviderError; @@ -154,16 +160,19 @@ service : { eth_fee_history : (MultiSource, FeeHistoryArgs) -> (Result); eth_get_block_by_number : (MultiSource, BlockSpec) -> (MultiRpcResult); eth_get_logs : (MultiSource, GetLogsArgs) -> (MultiRpcResult_1); - eth_get_transaction_receipt : (MultiSource, vec nat8) -> (MultiRpcResult_2); - eth_send_raw_transaction : (MultiSource, text) -> (Result_4); + eth_get_transaction_count : (MultiSource, GetTransactionCountArgs) -> ( + MultiRpcResult_2, + ); + eth_get_transaction_receipt : (MultiSource, vec nat8) -> (MultiRpcResult_3); + eth_send_raw_transaction : (MultiSource, text) -> (Result_5); get_accumulated_cycle_count : (nat64) -> (nat) query; get_authorized : (Auth) -> (vec text) query; get_nodes_in_subnet : () -> (nat32) query; get_open_rpc_access : () -> (bool) query; get_providers : () -> (vec ProviderView) query; register_provider : (RegisterProvider) -> (nat64); - request : (Source, text, nat64) -> (Result_5); - request_cost : (Source, text, nat64) -> (Result_6) query; + request : (Source, text, nat64) -> (Result_6); + request_cost : (Source, text, nat64) -> (Result_7) query; set_nodes_in_subnet : (nat32) -> (); set_open_rpc_access : (bool) -> (); unregister_provider : (nat64) -> (bool); diff --git a/lib/cketh/src/eth_rpc_client/mod.rs b/lib/cketh/src/eth_rpc_client/mod.rs index ba20c94d..bcfbf69f 100644 --- a/lib/cketh/src/eth_rpc_client/mod.rs +++ b/lib/cketh/src/eth_rpc_client/mod.rs @@ -235,13 +235,15 @@ impl EthRpcClient { pub async fn eth_get_transaction_count( &self, params: GetTransactionCountParams, - ) -> MultiCallResults { - self.parallel_call( - "eth_getTransactionCount", - params, - ResponseSizeEstimate::new(50), - ) - .await + ) -> Result> { + let results = self + .parallel_call( + "eth_getTransactionCount", + params, + ResponseSizeEstimate::new(50), + ) + .await; + results.reduce_with_equality() } } diff --git a/lib/motoko/declarations/evm_rpc.mo b/lib/motoko/declarations/evm_rpc.mo index 9df2a356..12ae4198 100644 --- a/lib/motoko/declarations/evm_rpc.mo +++ b/lib/motoko/declarations/evm_rpc.mo @@ -31,6 +31,7 @@ module { to_block : ?BlockSpec; from_block : ?BlockSpec; }; + public type GetTransactionCountArgs = { address : Text; block : BlockSpec }; public type HttpOutcallError = { #IcError : { code : RejectionCode; message : Text }; #InvalidHttpJsonRpcResponse : { @@ -64,6 +65,10 @@ module { #Consistent : Result_3; #Inconsistent : [(RpcNodeProvider, Result_3)]; }; + public type MultiRpcResult_3 = { + #Consistent : Result_4; + #Inconsistent : [(RpcNodeProvider, Result_4)]; + }; public type MultiSource = { #Ethereum : ?[EthereumProvider]; #Sepolia : ?[SepoliaProvider]; @@ -103,10 +108,11 @@ module { public type Result = { #Ok : ?FeeHistory; #Err : RpcError }; public type Result_1 = { #Ok : Block; #Err : RpcError }; public type Result_2 = { #Ok : [LogEntry]; #Err : RpcError }; - public type Result_3 = { #Ok : ?TransactionReceipt; #Err : RpcError }; - public type Result_4 = { #Ok : SendRawTransactionResult; #Err : RpcError }; - public type Result_5 = { #Ok : Text; #Err : RpcError }; - public type Result_6 = { #Ok : Nat; #Err : RpcError }; + public type Result_3 = { #Ok : Nat; #Err : RpcError }; + public type Result_4 = { #Ok : ?TransactionReceipt; #Err : RpcError }; + public type Result_5 = { #Ok : SendRawTransactionResult; #Err : RpcError }; + public type Result_6 = { #Ok : Text; #Err : RpcError }; + public type Result_7 = { #Ok : Nat; #Err : RpcError }; public type RpcError = { #JsonRpcError : JsonRpcError; #ProviderError : ProviderError; @@ -161,19 +167,23 @@ module { BlockSpec, ) -> async MultiRpcResult; eth_get_logs : shared (MultiSource, GetLogsArgs) -> async MultiRpcResult_1; + eth_get_transaction_count : shared ( + MultiSource, + GetTransactionCountArgs, + ) -> async MultiRpcResult_2; eth_get_transaction_receipt : shared ( MultiSource, Blob, - ) -> async MultiRpcResult_2; - eth_send_raw_transaction : shared (MultiSource, Text) -> async Result_4; + ) -> async MultiRpcResult_3; + eth_send_raw_transaction : shared (MultiSource, Text) -> async Result_5; get_accumulated_cycle_count : shared query Nat64 -> async Nat; get_authorized : shared query Auth -> async [Text]; get_nodes_in_subnet : shared query () -> async Nat32; get_open_rpc_access : shared query () -> async Bool; get_providers : shared query () -> async [ProviderView]; register_provider : shared RegisterProvider -> async Nat64; - request : shared (Source, Text, Nat64) -> async Result_5; - request_cost : shared query (Source, Text, Nat64) -> async Result_6; + request : shared (Source, Text, Nat64) -> async Result_6; + request_cost : shared query (Source, Text, Nat64) -> async Result_7; set_nodes_in_subnet : shared Nat32 -> async (); set_open_rpc_access : shared Bool -> async (); unregister_provider : shared Nat64 -> async Bool; diff --git a/src/main.rs b/src/main.rs index 310c4efe..6d06710e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,11 @@ use cketh_common::eth_rpc::{ Block, BlockSpec, FeeHistory, GetLogsParam, HttpOutcallError, JsonRpcReply, LogEntry, ProviderError, RpcError, SendRawTransactionResult, }; +use cketh_common::eth_rpc_client::requests::GetTransactionCountParams; use cketh_common::eth_rpc_client::{providers::RpcNodeProvider, EthRpcClient}; use cketh_common::eth_rpc_client::{MultiCallError, RpcTransport}; use cketh_common::lifecycle::EvmNetwork; +use cketh_common::numeric::TransactionCount; use ic_canister_log::log; use ic_canisters_http_types::{ HttpRequest as AssetHttpRequest, HttpResponse as AssetHttpResponse, HttpResponseBuilder, @@ -136,6 +138,23 @@ pub async fn eth_get_transaction_receipt( .map(|option| option.map(|r| r.into())) } +#[ic_cdk_macros::update] +#[candid_method] +pub async fn eth_get_transaction_count( + source: MultiSource, + args: candid_types::GetTransactionCountArgs, +) -> MultiRpcResult { + let args: GetTransactionCountParams = match args.try_into() { + Ok(args) => args, + Err(err) => return RpcError::from(err).into(), + }; + let client = match get_rpc_client(source) { + Ok(client) => client, + Err(err) => return err.into(), + }; + wrap_result(client.eth_get_transaction_count(args).await) +} + #[ic_cdk_macros::update] #[candid_method] pub async fn eth_fee_history( diff --git a/src/types.rs b/src/types.rs index e9d1b25d..3d9e3513 100644 --- a/src/types.rs +++ b/src/types.rs @@ -350,7 +350,6 @@ pub mod candid_types { impl TryFrom for cketh_common::eth_rpc::GetLogsParam { type Error = DataFormatError; - fn try_from(value: GetLogsArgs) -> Result { Ok(cketh_common::eth_rpc::GetLogsParam { from_block: value.from_block.map(|x| x.into()).unwrap_or_default(), @@ -411,4 +410,25 @@ pub mod candid_types { } } } + + #[derive(Clone, Debug, CandidType, Deserialize)] + pub struct GetTransactionCountArgs { + pub address: String, + pub block: BlockSpec, + } + + impl TryFrom + for cketh_common::eth_rpc_client::requests::GetTransactionCountParams + { + type Error = DataFormatError; + fn try_from(value: GetTransactionCountArgs) -> Result { + Ok( + cketh_common::eth_rpc_client::requests::GetTransactionCountParams { + address: Address::from_str(&value.address) + .map_err(|_| DataFormatError::InvalidHex(value.address))?, + block: value.block.into(), + }, + ) + } + } }