diff --git a/etherlink/CHANGES_KERNEL.md b/etherlink/CHANGES_KERNEL.md index 396c103f412cd5848d40e64ea6d9f1261d02bfbc..aff63e1db7e0649f22b31b3250441006048459a2 100644 --- a/etherlink/CHANGES_KERNEL.md +++ b/etherlink/CHANGES_KERNEL.md @@ -4,6 +4,8 @@ ### Features +- Da fee is sent to sequencer pool address. (!12113) + ### Bug fixes - `BLOCKHASH` opcode now returns the actual block hash instead of `0x00..0`. (!12130) diff --git a/etherlink/kernel_evm/kernel/src/fees.rs b/etherlink/kernel_evm/kernel/src/fees.rs index 8619b16ba47376b548b4c99543f557c6247b209a..fba08095e7bee688a52958d5b37a97c5fa39d93a 100644 --- a/etherlink/kernel_evm/kernel/src/fees.rs +++ b/etherlink/kernel_evm/kernel/src/fees.rs @@ -15,7 +15,7 @@ //! //! Additionally, we charge a _data-availability_ fee, for each tx posted through L1. -use core::mem::size_of; +use crate::storage::read_sequencer_pool_address; use evm_execution::account_storage::{account_path, EthereumAccountStorage}; use evm_execution::handler::ExecutionOutcome; @@ -26,6 +26,8 @@ use tezos_ethereum::block::BlockFees; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_smart_rollup_host::runtime::Runtime; +use std::mem::size_of; + /// Minimum base fee per gas, set to 0.05Gwei. pub const MINIMUM_BASE_FEE_PER_GAS: u64 = 5 * 10_u64.pow(7); @@ -144,7 +146,25 @@ impl FeeUpdates { )); } - crate::storage::update_burned_fees(host, self.burn_amount)?; + let sequencer = match read_sequencer_pool_address(host) { + None => { + let burned_fee = self + .burn_amount + .saturating_add(self.compensate_sequencer_amount); + + crate::storage::update_burned_fees(host, burned_fee)?; + return Ok(()); + } + Some(sequencer) => { + crate::storage::update_burned_fees(host, self.burn_amount)?; + sequencer + } + }; + + let sequencer_account_path = account_path(&sequencer)?; + accounts + .get_or_create(host, &sequencer_account_path)? + .balance_add(host, self.compensate_sequencer_amount)?; Ok(()) } @@ -236,6 +256,7 @@ fn gas_as_u64(gas_for_fees: U256) -> Result { #[cfg(test)] mod tests { use super::*; + use crate::storage::store_sequencer_pool_address; use evm::{ExitReason, ExitSucceed}; use evm_execution::{ account_storage::{account_path, EthereumAccountStorage}, @@ -290,7 +311,7 @@ mod tests { } #[test] - fn apply_deducts_balance_from_user_and_burns() { + fn apply_updates_balances_no_sequencer() { // Arrange let mut host = MockHost::default(); let mut evm_account_storage = @@ -301,13 +322,62 @@ mod tests { set_balance(&mut host, &mut evm_account_storage, address, balance); let burn_amount = balance / 3; + let compensate_sequencer_amount = balance / 4; let fee_updates = FeeUpdates { overall_gas_used: U256::zero(), overall_gas_price: U256::zero(), - burn_amount: balance / 3, + burn_amount, charge_user_amount: balance / 2, - compensate_sequencer_amount: U256::zero(), + compensate_sequencer_amount, + }; + + // Act + let result = fee_updates.apply(&mut host, &mut evm_account_storage, address); + + // Assert + assert!(result.is_ok()); + let new_balance = get_balance(&mut host, &mut evm_account_storage, address); + assert_eq!(balance / 2, new_balance); + + let burned = crate::storage::read_burned_fees(&mut host); + + // no sequencer reward address set - so everything is burned + assert_eq!(burn_amount + compensate_sequencer_amount, burned); + } + + #[test] + fn apply_updates_balances_with_sequencer() { + // Arrange + let mut host = MockHost::default(); + let sequencer_address = + address_from_str("0123456789ABCDEF0123456789ABCDEF01234567"); + store_sequencer_pool_address(&mut host, sequencer_address).unwrap(); + + let mut evm_account_storage = + evm_execution::account_storage::init_account_storage().unwrap(); + + let address = address_from_str("af1276cbb260bb13deddb4209ae99ae6e497f446"); + let balance = U256::from(1000); + set_balance(&mut host, &mut evm_account_storage, address, balance); + + let sequencer_balance = U256::from(500); + set_balance( + &mut host, + &mut evm_account_storage, + sequencer_address, + sequencer_balance, + ); + + let burn_amount = balance / 3; + let compensate_sequencer_amount = balance / 4; + + let fee_updates = FeeUpdates { + overall_gas_used: U256::zero(), + overall_gas_price: U256::zero(), + burn_amount, + charge_user_amount: balance / 2, + compensate_sequencer_amount, }; // Act @@ -320,6 +390,13 @@ mod tests { let burned = crate::storage::read_burned_fees(&mut host); assert_eq!(burn_amount, burned); + + let sequencer_new_balance = + get_balance(&mut host, &mut evm_account_storage, sequencer_address); + assert_eq!( + sequencer_new_balance, + sequencer_balance + compensate_sequencer_amount + ); } #[test] diff --git a/etherlink/kernel_evm/kernel/src/storage.rs b/etherlink/kernel_evm/kernel/src/storage.rs index 7707864c9a1feb46be9fc2a6c7485564f9a4b0d3..0fccd1730656fad53cd35b8af3a837e500cc020f 100644 --- a/etherlink/kernel_evm/kernel/src/storage.rs +++ b/etherlink/kernel_evm/kernel/src/storage.rs @@ -65,6 +65,13 @@ const EVM_MINIMUM_BASE_FEE_PER_GAS: RefPath = RefPath::assert_from(b"/fees/minimum_base_fee_per_gas"); const EVM_DA_FEE: RefPath = RefPath::assert_from(b"/fees/da_fee_per_byte"); +/// The sequencer pool is the designated account that the data-availability fees are sent to. +/// +/// This may be updated by the governance mechanism over time. If it is not set, the data-availability +/// fees are instead burned. +pub const SEQUENCER_POOL_PATH: RefPath = + RefPath::assert_from(b"/fees/sequencer_pool_address"); + /// Path to the last L1 level seen. const EVM_L1_LEVEL: RefPath = RefPath::assert_from(b"/l1_level"); @@ -598,6 +605,25 @@ pub fn read_burned_fees(host: &mut impl Runtime) -> U256 { read_u256(host, path).unwrap_or_else(|_| U256::zero()) } +pub fn read_sequencer_pool_address(host: &impl Runtime) -> Option { + let mut bytes = [0; std::mem::size_of::()]; + let Ok(20) = host.store_read_slice(&SEQUENCER_POOL_PATH, 0, bytes.as_mut_slice()) else { + log!(host, Debug, "No sequencer pool address set"); + return None; + }; + Some(bytes.into()) +} + +#[cfg(test)] +pub fn store_sequencer_pool_address( + host: &mut impl Runtime, + address: H160, +) -> Result<(), Error> { + let bytes = address.to_fixed_bytes(); + host.store_write_all(&SEQUENCER_POOL_PATH, bytes.as_slice())?; + Ok(()) +} + pub fn store_timestamp_path( host: &mut Host, path: &OwnedPath,