Skip to content

Commit 89daba1

Browse files
committed
Fix L1 -> L2 mint value divergence and warm precompiles
1 parent 5922cdd commit 89daba1

File tree

2 files changed

+46
-33
lines changed

2 files changed

+46
-33
lines changed

src/handler.rs

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,6 @@ where
136136
let is_eip3607_disabled = ctx.cfg().is_eip3607_disabled();
137137
let is_nonce_check_disabled = ctx.cfg().is_nonce_check_disabled();
138138

139-
let mint = ctx.tx().mint().unwrap_or_default();
140-
141139
let (tx, journal) = ctx.tx_journal_mut();
142140

143141
let mut caller_account = journal.load_account_with_code_mut(tx.caller())?.data;
@@ -152,11 +150,19 @@ where
152150
)?;
153151
}
154152

155-
let mut new_balance = caller_account.info.balance.saturating_add(U256::from(mint));
153+
// For L1->L2 transactions, we only credit the tx value so the initial value transfer succeeds.
154+
// On ZKsync OS, the mint value isn’t added to msg.sender.balance until the transaction finishes.
155+
if is_l1_to_l2_tx {
156+
let new_balance = caller_account.info.balance.saturating_add(tx.value());
157+
caller_account.touch();
158+
caller_account.set_balance(new_balance);
159+
return Ok(());
160+
}
156161

162+
let mut new_balance = caller_account.info.balance;
157163
let max_balance_spending = tx.max_balance_spending()?;
158164

159-
if !is_l1_to_l2_tx && max_balance_spending > new_balance {
165+
if max_balance_spending > new_balance {
160166
// skip max balance check for deposit transactions.
161167
// this check for deposit was skipped previously in `validate_tx_against_state` function
162168
return Err(InvalidTransaction::LackOfFundForMaxFee {
@@ -192,38 +198,41 @@ where
192198
evm: &mut Self::Evm,
193199
frame_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
194200
) -> Result<(), Self::Error> {
195-
reimburse_caller(evm.ctx(), frame_result.gas(), U256::ZERO)?;
201+
if !evm.ctx().tx().is_l1_to_l2_tx() {
202+
reimburse_caller(evm.ctx(), frame_result.gas(), U256::ZERO)?;
203+
return Ok(());
204+
}
196205

197-
let is_l1_to_l2_tx = evm.ctx().tx().is_l1_to_l2_tx();
198-
if is_l1_to_l2_tx {
199-
let caller = evm.ctx().tx().caller();
200-
let refund_recipient = evm
201-
.ctx()
202-
.tx()
203-
.refund_recipient()
204-
.expect("Refund recipient is missing for L1 -> L2 tx");
205-
206-
let basefee = evm.ctx().block().basefee() as u128;
207-
let effective_gas_price = evm.ctx().tx().effective_gas_price(basefee);
208-
let spent_fee =
209-
U256::from(frame_result.gas().spent()) * U256::from(effective_gas_price);
210-
let mint = evm.ctx().tx().mint().unwrap_or_default();
211-
let value = evm.ctx().tx().value();
212-
213-
// Did the call succeed?
214-
let is_success = frame_result.interpreter_result().result.is_ok();
215-
216-
let additional_refund = if is_success {
217-
mint - value - spent_fee
218-
} else {
219-
mint - spent_fee
220-
};
221-
222-
// // Return balance of not spend gas.
206+
// For L1->L2 transactions, mint and gas fees were not applied to the
207+
// sender before execution. Handle all balance accounting here.
208+
let caller = evm.ctx().tx().caller();
209+
let refund_recipient = evm
210+
.ctx()
211+
.tx()
212+
.refund_recipient()
213+
.expect("Refund recipient is missing for L1 -> L2 tx");
214+
215+
let basefee = evm.ctx().block().basefee() as u128;
216+
let effective_gas_price = evm.ctx().tx().effective_gas_price(basefee);
217+
let spent_fee = U256::from(frame_result.gas().spent()) * U256::from(effective_gas_price);
218+
let mint = evm.ctx().tx().mint().unwrap_or_default();
219+
let value = evm.ctx().tx().value();
220+
221+
let is_success = frame_result.interpreter_result().result.is_ok();
222+
223+
if !is_success {
224+
// On failure, value transfer was rolled back so sender still holds
225+
// the extra `value` credited before execution. Move it to refund_recipient.
223226
evm.ctx()
224227
.journal_mut()
225-
.transfer(caller, refund_recipient, additional_refund)?;
228+
.transfer(caller, refund_recipient, value)?;
226229
}
230+
231+
// Mint the remaining refund directly to refund_recipient.
232+
evm.ctx()
233+
.journal_mut()
234+
.balance_incr(refund_recipient, mint - value - spent_fee)?;
235+
227236
Ok(())
228237
}
229238

src/precompiles.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,15 @@ where
148148

149149
#[inline]
150150
fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
151+
// Warm Blake2 (0x09) and Point Evaluation (0x0a) addresses even though
152+
// they are not active precompiles.
153+
let extra = [u64_to_address(9), u64_to_address(10)].into_iter();
151154
// TODO: temporary workaround to not warm P256 precompile
152155
Box::new(
153156
self.inner
154157
.warm_addresses()
155-
.filter(|x| *x != u64_to_address(P256VERIFY_ADDRESS)),
158+
.filter(|x| *x != u64_to_address(P256VERIFY_ADDRESS))
159+
.chain(extra),
156160
)
157161
}
158162

0 commit comments

Comments
 (0)