@@ -15,7 +15,7 @@ use crate::{
1515 in_mem_rpc:: InMemoryRpcClient ,
1616 mining_engine:: MiningEngine ,
1717 service:: {
18- BackendError , BackendWithOverlay , Client , Service ,
18+ BackendError , BackendWithOverlay , Client , Service , TransactionPoolHandle ,
1919 storage:: {
2020 AccountType , ByteCodeType , CodeInfo , ContractInfo , ReviveAccountInfo ,
2121 SystemAccountInfo ,
@@ -26,12 +26,13 @@ use crate::{
2626} ;
2727use alloy_eips:: { BlockId , BlockNumberOrTag } ;
2828use alloy_primitives:: { Address , B256 , U64 , U256 } ;
29- use alloy_rpc_types:: { Filter , TransactionRequest , anvil:: MineOptions } ;
29+ use alloy_rpc_types:: { Filter , TransactionRequest , anvil:: MineOptions , txpool :: TxpoolStatus } ;
3030use alloy_serde:: WithOtherFields ;
3131use anvil_core:: eth:: { EthRequest , Params as MineParams } ;
3232use anvil_rpc:: response:: ResponseResult ;
33- use codec:: { Decode , Encode } ;
33+ use codec:: { Decode , DecodeLimit , Encode } ;
3434use futures:: { StreamExt , channel:: mpsc} ;
35+ use indexmap:: IndexMap ;
3536use pallet_revive_eth_rpc:: {
3637 BlockInfoProvider , EthRpcError , ReceiptExtractor , ReceiptProvider , SubxtBlockInfoProvider ,
3738 client:: { Client as EthRpcClient , ClientError , SubscriptionType } ,
@@ -48,7 +49,7 @@ use polkadot_sdk::{
4849 parachains_common:: { AccountId , Hash , Nonce } ,
4950 polkadot_sdk_frame:: runtime:: types_common:: OpaqueBlock ,
5051 sc_client_api:: HeaderBackend ,
51- sc_service:: SpawnTaskHandle ,
52+ sc_service:: { InPoolTransaction , SpawnTaskHandle , TransactionPool } ,
5253 sp_api:: { Metadata , ProvideRuntimeApi } ,
5354 sp_arithmetic:: Permill ,
5455 sp_blockchain:: Info ,
@@ -57,14 +58,15 @@ use polkadot_sdk::{
5758} ;
5859use sqlx:: sqlite:: SqlitePoolOptions ;
5960use std:: { collections:: HashSet , sync:: Arc , time:: Duration } ;
60- use substrate_runtime:: Balance ;
61+ use substrate_runtime:: { Balance , RuntimeCall , UncheckedExtrinsic } ;
6162use subxt:: {
6263 Metadata as SubxtMetadata , OnlineClient , backend:: rpc:: RpcClient ,
6364 client:: RuntimeVersion as SubxtRuntimeVersion , config:: substrate:: H256 ,
6465 ext:: subxt_rpcs:: LegacyRpcMethods , utils:: H160 ,
6566} ;
6667
6768pub const CLIENT_VERSION : & str = concat ! ( "anvil-polkadot/v" , env!( "CARGO_PKG_VERSION" ) ) ;
69+ const MAX_EXTRINSIC_DEPTH : u32 = 256 ;
6870
6971pub struct Wallet {
7072 accounts : Vec < Account > ,
@@ -81,6 +83,7 @@ pub struct ApiServer {
8183 wallet : Wallet ,
8284 snapshot_manager : SnapshotManager ,
8385 impersonation_manager : ImpersonationManager ,
86+ tx_pool : Arc < TransactionPoolHandle > ,
8487}
8588
8689impl ApiServer {
@@ -117,6 +120,7 @@ impl ApiServer {
117120 eth_rpc_client,
118121 snapshot_manager,
119122 impersonation_manager,
123+ tx_pool : substrate_service. tx_pool . clone ( ) ,
120124 wallet : Wallet {
121125 accounts : vec ! [
122126 Account :: from( subxt_signer:: eth:: dev:: baltathar( ) ) ,
@@ -283,6 +287,19 @@ impl ApiServer {
283287 node_info ! ( "eth_getLogs" ) ;
284288 self . get_logs ( filter) . await . to_rpc_result ( )
285289 }
290+ //------- Transaction Pool ---------
291+ EthRequest :: TxPoolStatus ( _) => {
292+ node_info ! ( "txpool_status" ) ;
293+ self . txpool_status ( ) . await . to_rpc_result ( )
294+ }
295+ EthRequest :: DropAllTransactions ( ) => {
296+ node_info ! ( "anvil_dropAllTransactions" ) ;
297+ self . anvil_drop_all_transactions ( ) . await . to_rpc_result ( )
298+ }
299+ EthRequest :: DropTransaction ( eth_hash) => {
300+ node_info ! ( "anvil_dropTransaction" ) ;
301+ self . anvil_drop_transaction ( eth_hash) . await . to_rpc_result ( )
302+ }
286303 _ => Err :: < ( ) , _ > ( Error :: RpcUnimplemented ) . to_rpc_result ( ) ,
287304 } ;
288305
@@ -1031,6 +1048,82 @@ impl ApiServer {
10311048
10321049 Ok ( ( ) )
10331050 }
1051+
1052+ /// Returns transaction pool status
1053+ async fn txpool_status ( & self ) -> Result < TxpoolStatus > {
1054+ let pool_status = self . tx_pool . status ( ) ;
1055+ Ok ( TxpoolStatus { pending : pool_status. ready as u64 , queued : pool_status. future as u64 } )
1056+ }
1057+
1058+ /// Drop all transactions from pool
1059+ async fn anvil_drop_all_transactions ( & self ) -> Result < ( ) > {
1060+ let ready_txs = self . tx_pool . ready ( ) ;
1061+ let future_txs = self . tx_pool . futures ( ) ;
1062+
1063+ let mut invalid_txs = IndexMap :: new ( ) ;
1064+
1065+ for tx in ready_txs {
1066+ invalid_txs. insert ( * tx. hash ( ) , None ) ;
1067+ }
1068+
1069+ for tx in future_txs {
1070+ invalid_txs. insert ( * tx. hash ( ) , None ) ;
1071+ }
1072+
1073+ self . tx_pool . report_invalid ( None , invalid_txs) . await ;
1074+
1075+ Ok ( ( ) )
1076+ }
1077+
1078+ /// Drop a specific transaction from the pool by its ETH hash
1079+ async fn anvil_drop_transaction ( & self , eth_hash : B256 ) -> Result < Option < B256 > > {
1080+ // Search in ready transactions
1081+ for tx in self . tx_pool . ready ( ) {
1082+ if transaction_matches_eth_hash ( tx. data ( ) , eth_hash) {
1083+ let mut invalid_txs = IndexMap :: new ( ) ;
1084+ invalid_txs. insert ( * tx. hash ( ) , None ) ;
1085+ self . tx_pool . report_invalid ( None , invalid_txs) . await ;
1086+ return Ok ( Some ( eth_hash) ) ;
1087+ }
1088+ }
1089+
1090+ // Search in future transactions
1091+ for tx in self . tx_pool . futures ( ) {
1092+ if transaction_matches_eth_hash ( tx. data ( ) , eth_hash) {
1093+ let mut invalid_txs = IndexMap :: new ( ) ;
1094+ invalid_txs. insert ( * tx. hash ( ) , None ) ;
1095+ self . tx_pool . report_invalid ( None , invalid_txs) . await ;
1096+ return Ok ( Some ( eth_hash) ) ;
1097+ }
1098+ }
1099+
1100+ // Transaction not found
1101+ Ok ( None )
1102+ }
1103+ }
1104+
1105+ /// Helper function to check if transaction matches ETH hash
1106+ fn transaction_matches_eth_hash (
1107+ tx_data : & Arc < polkadot_sdk:: sp_runtime:: OpaqueExtrinsic > ,
1108+ target_eth_hash : B256 ,
1109+ ) -> bool {
1110+ let encoded = tx_data. encode ( ) ;
1111+ let Ok ( ext) =
1112+ UncheckedExtrinsic :: decode_all_with_depth_limit ( MAX_EXTRINSIC_DEPTH , & mut & encoded[ ..] )
1113+ else {
1114+ return false ;
1115+ } ;
1116+
1117+ let polkadot_sdk:: sp_runtime:: generic:: UncheckedExtrinsic {
1118+ function : RuntimeCall :: Revive ( polkadot_sdk:: pallet_revive:: Call :: eth_transact { payload } ) ,
1119+ ..
1120+ } = ext. 0
1121+ else {
1122+ return false ;
1123+ } ;
1124+
1125+ let tx_eth_hash = keccak_256 ( & payload) ;
1126+ B256 :: from_slice ( & tx_eth_hash) == target_eth_hash
10341127}
10351128
10361129fn new_contract_info ( address : & Address , code_hash : H256 , nonce : Nonce ) -> ContractInfo {
0 commit comments