3434 to_address_masked ,
3535)
3636from ...vm .eoa_delegation import (
37- apply_delegation_tracking ,
38- check_delegation ,
37+ calculate_delegation_cost ,
38+ read_delegation_target ,
3939)
4040from .. import (
4141 Evm ,
@@ -397,13 +397,36 @@ def call(evm: Evm) -> None:
397397 if is_cold_access :
398398 evm .accessed_addresses .add (to )
399399
400+ # Calculate base gas cost for accessing 'to' account
401+ base_gas_cost = extend_memory .cost + access_gas_cost
402+
403+ # Check gas for base access BEFORE reading 'to' account
404+ check_gas (evm , base_gas_cost )
405+
406+ # Now safe to read 'to' account's code to check for delegation
407+ # This reads 'to' account's code and tracks it, but does NOT
408+ # read the delegation target yet
400409 (
401410 is_delegated ,
402411 original_address ,
403- final_address ,
404- code ,
412+ delegated_address ,
405413 delegation_gas_cost ,
406- ) = check_delegation (evm , to )
414+ ) = calculate_delegation_cost (evm , to )
415+
416+ # Check gas for delegation target access before reading it
417+ # Note: We haven't charged base_gas_cost yet (only checked it),
418+ # so we need to verify we have TOTAL gas (base + delegation)
419+ if is_delegated and delegation_gas_cost > Uint (0 ):
420+ check_gas (evm , base_gas_cost + delegation_gas_cost )
421+
422+ if is_delegated :
423+ assert delegated_address is not None
424+ code = read_delegation_target (evm , delegated_address )
425+ final_address = delegated_address
426+ else :
427+ code = get_account (evm .message .block_env .state , to ).code
428+ final_address = to
429+
407430 access_gas_cost += delegation_gas_cost
408431
409432 code_address = final_address
@@ -421,12 +444,6 @@ def call(evm: Evm) -> None:
421444 access_gas_cost + create_gas_cost + transfer_gas_cost ,
422445 )
423446
424- check_gas (evm , message_call_gas .cost + extend_memory .cost )
425-
426- track_address (evm .state_changes , to )
427- if is_delegated :
428- apply_delegation_tracking (evm , original_address , final_address )
429-
430447 charge_gas (evm , message_call_gas .cost + extend_memory .cost )
431448 if evm .message .is_static and value != U256 (0 ):
432449 raise WriteInStaticContext
@@ -497,13 +514,33 @@ def callcode(evm: Evm) -> None:
497514 if is_cold_access :
498515 evm .accessed_addresses .add (code_address )
499516
517+ # Check we have enough gas for base access before reading state
518+ base_gas_cost = extend_memory .cost + access_gas_cost
519+ check_gas (evm , base_gas_cost )
520+
521+ # Check delegation cost without reading delegation target yet
500522 (
501523 is_delegated ,
502524 original_address ,
503- final_address ,
504- code ,
525+ delegated_address ,
505526 delegation_gas_cost ,
506- ) = check_delegation (evm , code_address )
527+ ) = calculate_delegation_cost (evm , code_address )
528+
529+ # Check gas for delegation target access BEFORE reading it
530+ # Note: We haven't charged base_gas_cost yet (only checked it),
531+ # so we need to verify we have TOTAL gas (base + delegation)
532+ if is_delegated and delegation_gas_cost > Uint (0 ):
533+ check_gas (evm , base_gas_cost + delegation_gas_cost )
534+
535+ # Now safe to read delegation target since we verified gas
536+ if is_delegated :
537+ assert delegated_address is not None
538+ code = read_delegation_target (evm , delegated_address )
539+ final_address = delegated_address
540+ else :
541+ code = get_account (evm .message .block_env .state , code_address ).code
542+ final_address = code_address
543+
507544 access_gas_cost += delegation_gas_cost
508545
509546 code_address = final_address
@@ -518,12 +555,6 @@ def callcode(evm: Evm) -> None:
518555 access_gas_cost + transfer_gas_cost ,
519556 )
520557
521- check_gas (evm , message_call_gas .cost + extend_memory .cost )
522-
523- track_address (evm .state_changes , original_address )
524- if is_delegated :
525- apply_delegation_tracking (evm , original_address , final_address )
526-
527558 charge_gas (evm , message_call_gas .cost + extend_memory .cost )
528559
529560 # OPERATION
@@ -665,16 +696,37 @@ def delegatecall(evm: Evm) -> None:
665696 access_gas_cost = (
666697 GAS_COLD_ACCOUNT_ACCESS if is_cold_access else GAS_WARM_ACCESS
667698 )
699+
700+ # Check we have enough gas for base access before reading state
701+ base_gas_cost = extend_memory .cost + access_gas_cost
702+ check_gas (evm , base_gas_cost )
703+
668704 if is_cold_access :
669705 evm .accessed_addresses .add (code_address )
670706
707+ # Check delegation cost without reading delegation target yet
671708 (
672709 is_delegated ,
673710 original_address ,
674- final_address ,
675- code ,
711+ delegated_address ,
676712 delegation_gas_cost ,
677- ) = check_delegation (evm , code_address )
713+ ) = calculate_delegation_cost (evm , code_address )
714+
715+ # Check gas for delegation target access BEFORE reading it
716+ # Note: We haven't charged base_gas_cost yet (only checked it),
717+ # so we need to verify we have TOTAL gas (base + delegation)
718+ if is_delegated and delegation_gas_cost > Uint (0 ):
719+ check_gas (evm , base_gas_cost + delegation_gas_cost )
720+
721+ # Now safe to read delegation target since we verified gas
722+ if is_delegated :
723+ assert delegated_address is not None
724+ code = read_delegation_target (evm , delegated_address )
725+ final_address = delegated_address
726+ else :
727+ code = get_account (evm .message .block_env .state , code_address ).code
728+ final_address = code_address
729+
678730 access_gas_cost += delegation_gas_cost
679731
680732 code_address = final_address
@@ -684,12 +736,6 @@ def delegatecall(evm: Evm) -> None:
684736 U256 (0 ), gas , Uint (evm .gas_left ), extend_memory .cost , access_gas_cost
685737 )
686738
687- check_gas (evm , message_call_gas .cost + extend_memory .cost )
688-
689- track_address (evm .state_changes , original_address )
690- if is_delegated :
691- apply_delegation_tracking (evm , original_address , final_address )
692-
693739 charge_gas (evm , message_call_gas .cost + extend_memory .cost )
694740
695741 # OPERATION
@@ -749,13 +795,33 @@ def staticcall(evm: Evm) -> None:
749795 if is_cold_access :
750796 evm .accessed_addresses .add (to )
751797
798+ # Check we have enough gas for base access before reading state
799+ base_gas_cost = extend_memory .cost + access_gas_cost
800+ check_gas (evm , base_gas_cost )
801+
802+ # Check delegation cost without reading delegation target yet
752803 (
753804 is_delegated ,
754805 original_address ,
755- final_address ,
756- code ,
806+ delegated_address ,
757807 delegation_gas_cost ,
758- ) = check_delegation (evm , to )
808+ ) = calculate_delegation_cost (evm , to )
809+
810+ # Check gas for delegation target access BEFORE reading it
811+ # Note: We haven't charged base_gas_cost yet (only checked it),
812+ # so we need to verify we have TOTAL gas (base + delegation)
813+ if is_delegated and delegation_gas_cost > Uint (0 ):
814+ check_gas (evm , base_gas_cost + delegation_gas_cost )
815+
816+ # Now safe to read delegation target since we verified gas
817+ if is_delegated :
818+ assert delegated_address is not None
819+ code = read_delegation_target (evm , delegated_address )
820+ final_address = delegated_address
821+ else :
822+ code = get_account (evm .message .block_env .state , to ).code
823+ final_address = to
824+
759825 access_gas_cost += delegation_gas_cost
760826
761827 code_address = final_address
@@ -769,12 +835,6 @@ def staticcall(evm: Evm) -> None:
769835 access_gas_cost ,
770836 )
771837
772- check_gas (evm , message_call_gas .cost + extend_memory .cost )
773-
774- track_address (evm .state_changes , to )
775- if is_delegated :
776- apply_delegation_tracking (evm , original_address , final_address )
777-
778838 charge_gas (evm , message_call_gas .cost + extend_memory .cost )
779839
780840 # OPERATION
0 commit comments