diff --git a/crates/precompiles/src/lib.rs b/crates/precompiles/src/lib.rs index 8c0792017d..85ecdade95 100644 --- a/crates/precompiles/src/lib.rs +++ b/crates/precompiles/src/lib.rs @@ -170,7 +170,8 @@ macro_rules! tempo_precompile { amsterdam_eip8037_enabled, $input.is_static, gas_params.clone(), - ); + ) + .with_msg_sender($input.caller); crate::storage::StorageCtx::enter(&mut storage, || { $impl.call($input.data, $input.caller) }) diff --git a/crates/precompiles/src/stablecoin_dex/mod.rs b/crates/precompiles/src/stablecoin_dex/mod.rs index 106ea1a770..7ac2543c51 100644 --- a/crates/precompiles/src/stablecoin_dex/mod.rs +++ b/crates/precompiles/src/stablecoin_dex/mod.rs @@ -2230,6 +2230,7 @@ mod tests { let (base_token, _) = setup_test_tokens(admin, alice, exchange.address, escrow)?; exchange.create_pair(base_token)?; + StorageCtx.set_msg_sender(alice); let result = exchange.place_flip( alice, base_token, @@ -2364,6 +2365,7 @@ mod tests { // Place same-tick flip bid FIRST so it sits at the head of the bid // level and is the order consumed by the next swap-sell. + StorageCtx.set_msg_sender(alice); let flip_id = exchange .place_flip(alice, base_token, amount, true, tick, tick, false) .expect("same-tick flip should succeed on T5"); @@ -2377,6 +2379,7 @@ mod tests { // Bob sells exactly `amount` base, which fully consumes only the // flip bid (FIFO) and triggers the post-fill flip into an ask at // the same tick. + StorageCtx.set_msg_sender(bob); exchange.swap_exact_amount_in(bob, base_token, quote_token, amount, 0)?; // Flip bid is gone, regular bid remains untouched. @@ -2881,6 +2884,7 @@ mod tests { setup_test_tokens(admin, alice, exchange.address, expected_escrow * 2)?; exchange.create_pair(base_token)?; + StorageCtx.set_msg_sender(alice); let flip_order_id = exchange.place_flip(alice, base_token, amount, true, tick, flip_tick, false)?; diff --git a/crates/precompiles/src/storage/evm.rs b/crates/precompiles/src/storage/evm.rs index 411b36998c..beddf1357f 100644 --- a/crates/precompiles/src/storage/evm.rs +++ b/crates/precompiles/src/storage/evm.rs @@ -20,6 +20,7 @@ pub struct EvmPrecompileStorageProvider<'a> { amsterdam_eip8037_enabled: bool, is_static: bool, gas_params: GasParams, + msg_sender: Option
, /// Debug-only LIFO checkpoint validator. See [`Self::assert_lifo`]. #[cfg(debug_assertions)] checkpoint_stack: Vec<(usize, usize)>, @@ -45,6 +46,7 @@ impl<'a> EvmPrecompileStorageProvider<'a> { gas_params, #[cfg(debug_assertions)] checkpoint_stack: Vec::new(), + msg_sender: None, } } @@ -86,6 +88,12 @@ impl<'a> EvmPrecompileStorageProvider<'a> { } Ok(()) } + + /// Sets the `msg.sender`. + pub fn with_msg_sender(mut self, msg_sender: Address) -> Self { + self.msg_sender = Some(msg_sender); + self + } } impl<'a> PrecompileStorageProvider for EvmPrecompileStorageProvider<'a> { @@ -349,6 +357,11 @@ impl<'a> PrecompileStorageProvider for EvmPrecompileStorageProvider<'a> { self.assert_lifo(&checkpoint, "revert"); self.internals.checkpoint_revert(checkpoint) } + + #[inline] + fn msg_sender(&self) -> Option
{ + self.msg_sender + } } /// LIFO checkpoint validation (debug builds only). diff --git a/crates/precompiles/src/storage/hashmap.rs b/crates/precompiles/src/storage/hashmap.rs index 19a4a231c8..ceacb771de 100644 --- a/crates/precompiles/src/storage/hashmap.rs +++ b/crates/precompiles/src/storage/hashmap.rs @@ -26,6 +26,7 @@ pub struct HashMapStorageProvider { counter_sload: u64, counter_sstore: u64, snapshots: Vec, + msg_sender: Option
, /// Emitted events keyed by contract address. pub events: HashMap>, @@ -63,6 +64,7 @@ impl HashMapStorageProvider { .as_secs(), ), beneficiary: Address::ZERO, + msg_sender: None, block_number: 0, spec, amsterdam_eip8037_enabled: false, @@ -239,6 +241,10 @@ impl PrecompileStorageProvider for HashMapStorageProvider { self.events = snapshot.events; } } + + fn msg_sender(&self) -> Option
{ + self.msg_sender + } } #[cfg(any(test, feature = "test-utils"))] @@ -320,4 +326,9 @@ impl HashMapStorageProvider { .into_iter() .map(|((addr, slot), value)| (addr, slot, value)) } + + /// Overrides the message sender. + pub fn set_msg_sender(&mut self, msg_sender: Address) { + self.msg_sender = Some(msg_sender); + } } diff --git a/crates/precompiles/src/storage/mod.rs b/crates/precompiles/src/storage/mod.rs index 31edb42b81..17596193fa 100644 --- a/crates/precompiles/src/storage/mod.rs +++ b/crates/precompiles/src/storage/mod.rs @@ -158,6 +158,9 @@ pub trait PrecompileStorageProvider { Ok(recovered.ok().filter(|addr| !addr.is_zero())) } + + /// Returns the address of the caller of the current precompile invocation. + fn msg_sender(&self) -> Option
; } /// Storage operations for a given (contract) address. diff --git a/crates/precompiles/src/storage/thread_local.rs b/crates/precompiles/src/storage/thread_local.rs index 5aeea0c8e1..66eb7bad2c 100644 --- a/crates/precompiles/src/storage/thread_local.rs +++ b/crates/precompiles/src/storage/thread_local.rs @@ -257,6 +257,11 @@ impl StorageCtx { Self::try_with_storage(|storage| storage.recover_signer(digest, v, r, s)) } + /// Returns the address of the caller of the current precompile invocation. + pub fn msg_sender(&self) -> Option
{ + Self::with_storage(|storage| storage.msg_sender()) + } + /// Returns a [`PrecompileOutput`] with [`revm::precompile::PrecompileStatus::Success`] and the current gas values. pub fn success_output(&self, output: Bytes) -> PrecompileOutput { PrecompileOutput::new(self.gas_used(), output, self.reservoir()) @@ -452,6 +457,11 @@ impl StorageCtx { self.as_hashmap().set_spec(spec) } + /// NOTE: assumes storage tests always use the `HashMapStorageProvider` + pub fn set_msg_sender(&mut self, msg_sender: Address) { + self.as_hashmap().set_msg_sender(msg_sender) + } + /// NOTE: assumes storage tests always use the `HashMapStorageProvider` pub fn clear_transient(&mut self) { self.as_hashmap().clear_transient() diff --git a/crates/precompiles/src/tip20/mod.rs b/crates/precompiles/src/tip20/mod.rs index d3865eeee9..a7653fff10 100644 --- a/crates/precompiles/src/tip20/mod.rs +++ b/crates/precompiles/src/tip20/mod.rs @@ -780,6 +780,10 @@ impl TIP20Token { return Err(TIP20Error::unauthorized().into()); } + if self.storage.msg_sender() != Some(from) { + return Err(TIP20Error::unauthorized().into()); + } + let to = Recipient::resolve(caller)?; self.validate_transfer(from, &to)?; self.check_and_update_spending_limit(from, amount)?; @@ -1843,6 +1847,7 @@ pub(crate) mod tests { .apply()?; // Pre-T5: caller is unchecked (preserves pre-TIP-1035 FeeAMM behavior). + StorageCtx.set_msg_sender(from); assert!(token.system_transfer_from(to, from, amount).is_ok()); assert_eq!( token.emitted_events().last().unwrap(), @@ -1867,6 +1872,7 @@ pub(crate) mod tests { .apply()?; // Listed precompile is allowed to invoke `system_transfer_from`. + StorageCtx.set_msg_sender(from); assert!( token .system_transfer_from(TIP_FEE_MANAGER_ADDRESS, from, amount) diff --git a/crates/precompiles/src/tip_fee_manager/amm.rs b/crates/precompiles/src/tip_fee_manager/amm.rs index e7429ef32e..c63cfce869 100644 --- a/crates/precompiles/src/tip_fee_manager/amm.rs +++ b/crates/precompiles/src/tip_fee_manager/amm.rs @@ -814,6 +814,7 @@ mod tests { let mut amm = TipFeeManager::new(); let amount = uint!(10000_U256); + StorageCtx.set_msg_sender(admin); let result = amm.mint(admin, token1, token2, amount, admin)?; let expected_mean = amount / uint!(2_U256); let expected_liquidity = expected_mean - MIN_LIQUIDITY; @@ -1162,6 +1163,7 @@ mod tests { let amount_out = uint!(1000_U256); let expected_in = (amount_out * N) / SCALE + U256::ONE; + StorageCtx.set_msg_sender(admin); let amount_in = amm.rebalance_swap(admin, user_token, validator_token, amount_out, recipient)?; @@ -1202,6 +1204,7 @@ mod tests { let mut amm = TipFeeManager::new(); let initial_amount = uint!(100000_U256); + StorageCtx.set_msg_sender(admin); let first_liquidity = amm.mint(admin, user_token, validator_token, initial_amount, admin)?; @@ -1216,6 +1219,7 @@ mod tests { let reserve_val = U256::from(pool_after_first.reserve_validator_token); let second_amount = uint!(50000_U256); + StorageCtx.set_msg_sender(second_user); let second_liquidity = amm.mint( second_user, user_token, @@ -1264,6 +1268,7 @@ mod tests { let mut amm = TipFeeManager::new(); let deposit_amount = uint!(100000_U256); + StorageCtx.set_msg_sender(admin); let liquidity = amm.mint(admin, user_token, validator_token, deposit_amount, admin)?; let expected_liquidity = deposit_amount / uint!(2_U256) - MIN_LIQUIDITY; @@ -1327,6 +1332,7 @@ mod tests { let mut amm = TipFeeManager::new(); let deposit_amount = uint!(100000_U256); + StorageCtx.set_msg_sender(admin); let liquidity = amm.mint(admin, user_token, validator_token, deposit_amount, admin)?; let result = amm.burn( @@ -1440,6 +1446,7 @@ mod tests { let mut amm = TipFeeManager::new(); let deposit_amount = uint!(100000_U256); + StorageCtx.set_msg_sender(admin); let liquidity = amm.mint(admin, user_token, validator_token, deposit_amount, admin)?; let pool_id = amm.pool_id(user_token, validator_token); @@ -1484,6 +1491,7 @@ mod tests { let mut amm = TipFeeManager::new(); let deposit_amount = uint!(100000_U256); + StorageCtx.set_msg_sender(admin); let liquidity = amm.mint(admin, user_token, validator_token, deposit_amount, admin)?; let pool_id = amm.pool_id(user_token, validator_token); @@ -1529,6 +1537,7 @@ mod tests { let amount_out = amm.check_sufficient_liquidity(pool_id, uint!(50000_U256))?; amm.reserve_pool_liquidity(pool_id, amount_out)?; + StorageCtx.set_msg_sender(admin); amm.rebalance_swap(admin, user_token, validator_token, uint!(5000_U256), to)?; let pool = amm.pools[pool_id].read()?; let reserved = amm.pending_fee_swap_reservation[pool_id].t_read()?; @@ -1564,6 +1573,7 @@ mod tests { let pool_id = setup_pool_with_liquidity(&mut amm, user_token, validator_token, liq, liq)?; amm.check_sufficient_liquidity(pool_id, uint!(90000_U256))?; + StorageCtx.set_msg_sender(admin); assert!( amm.rebalance_swap(admin, user_token, validator_token, uint!(5000_U256), to) .is_ok() @@ -1595,6 +1605,7 @@ mod tests { let mut amm = TipFeeManager::new(); let deposit_amount = uint!(100000_U256); + StorageCtx.set_msg_sender(admin); let liquidity = amm.mint(admin, user_token, validator_token, deposit_amount, admin)?; let pool_id = amm.pool_id(user_token, validator_token); diff --git a/crates/revm/src/common.rs b/crates/revm/src/common.rs index 489b53b9a7..66367878c6 100644 --- a/crates/revm/src/common.rs +++ b/crates/revm/src/common.rs @@ -459,6 +459,10 @@ where fn checkpoint_revert(&mut self, _: revm::context::journaled_state::JournalCheckpoint) { unreachable!("'checkpoint_revert' not supported in read-only context") } + + fn msg_sender(&self) -> Option
{ + None + } } #[cfg(test)]