3232from ..blocks import Log
3333from ..fork_types import Address
3434from ..state import (
35+ State ,
3536 account_has_code_or_nonce ,
3637 account_has_storage ,
3738 begin_transaction ,
4647 rollback_transaction ,
4748 set_code ,
4849)
49- from ..vm import Message
50+ from ..vm import Message , TransactionEnvironment
5051from ..vm .eoa_delegation import (
5152 get_delegated_code_address ,
5253 is_valid_delegation ,
5354 set_delegation ,
5455)
5556from ..vm .gas import GAS_CODE_DEPOSIT , charge_gas
57+ from ..vm .precompiled_contracts import MONAD_PRECOMPILE_ADDRESSES
5658from ..vm .precompiled_contracts .mapping import PRE_COMPILED_CONTRACTS
5759from . import Evm
5860from .exceptions import (
6264 InvalidOpcode ,
6365 OutOfGasError ,
6466 Revert ,
67+ RevertInMonadPrecompile ,
6568 RevertOnReserveBalance ,
6669 StackDepthLimitError ,
6770)
7679RESERVE_BALANCE = U256 (10 * 10 ** 18 ) # 10 MON
7780
7881
82+ def is_reserve_balance_violated (
83+ state : State ,
84+ tx_env : TransactionEnvironment ,
85+ ) -> bool :
86+ """
87+ Check if any EOA has violated the reserve balance constraint.
88+
89+ Returns True if a violation is detected, False otherwise.
90+ """
91+ for addr in set (state ._main_trie ._data .keys ()):
92+ acc = get_account (state , addr )
93+ if acc .code == b"" or is_valid_delegation (acc .code ):
94+ original_balance = get_balance_original (state , addr )
95+ if tx_env .origin == addr :
96+ # gas_fees already deducted, need to re-add if sender
97+ # to match with spec.
98+ gas_fees = U256 (tx_env .gas_price * tx_env .tx_gas_limit )
99+ original_balance += gas_fees
100+ reserve = min (RESERVE_BALANCE , original_balance )
101+ threshold = reserve - gas_fees
102+ else :
103+ threshold = RESERVE_BALANCE
104+ is_exception = not is_sender_authority (
105+ state , addr
106+ ) and not is_valid_delegation (acc .code )
107+ if (
108+ acc .balance < original_balance
109+ and acc .balance < threshold
110+ and not is_exception
111+ ):
112+ return True
113+ return False
114+
115+
79116@dataclass
80117class MessageCallOutput :
81118 """
@@ -293,6 +330,10 @@ def process_message(message: Message) -> Evm:
293330 evm_trace (evm , PrecompileStart (evm .message .code_address ))
294331 PRE_COMPILED_CONTRACTS [evm .message .code_address ](evm )
295332 evm_trace (evm , PrecompileEnd ())
333+ elif evm .message .code_address in MONAD_PRECOMPILE_ADDRESSES :
334+ # Calling a precompile via delegation and it's a Monad
335+ # precompile => revert.
336+ raise RevertInMonadPrecompile
296337 else :
297338 while evm .running and evm .pc < ulen (evm .code ):
298339 try :
@@ -306,6 +347,11 @@ def process_message(message: Message) -> Evm:
306347
307348 evm_trace (evm , EvmStop (Ops .STOP ))
308349
350+ except RevertInMonadPrecompile as error :
351+ evm_trace (evm , OpException (error ))
352+ evm .gas_left = Uint (0 )
353+ # evm.output preserved — contains the raw error message
354+ evm .error = error
309355 except ExceptionalHalt as error :
310356 evm_trace (evm , OpException (error ))
311357 evm .gas_left = Uint (0 )
@@ -322,34 +368,9 @@ def process_message(message: Message) -> Evm:
322368 else :
323369 # FIXME: index_in_block is a proxy for not being a system tx
324370 if message .depth == 0 and message .tx_env .index_in_block is not None :
325- for addr in set (state ._main_trie ._data .keys ()):
326- acc = get_account (state , addr )
327- if acc .code == b"" or is_valid_delegation (acc .code ):
328- original_balance = get_balance_original (state , addr )
329- if message .tx_env .origin == addr :
330- # gas_fees already deducted, need to re-add if sender
331- # to match with spec.
332- gas_fees = U256 (
333- message .tx_env .gas_price
334- * message .tx_env .tx_gas_limit
335- )
336- original_balance += gas_fees
337- reserve = min (RESERVE_BALANCE , original_balance )
338- threshold = reserve - gas_fees
339- else :
340- threshold = RESERVE_BALANCE
341- is_exception = not is_sender_authority (
342- state , addr
343- ) and not is_valid_delegation (acc .code )
344- if (
345- acc .balance < original_balance
346- and acc .balance < threshold
347- and not is_exception
348- ):
349- rollback_transaction (state , transient_storage )
350- evm .error = RevertOnReserveBalance ()
351- return evm
352- # cannot do this because it fails the entire tx
353- # raise RevertOnReserveBalance
371+ if is_reserve_balance_violated (state , message .tx_env ):
372+ rollback_transaction (state , transient_storage )
373+ evm .error = RevertOnReserveBalance ()
374+ return evm
354375 commit_transaction (state , transient_storage )
355376 return evm
0 commit comments