@@ -37,7 +37,7 @@ use crate::{
3737} ;
3838use alloy_dyn_abi:: TypedData ;
3939use alloy_eips:: { BlockId , BlockNumberOrTag } ;
40- use alloy_primitives:: { hex , Address , B256 , U64 , U256 } ;
40+ use alloy_primitives:: { Address , B256 , U64 , U256 } ;
4141use alloy_rpc_types:: {
4242 Filter , TransactionRequest ,
4343 anvil:: { Forking , Metadata as AnvilMetadata , MineOptions , NodeEnvironment , NodeInfo } ,
@@ -1887,58 +1887,26 @@ async fn create_online_client(
18871887 ) ) ) ;
18881888 } ;
18891889
1890- // In fork mode, fetch metadata from remote RPC to avoid Tokio runtime conflicts
1891- // Note: Remote metadata may not have ReviveApi, so we skip block processing in fork mode
1892- if let Some ( fork_url) = & substrate_service. fork_url {
1893- use jsonrpsee:: core:: client:: ClientT ;
1894- use subxt:: ext:: jsonrpsee:: core:: params:: ArrayParams as RpcParams ;
1895-
1896- let http_client = jsonrpsee:: http_client:: HttpClientBuilder :: default ( )
1897- . max_request_size ( u32:: MAX )
1898- . max_response_size ( u32:: MAX )
1899- . request_timeout ( std:: time:: Duration :: from_secs ( 30 ) )
1900- . build ( fork_url)
1901- . map_err ( |e| Error :: InternalError ( format ! ( "Failed to build HTTP client: {e}" ) ) ) ?;
1902-
1903- let runtime_version: polkadot_sdk:: sp_version:: RuntimeVersion = http_client
1904- . request ( "state_getRuntimeVersion" , RpcParams :: new ( ) )
1905- . await
1906- . map_err ( |e| Error :: InternalError ( format ! ( "Failed to fetch runtime version: {e}" ) ) ) ?;
1890+ // Fetch runtime version - this works without triggering lazy loading
1891+ let Ok ( runtime_version) = substrate_service. client . runtime_version_at ( genesis_hash) else {
1892+ return Err ( Error :: InternalError ( "Runtime version not found" . to_string ( ) ) ) ;
1893+ } ;
19071894
1908- let subxt_runtime_version = SubxtRuntimeVersion {
1909- spec_version : runtime_version. spec_version ,
1910- transaction_version : runtime_version. transaction_version ,
1911- } ;
1895+ let subxt_runtime_version = SubxtRuntimeVersion {
1896+ spec_version : runtime_version. spec_version ,
1897+ transaction_version : runtime_version. transaction_version ,
1898+ } ;
19121899
1913- let metadata_hex: String = http_client
1914- . request ( "state_getMetadata" , RpcParams :: new ( ) )
1900+ // In fork mode, use OnlineClient::from_rpc_client() which fetches metadata from RPC automatically
1901+ // In normal mode, use local client metadata to avoid unnecessary RPC calls
1902+ if substrate_service. backend . rpc ( ) . is_some ( ) {
1903+ // Fork mode: Let OnlineClient fetch metadata from the remote RPC
1904+ // This avoids triggering the lazy loading backend during initialization
1905+ OnlineClient :: < SrcChainConfig > :: from_rpc_client ( rpc_client)
19151906 . await
1916- . map_err ( |e| Error :: InternalError ( format ! ( "Failed to fetch metadata: {e}" ) ) ) ?;
1917-
1918- let metadata_bytes = hex:: decode ( metadata_hex. trim_start_matches ( "0x" ) )
1919- . map_err ( |e| Error :: InternalError ( format ! ( "Failed to decode metadata: {e}" ) ) ) ?;
1920-
1921- let subxt_metadata = SubxtMetadata :: decode ( & mut & metadata_bytes[ ..] )
1922- . map_err ( |e| Error :: InternalError ( format ! ( "Failed to decode metadata: {e}" ) ) ) ?;
1923-
1924- OnlineClient :: < SrcChainConfig > :: from_rpc_client_with (
1925- genesis_hash,
1926- subxt_runtime_version,
1927- subxt_metadata,
1928- rpc_client,
1929- )
1930- . map_err ( |err| Error :: InternalError ( format ! ( "Failed to initialize online client: {err}" ) ) )
1907+ . map_err ( |err| Error :: InternalError ( format ! ( "Failed to initialize online client from RPC: {err}" ) ) )
19311908 } else {
1932- // Normal mode: use local runtime metadata
1933- let Ok ( runtime_version) = substrate_service. client . runtime_version_at ( genesis_hash) else {
1934- return Err ( Error :: InternalError ( "Runtime version not found" . to_string ( ) ) ) ;
1935- } ;
1936-
1937- let subxt_runtime_version = SubxtRuntimeVersion {
1938- spec_version : runtime_version. spec_version ,
1939- transaction_version : runtime_version. transaction_version ,
1940- } ;
1941-
1909+ // Normal mode: Use local client runtime API (no lazy loading involved)
19421910 let Ok ( supported_metadata_versions) =
19431911 substrate_service. client . runtime_api ( ) . metadata_versions ( genesis_hash)
19441912 else {
@@ -1954,7 +1922,9 @@ async fn create_online_client(
19541922 . runtime_api ( )
19551923 . metadata_at_version ( genesis_hash, latest_metadata_version)
19561924 . map_err ( |_| Error :: InternalError ( "Failed to get runtime API" . to_string ( ) ) ) ?
1957- . ok_or_else ( || Error :: InternalError ( format ! ( "Metadata not found for version {latest_metadata_version}" ) ) ) ?;
1925+ . ok_or_else ( || {
1926+ Error :: InternalError ( format ! ( "Metadata not found for version {latest_metadata_version}" ) )
1927+ } ) ?;
19581928
19591929 let subxt_metadata = SubxtMetadata :: decode ( & mut ( * opaque_metadata) . as_slice ( ) )
19601930 . map_err ( |_| Error :: InternalError ( "Unable to decode metadata" . to_string ( ) ) ) ?;
@@ -1978,22 +1948,57 @@ async fn create_revive_rpc_client(
19781948 keep_latest_n_blocks : Option < usize > ,
19791949 in_fork_mode : bool ,
19801950) -> Result < Option < EthRpcClient > > {
1981- // In fork mode, check if ReviveApi exists in metadata before proceeding
1951+ // In fork mode, try to create the EthRpcClient. If it fails (e.g., ReviveApi not available),
1952+ // return None to indicate limited mode without EVM RPC functionality.
1953+ // This is more robust than checking metadata, which may not include runtime API information.
19821954 if in_fork_mode {
1983- let metadata = api. metadata ( ) ;
1984- let has_revive_api = metadata. runtime_api_traits ( ) . any ( |trait_metadata| {
1985- trait_metadata. name ( ) == "ReviveApi"
1986- } ) ;
1987-
1988- if !has_revive_api {
1989- tracing:: warn!(
1990- "ReviveApi runtime trait not found in metadata. \
1991- Forking in limited mode - EVM RPC functionality will not be available."
1992- ) ;
1993- return Ok ( None ) ;
1955+ // Try to create the client - if ReviveApi doesn't exist, this will fail
1956+ match create_revive_client_impl (
1957+ api. clone ( ) ,
1958+ rpc_client. clone ( ) ,
1959+ rpc. clone ( ) ,
1960+ block_provider. clone ( ) ,
1961+ task_spawn_handle. clone ( ) ,
1962+ keep_latest_n_blocks,
1963+ )
1964+ . await
1965+ {
1966+ Ok ( client) => {
1967+ tracing:: info!( "ReviveApi available - EVM RPC functionality enabled" ) ;
1968+ return Ok ( Some ( client) ) ;
1969+ }
1970+ Err ( e) => {
1971+ tracing:: warn!(
1972+ "ReviveApi not available in forked chain: {}. \
1973+ Forking in limited mode - EVM RPC functionality will not be available.",
1974+ e
1975+ ) ;
1976+ return Ok ( None ) ;
1977+ }
19941978 }
19951979 }
19961980
1981+ // Normal mode: create the client and return errors if it fails
1982+ create_revive_client_impl (
1983+ api,
1984+ rpc_client,
1985+ rpc,
1986+ block_provider,
1987+ task_spawn_handle,
1988+ keep_latest_n_blocks,
1989+ )
1990+ . await
1991+ . map ( Some )
1992+ }
1993+
1994+ async fn create_revive_client_impl (
1995+ api : OnlineClient < SrcChainConfig > ,
1996+ rpc_client : RpcClient ,
1997+ rpc : LegacyRpcMethods < SrcChainConfig > ,
1998+ block_provider : SubxtBlockInfoProvider ,
1999+ task_spawn_handle : SpawnTaskHandle ,
2000+ keep_latest_n_blocks : Option < usize > ,
2001+ ) -> Result < EthRpcClient > {
19972002 let pool = SqlitePoolOptions :: new ( )
19982003 . max_connections ( 1 )
19992004 // see sqlite in-memory issue: https://github.com/launchbadge/sqlx/issues/2510
@@ -2024,23 +2029,20 @@ async fn create_revive_rpc_client(
20242029 . await
20252030 . map_err ( Error :: from) ?;
20262031
2027- // Skip block subscription task in fork mode since it requires ReviveApi
2028- if !in_fork_mode {
2029- // Capacity is chosen using random.org
2030- eth_rpc_client. set_block_notifier ( Some ( tokio:: sync:: broadcast:: channel :: < H256 > ( 50 ) . 0 ) ) ;
2031- let eth_rpc_client_clone = eth_rpc_client. clone ( ) ;
2032- task_spawn_handle. spawn ( "block-subscription" , "None" , async move {
2033- let eth_rpc_client = eth_rpc_client_clone;
2034- let best_future =
2035- eth_rpc_client. subscribe_and_cache_new_blocks ( SubscriptionType :: BestBlocks ) ;
2036- let finalized_future =
2037- eth_rpc_client. subscribe_and_cache_new_blocks ( SubscriptionType :: FinalizedBlocks ) ;
2038- let res = tokio:: try_join!( best_future, finalized_future) . map ( |_| ( ) ) ;
2039- if let Err ( err) = res {
2040- panic ! ( "Block subscription task failed: {err:?}" )
2041- }
2042- } ) ;
2043- }
2032+ // Capacity is chosen using random.org
2033+ eth_rpc_client. set_block_notifier ( Some ( tokio:: sync:: broadcast:: channel :: < H256 > ( 50 ) . 0 ) ) ;
2034+ let eth_rpc_client_clone = eth_rpc_client. clone ( ) ;
2035+ task_spawn_handle. spawn ( "block-subscription" , "None" , async move {
2036+ let eth_rpc_client = eth_rpc_client_clone;
2037+ let best_future =
2038+ eth_rpc_client. subscribe_and_cache_new_blocks ( SubscriptionType :: BestBlocks ) ;
2039+ let finalized_future =
2040+ eth_rpc_client. subscribe_and_cache_new_blocks ( SubscriptionType :: FinalizedBlocks ) ;
2041+ let res = tokio:: try_join!( best_future, finalized_future) . map ( |_| ( ) ) ;
2042+ if let Err ( err) = res {
2043+ panic ! ( "Block subscription task failed: {err:?}" )
2044+ }
2045+ } ) ;
20442046
2045- Ok ( Some ( eth_rpc_client) )
2047+ Ok ( eth_rpc_client)
20462048}
0 commit comments