Skip to content

Commit 11c3689

Browse files
committed
Optmize safe setup events processing
- Make `_process_safe_creation_events` more readable - Optimize finding `indexed_addresses` query without a new index or database migration New query allows to reuse "_from" index in `InternalTx`: ``` indexed_addresses = InternalTx.objects.filter( _from__in=safe_creation_events_addresses, decoded_tx__function_name="setup", contract_address=None, ).values_list("_from", flat=True) ```
1 parent 278d8ab commit 11c3689

File tree

1 file changed

+79
-87
lines changed

1 file changed

+79
-87
lines changed

safe_transaction_service/history/indexers/safe_events_indexer.py

Lines changed: 79 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -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,11 +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
)
528-
indexed_addresses = InternalTxDecoded.objects.filter(
529-
safe_address__in=safe_creation_events_addresses,
530-
function_name="setup",
531-
internal_tx__contract_address=None,
532-
).values_list("safe_address", flat=True)
533+
indexed_addresses = InternalTx.objects.filter(
534+
_from__in=safe_creation_events_addresses,
535+
decoded_tx__function_name="setup",
536+
contract_address=None,
537+
).values_list("_from", flat=True)
533538
# Ignoring the already indexed contracts
534539
addresses_to_index = safe_creation_events_addresses - set(indexed_addresses)
535540
logger.debug(
@@ -541,88 +546,75 @@ def _process_safe_creation_events(
541546
)
542547
for safe_address in addresses_to_index:
543548
events = safe_addresses_with_creation_events[safe_address]
544-
for event_position, event in enumerate(events):
549+
550+
# Find events by type (each Safe should have at most one of each)
551+
setup_event: EventData | None = None
552+
proxy_creation_event: EventData | None = None
553+
for event in events:
545554
if event["event"] == "SafeSetup":
546555
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)
614556
elif event["event"] == "ProxyCreation":
615557
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,
558+
else:
559+
logger.error("Unexpected event type: %s", event["event"])
560+
561+
# Process ProxyCreation - creates the proxy contract
562+
if proxy_creation_event:
563+
internal_tx = self._get_internal_tx_from_decoded_element(
564+
proxy_creation_event,
565+
contract_address=proxy_creation_event["args"].get("proxy"),
566+
tx_type=InternalTxType.CREATE.value,
567+
call_type=None,
568+
)
569+
internal_txs.append(internal_tx)
570+
571+
# Process SafeSetup - initializes the Safe
572+
if setup_event:
573+
if not proxy_creation_event:
574+
# SafeSetup without ProxyCreation means proxy was created in a previous block
575+
logger.debug(
576+
"[%s] Proxy was created in previous blocks, deleting the old InternalTx",
577+
safe_address,
622578
)
623-
internal_txs.append(internal_tx)
579+
InternalTx.objects.filter(contract_address=safe_address).delete()
580+
logger.debug(
581+
"[%s] Proxy was created in previous blocks, old InternalTx deleted",
582+
safe_address,
583+
)
584+
585+
# Determine trace_address and singleton based on whether ProxyCreation exists
586+
if proxy_creation_event:
587+
setup_trace_address = f"{proxy_creation_event['logIndex']},0"
588+
singleton = proxy_creation_event["args"].get("singleton")
589+
# contract_address=None signals this came via event indexer with ProxyCreation
590+
contract_address = None
624591
else:
625-
logger.error(f"Event is not a Safe creation event {event['event']}")
592+
setup_trace_address = str(setup_event["logIndex"])
593+
singleton = NULL_ADDRESS
594+
contract_address = safe_address
595+
596+
internal_tx = self._get_internal_tx_from_decoded_element(
597+
setup_event,
598+
contract_address=contract_address,
599+
to=singleton,
600+
trace_address=setup_trace_address,
601+
call_type=EthereumTxCallType.DELEGATE_CALL.value,
602+
)
603+
604+
# Generate InternalDecodedTx for SafeSetup event
605+
setup_args = dict(setup_event["args"])
606+
setup_args["payment"] = 0
607+
setup_args["paymentReceiver"] = NULL_ADDRESS
608+
setup_args["_threshold"] = setup_args.pop("threshold")
609+
setup_args["_owners"] = setup_args.pop("owners")
610+
internal_tx_decoded = InternalTxDecoded(
611+
internal_tx=internal_tx,
612+
function_name="setup",
613+
arguments=setup_args,
614+
safe_address=internal_tx._from, # Denormalized for efficient querying
615+
)
616+
internal_txs.append(internal_tx)
617+
internal_txs_decoded.append(internal_tx_decoded)
626618

627619
logger.debug("InternalTx and InternalTxDecoded objects for creation were built")
628620
return InternalTx.objects.store_internal_txs_and_decoded_in_db(

0 commit comments

Comments
 (0)