@@ -482,8 +482,8 @@ def _get_safe_creation_events(
482482 self , decoded_elements : list [EventData ]
483483 ) -> dict [ChecksumAddress , list [EventData ]]:
484484 """
485- Get the creation events (ProxyCreation and SafeSetup ) from decoded elements and generates a dictionary
486- that groups these events by Safe address, so they are processed together
485+ Get the creation events (SafeSetup and ProxyCreation ) from decoded elements and generates a dictionary
486+ that groups these events by Safe address, so they are processed together.
487487
488488 :param decoded_elements:
489489 :return: dictionary with creation events by Safe address
@@ -493,6 +493,7 @@ def _get_safe_creation_events(
493493 event_name = decoded_element ["event" ]
494494 if event_name == "SafeSetup" :
495495 safe_address = decoded_element ["address" ]
496+ # Always insert SafeSetup event at the beginning of the list
496497 safe_creation_events .setdefault (safe_address , []).append (
497498 decoded_element
498499 )
@@ -509,13 +510,17 @@ def _process_safe_creation_events(
509510 safe_addresses_with_creation_events : dict [ChecksumAddress , list [EventData ]],
510511 ) -> list [InternalTx ]:
511512 """
512- Process creation events (ProxyCreation and SafeSetup). They must be processed together
513+ Process creation events (ProxyCreation and SafeSetup). They must be processed together.
514+
515+ Usual order is:
516+ - SafeSetup
517+ - ProxyCreation
513518
514519 :param safe_addresses_with_creation_events:
515- :return:
520+ :return: Generated InternalTxs for safe creation
516521 """
517- internal_txs = []
518- internal_txs_decoded = []
522+ internal_txs : list [ InternalTx ] = []
523+ internal_txs_decoded : list [ InternalTxDecoded ] = []
519524
520525 logger .debug ("Processing Safe Creation events" )
521526
@@ -525,10 +530,11 @@ def _process_safe_creation_events(
525530 "Got %d addresses to index, checking if some are indexed" ,
526531 len (safe_creation_events_addresses ),
527532 )
533+ # Check if SafeSetup event was indexed. ProxyCreation must not come after SafeSetup, so we consider
534+ # indexed a Safe with a SafeSetup event processed (InternalTxDecoded with `function_name="setup"`).
528535 indexed_addresses = InternalTxDecoded .objects .filter (
529536 safe_address__in = safe_creation_events_addresses ,
530537 function_name = "setup" ,
531- internal_tx__contract_address = None ,
532538 ).values_list ("safe_address" , flat = True )
533539 # Ignoring the already indexed contracts
534540 addresses_to_index = safe_creation_events_addresses - set (indexed_addresses )
@@ -541,88 +547,75 @@ def _process_safe_creation_events(
541547 )
542548 for safe_address in addresses_to_index :
543549 events = safe_addresses_with_creation_events [safe_address ]
544- for event_position , event in enumerate (events ):
550+
551+ # Find events by type (each Safe should have at most one of each)
552+ setup_event : EventData | None = None
553+ proxy_creation_event : EventData | None = None
554+ for event in events :
545555 if event ["event" ] == "SafeSetup" :
546556 setup_event = event
547- # If we have both events we should extract Singleton and trace_address from ProxyCreation event
548- if len (events ) > 1 :
549- if (
550- event_position == 0
551- and events [1 ]["event" ] == "ProxyCreation"
552- ):
553- # Usually SafeSetup is the first event and next is ProxyCreation when ProxyFactory is called with initializer.
554- proxy_creation_event = events [1 ]
555- elif (
556- event_position == 1
557- and events [0 ]["event" ] == "ProxyCreation"
558- ):
559- # ProxyCreation first and SafeSetup later
560- proxy_creation_event = events [0 ]
561- else :
562- # This shouldn't happen, as there will be no proxy_creation event
563- continue
564- else :
565- logger .debug (
566- "[%s] Proxy was created in previous blocks, deleting the old InternalTx" ,
567- safe_address ,
568- )
569- # Proxy was created in previous blocks.
570- proxy_creation_event = None
571- # Safe was created and configure it in the next transaction. Remove it if that's the case
572- InternalTx .objects .filter (
573- contract_address = safe_address
574- ).delete ()
575- logger .debug (
576- "[%s] Proxy was created in previous blocks, old InternalTx deleted" ,
577- safe_address ,
578- )
579-
580- # Generate InternalTx and internalDecodedTx for SafeSetup event
581- setup_trace_address = (
582- f"{ proxy_creation_event ['logIndex' ]} ,0"
583- if proxy_creation_event
584- else str (setup_event ["logIndex" ])
585- )
586- singleton = (
587- proxy_creation_event ["args" ].get ("singleton" )
588- if proxy_creation_event
589- else NULL_ADDRESS
590- )
591- # Keep previous implementation
592- contract_address = None if proxy_creation_event else safe_address
593- internal_tx = self ._get_internal_tx_from_decoded_element (
594- setup_event ,
595- contract_address = contract_address ,
596- to = singleton ,
597- trace_address = setup_trace_address ,
598- call_type = EthereumTxCallType .DELEGATE_CALL .value ,
599- )
600- # Generate InternalDecodedTx for SafeSetup event
601- setup_args = dict (event ["args" ])
602- setup_args ["payment" ] = 0
603- setup_args ["paymentReceiver" ] = NULL_ADDRESS
604- setup_args ["_threshold" ] = setup_args .pop ("threshold" )
605- setup_args ["_owners" ] = setup_args .pop ("owners" )
606- internal_tx_decoded = InternalTxDecoded (
607- internal_tx = internal_tx ,
608- function_name = "setup" ,
609- arguments = setup_args ,
610- safe_address = internal_tx ._from , # Denormalized for efficient querying
611- )
612- internal_txs .append (internal_tx )
613- internal_txs_decoded .append (internal_tx_decoded )
614557 elif event ["event" ] == "ProxyCreation" :
615558 proxy_creation_event = event
616- # Generate InternalTx for ProxyCreation
617- internal_tx = self ._get_internal_tx_from_decoded_element (
618- proxy_creation_event ,
619- contract_address = proxy_creation_event ["args" ].get ("proxy" ),
620- tx_type = InternalTxType .CREATE .value ,
621- call_type = None ,
559+ else :
560+ logger .error ("Unexpected event type: %s" , event ["event" ])
561+
562+ # Process ProxyCreation - creates the proxy contract
563+ if proxy_creation_event :
564+ internal_tx = self ._get_internal_tx_from_decoded_element (
565+ proxy_creation_event ,
566+ contract_address = proxy_creation_event ["args" ].get ("proxy" ),
567+ tx_type = InternalTxType .CREATE .value ,
568+ call_type = None ,
569+ )
570+ internal_txs .append (internal_tx )
571+
572+ # Process SafeSetup - initializes the Safe
573+ if setup_event :
574+ if not proxy_creation_event :
575+ # SafeSetup without ProxyCreation means proxy was created in a previous block
576+ logger .debug (
577+ "[%s] Proxy was created in previous blocks, deleting the old InternalTx" ,
578+ safe_address ,
622579 )
623- internal_txs .append (internal_tx )
580+ InternalTx .objects .filter (contract_address = safe_address ).delete ()
581+ logger .debug (
582+ "[%s] Proxy was created in previous blocks, old InternalTx deleted" ,
583+ safe_address ,
584+ )
585+
586+ # Determine trace_address and singleton based on whether ProxyCreation exists
587+ if proxy_creation_event :
588+ setup_trace_address = f"{ proxy_creation_event ['logIndex' ]} ,0"
589+ singleton = proxy_creation_event ["args" ].get ("singleton" )
590+ # contract_address=None signals this came via event indexer with ProxyCreation
591+ contract_address = None
624592 else :
625- logger .error (f"Event is not a Safe creation event { event ['event' ]} " )
593+ setup_trace_address = str (setup_event ["logIndex" ])
594+ singleton = NULL_ADDRESS
595+ contract_address = safe_address
596+
597+ internal_tx = self ._get_internal_tx_from_decoded_element (
598+ setup_event ,
599+ contract_address = contract_address ,
600+ to = singleton ,
601+ trace_address = setup_trace_address ,
602+ call_type = EthereumTxCallType .DELEGATE_CALL .value ,
603+ )
604+
605+ # Generate InternalDecodedTx for SafeSetup event
606+ setup_args = dict (setup_event ["args" ])
607+ setup_args ["payment" ] = 0
608+ setup_args ["paymentReceiver" ] = NULL_ADDRESS
609+ setup_args ["_threshold" ] = setup_args .pop ("threshold" )
610+ setup_args ["_owners" ] = setup_args .pop ("owners" )
611+ internal_tx_decoded = InternalTxDecoded (
612+ internal_tx = internal_tx ,
613+ function_name = "setup" ,
614+ arguments = setup_args ,
615+ safe_address = internal_tx ._from , # Denormalized for efficient querying
616+ )
617+ internal_txs .append (internal_tx )
618+ internal_txs_decoded .append (internal_tx_decoded )
626619
627620 logger .debug ("InternalTx and InternalTxDecoded objects for creation were built" )
628621 return InternalTx .objects .store_internal_txs_and_decoded_in_db (
0 commit comments