-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(gas): amortize evm transaction fixed cost over bundle #442
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,23 +33,23 @@ use super::polygon::Polygon; | |
// see: https://github.com/eth-infinitism/bundler/blob/main/packages/sdk/src/calcPreVerificationGas.ts | ||
#[derive(Clone, Copy, Debug)] | ||
struct GasOverheads { | ||
fixed: U256, | ||
bundle_transaction_gas_buffer: U256, | ||
transaction_gas_overhead: U256, | ||
per_user_op: U256, | ||
per_user_op_word: U256, | ||
zero_byte: U256, | ||
non_zero_byte: U256, | ||
bundle_size: U256, | ||
dancoombs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
impl Default for GasOverheads { | ||
fn default() -> Self { | ||
Self { | ||
fixed: 21000.into(), | ||
per_user_op: 18300.into(), | ||
bundle_transaction_gas_buffer: 5_000.into(), // Entrypoint requires a buffer over the user operation gas limits in the bundle transaction | ||
transaction_gas_overhead: 21_000.into(), // The fixed gas overhead for any EVM transaction | ||
per_user_op: 18_300.into(), | ||
per_user_op_word: 4.into(), | ||
zero_byte: 4.into(), | ||
non_zero_byte: 16.into(), | ||
bundle_size: 1.into(), | ||
} | ||
} | ||
} | ||
|
@@ -74,7 +74,7 @@ pub async fn calc_pre_verification_gas<P: Provider>( | |
provider: Arc<P>, | ||
chain_id: u64, | ||
) -> anyhow::Result<U256> { | ||
let static_gas = calc_static_pre_verification_gas(full_op); | ||
let static_gas = calc_static_pre_verification_gas(full_op, GasOverheads::default(), true); | ||
let dynamic_gas = match chain_id { | ||
_ if ARBITRUM_CHAIN_IDS.contains(&chain_id) => { | ||
provider | ||
|
@@ -94,13 +94,31 @@ pub async fn calc_pre_verification_gas<P: Provider>( | |
Ok(static_gas + dynamic_gas) | ||
} | ||
|
||
/// Compute the gas limit for the bundle composed of the given user operations | ||
pub fn bundle_gas_limit<'a, I>(iter_ops: I, chain_id: u64) -> U256 | ||
where | ||
I: Iterator<Item = &'a UserOperation>, | ||
{ | ||
let ov = GasOverheads::default(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. somewhat unnecessary duplication since we are creating the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're doing this here, we might as well do the same thing in Either we make it part of both the interfaces, or we instantiate in each. Again I eventually want these to look like |
||
iter_ops | ||
.map(|op| user_operation_gas_limit(op, chain_id, false)) | ||
.fold( | ||
ov.bundle_transaction_gas_buffer + ov.transaction_gas_overhead, | ||
|acc, c| acc + c, | ||
) | ||
} | ||
|
||
/// Returns the gas limit for the user operation that applies to bundle transaction's limit | ||
pub fn user_operation_gas_limit(uo: &UserOperation, chain_id: u64) -> U256 { | ||
pub fn user_operation_gas_limit( | ||
uo: &UserOperation, | ||
chain_id: u64, | ||
include_fixed_gas_overhead: bool, | ||
) -> U256 { | ||
// On some chains (OP bedrock, Arbitrum) the L1 gas fee is charged via pre_verification_gas | ||
// but this not part of the execution gas limit of the transaction. | ||
// In such cases we only consider the static portion of the pre_verification_gas in the gas limit. | ||
let pvg = if OP_BEDROCK_CHAIN_IDS.contains(&chain_id) | ARBITRUM_CHAIN_IDS.contains(&chain_id) { | ||
calc_static_pre_verification_gas(uo) | ||
calc_static_pre_verification_gas(uo, GasOverheads::default(), include_fixed_gas_overhead) | ||
} else { | ||
uo.pre_verification_gas | ||
}; | ||
|
@@ -115,8 +133,11 @@ pub fn user_operation_max_gas_cost(uo: &UserOperation) -> U256 { | |
* (uo.pre_verification_gas + uo.call_gas_limit + uo.verification_gas_limit * mul) | ||
} | ||
|
||
fn calc_static_pre_verification_gas(op: &UserOperation) -> U256 { | ||
let ov = GasOverheads::default(); | ||
fn calc_static_pre_verification_gas( | ||
op: &UserOperation, | ||
ov: GasOverheads, | ||
include_fixed_gas_overhead: bool, | ||
) -> U256 { | ||
let encoded_op = op.clone().encode(); | ||
let length_in_words = encoded_op.len() / 32; // size of packed user op is always a multiple of 32 bytes | ||
let call_data_cost: U256 = encoded_op | ||
|
@@ -131,10 +152,14 @@ fn calc_static_pre_verification_gas(op: &UserOperation) -> U256 { | |
.reduce(|a, b| a + b) | ||
.unwrap_or_default(); | ||
|
||
ov.fixed / ov.bundle_size | ||
+ call_data_cost | ||
call_data_cost | ||
+ ov.per_user_op | ||
+ ov.per_user_op_word * length_in_words | ||
+ (if include_fixed_gas_overhead { | ||
ov.transaction_gas_overhead | ||
} else { | ||
0.into() | ||
}) | ||
} | ||
|
||
fn verification_gas_limit_multiplier(uo: &UserOperation) -> u64 { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to get these from some common place? Tests will break if we ever modify these values.
These are the sorts of things I want to move to the
ChainSpec
config class in the future.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to exposing the relevant fields of
GasOverheads
, it's not great but just avoiding a major refactor for now.