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)]