@@ -61,7 +61,9 @@ use sp_runtime::{
6161 AccountIdLookup , BlakeTwo256 , Block as BlockT , DispatchInfoOf , Dispatchable , One ,
6262 PostDispatchInfoOf , UniqueSaturatedInto , Verify ,
6363 } ,
64- transaction_validity:: { TransactionSource , TransactionValidity , TransactionValidityError } ,
64+ transaction_validity:: {
65+ TransactionPriority , TransactionSource , TransactionValidity , TransactionValidityError ,
66+ } ,
6567} ;
6668use sp_std:: cmp:: Ordering ;
6769use sp_std:: prelude:: * ;
@@ -239,7 +241,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
239241 // `spec_version`, and `authoring_version` are the same between Wasm and native.
240242 // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
241243 // the compatible custom types.
242- spec_version : 364 ,
244+ spec_version : 365 ,
243245 impl_version : 1 ,
244246 apis : RUNTIME_API_VERSIONS ,
245247 transaction_version : 1 ,
@@ -1221,6 +1223,10 @@ impl<F: FindAuthor<u32>> FindAuthor<H160> for FindAuthorTruncated<F> {
12211223}
12221224
12231225const BLOCK_GAS_LIMIT : u64 = 75_000_000 ;
1226+ pub const NORMAL_DISPATCH_BASE_PRIORITY : TransactionPriority = 1 ;
1227+ pub const OPERATIONAL_DISPATCH_PRIORITY : TransactionPriority = 10_000_000_000 ;
1228+ const EVM_TRANSACTION_BASE_PRIORITY : TransactionPriority = NORMAL_DISPATCH_BASE_PRIORITY ;
1229+ const EVM_LOG_TARGET : & str = "runtime::ethereum" ;
12241230
12251231/// `WeightPerGas` is an approximate ratio of the amount of Weight per Gas.
12261232///
@@ -1384,6 +1390,35 @@ impl<B: BlockT> fp_rpc::ConvertTransaction<<B as BlockT>::Extrinsic> for Transac
13841390 }
13851391}
13861392
1393+ fn adjust_evm_priority_and_warn (
1394+ validity : & mut Option < TransactionValidity > ,
1395+ priority_fee : Option < U256 > ,
1396+ info : & H160 ,
1397+ ) {
1398+ if let Some ( Ok ( valid_transaction) ) = validity. as_mut ( ) {
1399+ let original_priority = valid_transaction. priority ;
1400+ valid_transaction. priority = EVM_TRANSACTION_BASE_PRIORITY ;
1401+
1402+ let has_priority_fee = priority_fee. is_some_and ( |fee| !fee. is_zero ( ) ) ;
1403+ if has_priority_fee {
1404+ log:: warn!(
1405+ target: EVM_LOG_TARGET ,
1406+ "Priority fee/tip from {:?} (max_priority_fee_per_gas: {:?}) is ignored for transaction ordering" ,
1407+ info,
1408+ priority_fee. unwrap_or_default( ) ,
1409+ ) ;
1410+ } else if original_priority > EVM_TRANSACTION_BASE_PRIORITY {
1411+ log:: warn!(
1412+ target: EVM_LOG_TARGET ,
1413+ "EVM transaction priority from {:?} reduced from {} to {}; priority tips are ignored for ordering" ,
1414+ info,
1415+ original_priority,
1416+ EVM_TRANSACTION_BASE_PRIORITY ,
1417+ ) ;
1418+ }
1419+ }
1420+ }
1421+
13871422impl fp_self_contained:: SelfContainedCall for RuntimeCall {
13881423 type SignedInfo = H160 ;
13891424
@@ -1408,7 +1443,21 @@ impl fp_self_contained::SelfContainedCall for RuntimeCall {
14081443 len : usize ,
14091444 ) -> Option < TransactionValidity > {
14101445 match self {
1411- RuntimeCall :: Ethereum ( call) => call. validate_self_contained ( info, dispatch_info, len) ,
1446+ RuntimeCall :: Ethereum ( call) => {
1447+ let priority_fee = match call {
1448+ pallet_ethereum:: Call :: transact { transaction } => match transaction {
1449+ EthereumTransaction :: EIP1559 ( tx) => Some ( tx. max_priority_fee_per_gas ) ,
1450+ EthereumTransaction :: EIP7702 ( tx) => Some ( tx. max_priority_fee_per_gas ) ,
1451+ _ => None ,
1452+ } ,
1453+ _ => None ,
1454+ } ;
1455+
1456+ let mut validity = call. validate_self_contained ( info, dispatch_info, len) ;
1457+ adjust_evm_priority_and_warn ( & mut validity, priority_fee, info) ;
1458+
1459+ validity
1460+ }
14121461 _ => None ,
14131462 }
14141463 }
@@ -2619,6 +2668,52 @@ fn test_into_substrate_balance_zero_value() {
26192668 assert_eq ! ( result, Some ( expected_substrate_balance) ) ;
26202669}
26212670
2671+ #[ test]
2672+ fn evm_priority_overrides_tip_to_base ( ) {
2673+ let mut validity: Option < TransactionValidity > =
2674+ Some ( Ok ( sp_runtime:: transaction_validity:: ValidTransaction {
2675+ priority : 99 ,
2676+ requires : vec ! [ ] ,
2677+ provides : vec ! [ ] ,
2678+ longevity : sp_runtime:: transaction_validity:: TransactionLongevity :: MAX ,
2679+ propagate : true ,
2680+ } ) ) ;
2681+
2682+ let signer = H160 :: repeat_byte ( 1 ) ;
2683+ adjust_evm_priority_and_warn ( & mut validity, Some ( U256 :: from ( 10 ) ) , & signer) ;
2684+
2685+ let adjusted_priority = validity
2686+ . as_ref ( )
2687+ . and_then ( |v| v. as_ref ( ) . ok ( ) )
2688+ . map ( |v| v. priority ) ;
2689+
2690+ assert_eq ! ( adjusted_priority, Some ( EVM_TRANSACTION_BASE_PRIORITY ) ) ;
2691+ }
2692+
2693+ #[ test]
2694+ fn evm_priority_cannot_overtake_unstake ( ) {
2695+ // Unstake is a normal-class extrinsic (priority = NORMAL_DISPATCH_BASE_PRIORITY).
2696+ let unstake_priority: TransactionPriority = NORMAL_DISPATCH_BASE_PRIORITY ;
2697+ let evm_priority: TransactionPriority = EVM_TRANSACTION_BASE_PRIORITY ;
2698+
2699+ // Clamp guarantees the EVM tx is never above the unstake priority.
2700+ assert ! ( evm_priority <= unstake_priority) ;
2701+
2702+ // If both arrive with equal priority, arrival order keeps unstake first.
2703+ let mut queue: Vec < ( & str , TransactionPriority , usize ) > = vec ! [
2704+ ( "unstake" , unstake_priority, 0 ) , // arrives first
2705+ ( "evm" , evm_priority, 1 ) , // arrives later
2706+ ] ;
2707+
2708+ queue. sort_by ( |a, b| {
2709+ b. 1 . cmp ( & a. 1 ) // higher priority first
2710+ . then_with ( || a. 2 . cmp ( & b. 2 ) ) // earlier arrival first when equal
2711+ } ) ;
2712+
2713+ let first = queue. first ( ) . map ( |entry| entry. 0 ) ;
2714+ assert_eq ! ( first, Some ( "unstake" ) ) ;
2715+ }
2716+
26222717#[ test]
26232718fn test_into_evm_balance_valid ( ) {
26242719 // Valid conversion from Substrate to EVM
0 commit comments