diff --git a/operator/runtime/mainnet/src/lib.rs b/operator/runtime/mainnet/src/lib.rs index 85527ea22..921f4cc38 100644 --- a/operator/runtime/mainnet/src/lib.rs +++ b/operator/runtime/mainnet/src/lib.rs @@ -1122,6 +1122,8 @@ impl_runtime_apis! { access_list: Option)>>, authorization_list: Option, ) -> Result { + use pallet_evm::GasWeightMapping as _; + let config = if estimate { let mut config = ::config().clone(); config.estimate = true; @@ -1132,6 +1134,16 @@ impl_runtime_apis! { let is_transactional = false; let validate = true; + // Estimated encoded transaction size for create (EIP1559-style) for PoV validation. + // Base: from 20 + value 32 + gas_limit 32 + nonce 32 + action variant 1 + chain_id 8 + signature 65 = 190. + let mut estimated_transaction_len = data.len() + + 190 + + if max_fee_per_gas.is_some() { 32 } else { 0 } + + if max_priority_fee_per_gas.is_some() { 32 } else { 0 }; + if let Some(ref list) = access_list { + estimated_transaction_len += list.encode().len(); + } + let gas_limit = if gas_limit > U256::from(u64::MAX) { u64::MAX } else { @@ -1141,8 +1153,14 @@ impl_runtime_apis! { let without_base_extrinsic_weight = true; let weight_limit = ::GasWeightMapping::gas_to_weight( gas_limit, - without_base_extrinsic_weight + without_base_extrinsic_weight, ); + let (weight_limit, proof_size_base_cost) = + if weight_limit.proof_size() > 0 { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } else { + (None, None) + }; #[allow(clippy::or_fun_call)] ::Runner::create( @@ -1157,10 +1175,11 @@ impl_runtime_apis! { authorization_list.unwrap_or_default(), is_transactional, validate, - Some(weight_limit), - None, + weight_limit, + proof_size_base_cost, config.as_ref().unwrap_or(::config()), - ).map_err(|err| err.error.into()) + ) + .map_err(|err| err.error.into()) } fn current_transaction_statuses() -> Option> { diff --git a/operator/runtime/stagenet/src/lib.rs b/operator/runtime/stagenet/src/lib.rs index 9ac518f11..5a9269d64 100644 --- a/operator/runtime/stagenet/src/lib.rs +++ b/operator/runtime/stagenet/src/lib.rs @@ -1124,6 +1124,8 @@ impl_runtime_apis! { access_list: Option)>>, authorization_list: Option, ) -> Result { + use pallet_evm::GasWeightMapping as _; + let config = if estimate { let mut config = ::config().clone(); config.estimate = true; @@ -1134,6 +1136,16 @@ impl_runtime_apis! { let is_transactional = false; let validate = true; + // Estimated encoded transaction size for create (EIP1559-style) for PoV validation. + // Base: from 20 + value 32 + gas_limit 32 + nonce 32 + action variant 1 + chain_id 8 + signature 65 = 190. + let mut estimated_transaction_len = data.len() + + 190 + + if max_fee_per_gas.is_some() { 32 } else { 0 } + + if max_priority_fee_per_gas.is_some() { 32 } else { 0 }; + if let Some(ref list) = access_list { + estimated_transaction_len += list.encode().len(); + } + let gas_limit = if gas_limit > U256::from(u64::MAX) { u64::MAX } else { @@ -1143,8 +1155,14 @@ impl_runtime_apis! { let without_base_extrinsic_weight = true; let weight_limit = ::GasWeightMapping::gas_to_weight( gas_limit, - without_base_extrinsic_weight + without_base_extrinsic_weight, ); + let (weight_limit, proof_size_base_cost) = + if weight_limit.proof_size() > 0 { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } else { + (None, None) + }; #[allow(clippy::or_fun_call)] ::Runner::create( @@ -1159,10 +1177,11 @@ impl_runtime_apis! { authorization_list.unwrap_or_default(), is_transactional, validate, - Some(weight_limit), - None, + weight_limit, + proof_size_base_cost, config.as_ref().unwrap_or(::config()), - ).map_err(|err| err.error.into()) + ) + .map_err(|err| err.error.into()) } fn current_transaction_statuses() -> Option> { diff --git a/operator/runtime/stagenet/tests/lib.rs b/operator/runtime/stagenet/tests/lib.rs index 93ee9058f..2cef1326e 100644 --- a/operator/runtime/stagenet/tests/lib.rs +++ b/operator/runtime/stagenet/tests/lib.rs @@ -77,3 +77,16 @@ fn validate_transaction_fails_on_filtered_call() { ); }); } + +/// Sanity-check for the create transaction size estimation used in EthereumRuntimeRPCApi::create +/// for proof_size_base_cost (see Frontier template). Ensures the base constant stays correct. +#[test] +fn ethereum_create_proof_size_base_cost_estimation_constants() { + // Base size for create tx (EIP1559-style): from 20 + value 32 + gas_limit 32 + nonce 32 + // + action variant 1 + chain_id 8 + signature 65 = 190. Optional: +32 per max_fee/max_priority_fee. + const BASE: usize = 20 + 32 + 32 + 32 + 1 + 8 + 65; + assert_eq!( + BASE, 190, + "create proof_size_base_cost base must match Frontier template" + ); +} diff --git a/operator/runtime/testnet/src/lib.rs b/operator/runtime/testnet/src/lib.rs index 554f337e7..774f10a99 100644 --- a/operator/runtime/testnet/src/lib.rs +++ b/operator/runtime/testnet/src/lib.rs @@ -1122,6 +1122,8 @@ impl_runtime_apis! { access_list: Option)>>, authorization_list: Option, ) -> Result { + use pallet_evm::GasWeightMapping as _; + let config = if estimate { let mut config = ::config().clone(); config.estimate = true; @@ -1132,6 +1134,16 @@ impl_runtime_apis! { let is_transactional = false; let validate = true; + // Estimated encoded transaction size for create (EIP1559-style) for PoV validation. + // Base: from 20 + value 32 + gas_limit 32 + nonce 32 + action variant 1 + chain_id 8 + signature 65 = 190. + let mut estimated_transaction_len = data.len() + + 190 + + if max_fee_per_gas.is_some() { 32 } else { 0 } + + if max_priority_fee_per_gas.is_some() { 32 } else { 0 }; + if let Some(ref list) = access_list { + estimated_transaction_len += list.encode().len(); + } + let gas_limit = if gas_limit > U256::from(u64::MAX) { u64::MAX } else { @@ -1141,8 +1153,14 @@ impl_runtime_apis! { let without_base_extrinsic_weight = true; let weight_limit = ::GasWeightMapping::gas_to_weight( gas_limit, - without_base_extrinsic_weight + without_base_extrinsic_weight, ); + let (weight_limit, proof_size_base_cost) = + if weight_limit.proof_size() > 0 { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } else { + (None, None) + }; #[allow(clippy::or_fun_call)] ::Runner::create( @@ -1157,10 +1175,11 @@ impl_runtime_apis! { authorization_list.unwrap_or_default(), is_transactional, validate, - Some(weight_limit), - None, + weight_limit, + proof_size_base_cost, config.as_ref().unwrap_or(::config()), - ).map_err(|err| err.error.into()) + ) + .map_err(|err| err.error.into()) } fn current_transaction_statuses() -> Option> { diff --git a/test/moonwall/suites/dev/stagenet/gas/test-gas-contract-creation.ts b/test/moonwall/suites/dev/stagenet/gas/test-gas-contract-creation.ts index 46dfcdd56..cfab7e4ac 100644 --- a/test/moonwall/suites/dev/stagenet/gas/test-gas-contract-creation.ts +++ b/test/moonwall/suites/dev/stagenet/gas/test-gas-contract-creation.ts @@ -16,7 +16,7 @@ describeSuite({ account: ALITH_ADDRESS, data: bytecode }) - ).to.equal(210541n); + ).to.equal(156082n); } }); } diff --git a/test/moonwall/suites/dev/stagenet/gas/test-gas-estimation-contracts.ts b/test/moonwall/suites/dev/stagenet/gas/test-gas-estimation-contracts.ts index 3ebfcec94..fd613ebcd 100644 --- a/test/moonwall/suites/dev/stagenet/gas/test-gas-estimation-contracts.ts +++ b/test/moonwall/suites/dev/stagenet/gas/test-gas-estimation-contracts.ts @@ -40,13 +40,13 @@ describeSuite({ data: bytecode, gasPrice: 0n }); - expect(result).to.equal(255341n); + expect(result).to.equal(172902n); const result2 = await context.viem().estimateGas({ account: ALITH_ADDRESS, data: bytecode }); - expect(result2).to.equal(255341n); + expect(result2).to.equal(172902n); } }); @@ -129,7 +129,7 @@ describeSuite({ account: PRECOMPILE_BATCH_ADDRESS, data: bytecode }) - ).toBe(210541n); + ).toBe(156082n); } });