Skip to content

Commit

Permalink
Add DELETED_MULTISIG_TRANSACTION event (#1810)
Browse files Browse the repository at this point in the history
  • Loading branch information
Uxio0 authored Jan 16, 2024
1 parent 6873413 commit 034bfde
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 32 deletions.
1 change: 1 addition & 0 deletions safe_transaction_service/history/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2019,6 +2019,7 @@ class WebHookType(Enum):
OUTGOING_TOKEN = 9
MESSAGE_CREATED = 10
MESSAGE_CONFIRMATION = 11
DELETED_MULTISIG_TRANSACTION = 12


class WebHookQuerySet(models.QuerySet):
Expand Down
10 changes: 10 additions & 0 deletions safe_transaction_service/history/services/webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ def build_webhook_payload(
instance: Union[
TokenTransfer, InternalTx, MultisigConfirmation, MultisigTransaction
],
deleted: bool = False,
) -> List[Dict[str, Any]]:
"""
:param sender: Sender type
:param instance: Sender instance
:param deleted: If the instance has been deleted
:return: A list of webhooks generated from the instance provided
"""
payloads: List[Dict[str, Any]] = []
Expand All @@ -48,6 +50,14 @@ def build_webhook_payload(
).hex(),
}
]
elif sender == MultisigTransaction and deleted:
payloads = [
{
"address": instance.safe,
"type": WebHookType.DELETED_MULTISIG_TRANSACTION.name,
"safeTxHash": HexBytes(instance.safe_tx_hash).hex(),
}
]
elif sender == MultisigTransaction:
payload = {
"address": instance.safe,
Expand Down
86 changes: 58 additions & 28 deletions safe_transaction_service/history/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Type, Union

from django.db.models import Model
from django.db.models.signals import post_save
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.utils import timezone

Expand Down Expand Up @@ -112,30 +112,7 @@ def safe_master_copy_clear_cache(
SafeMasterCopy.objects.get_version_for_address.cache_clear()


@receiver(
post_save,
sender=ModuleTransaction,
dispatch_uid="module_transaction.process_webhook",
)
@receiver(
post_save,
sender=MultisigConfirmation,
dispatch_uid="multisig_confirmation.process_webhook",
)
@receiver(
post_save,
sender=MultisigTransaction,
dispatch_uid="multisig_transaction.process_webhook",
)
@receiver(
post_save, sender=ERC20Transfer, dispatch_uid="erc20_transfer.process_webhook"
)
@receiver(
post_save, sender=ERC721Transfer, dispatch_uid="erc721_transfer.process_webhook"
)
@receiver(post_save, sender=InternalTx, dispatch_uid="internal_tx.process_webhook")
@receiver(post_save, sender=SafeContract, dispatch_uid="safe_contract.process_webhook")
def process_webhook(
def _process_webhook(
sender: Type[Model],
instance: Union[
TokenTransfer,
Expand All @@ -145,10 +122,14 @@ def process_webhook(
SafeContract,
],
created: bool,
**kwargs,
) -> None:
deleted: bool,
):
assert not (
created and deleted
), "An instance cannot be created and deleted at the same time"

logger.debug("Start building payloads for created=%s object=%s", created, instance)
payloads = build_webhook_payload(sender, instance)
payloads = build_webhook_payload(sender, instance, deleted=deleted)
logger.debug(
"End building payloads %s for created=%s object=%s", payloads, created, instance
)
Expand Down Expand Up @@ -177,6 +158,55 @@ def process_webhook(
)


@receiver(
post_save,
sender=ModuleTransaction,
dispatch_uid="module_transaction.process_webhook",
)
@receiver(
post_save,
sender=MultisigConfirmation,
dispatch_uid="multisig_confirmation.process_webhook",
)
@receiver(
post_save,
sender=MultisigTransaction,
dispatch_uid="multisig_transaction.process_webhook",
)
@receiver(
post_save, sender=ERC20Transfer, dispatch_uid="erc20_transfer.process_webhook"
)
@receiver(
post_save, sender=ERC721Transfer, dispatch_uid="erc721_transfer.process_webhook"
)
@receiver(post_save, sender=InternalTx, dispatch_uid="internal_tx.process_webhook")
@receiver(post_save, sender=SafeContract, dispatch_uid="safe_contract.process_webhook")
def process_webhook(
sender: Type[Model],
instance: Union[
TokenTransfer,
InternalTx,
MultisigConfirmation,
MultisigTransaction,
SafeContract,
],
created: bool,
**kwargs,
) -> None:
return _process_webhook(sender, instance, created, False)


@receiver(
post_delete,
sender=MultisigTransaction,
dispatch_uid="multisig_transaction.process_delete_webhook",
)
def process_delete_webhook(
sender: Type[Model], instance: MultisigTransaction, *args, **kwargs
):
return _process_webhook(sender, instance, False, True)


@receiver(
post_save,
sender=SafeLastStatus,
Expand Down
58 changes: 58 additions & 0 deletions safe_transaction_service/history/tests/test_signals.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import timedelta
from unittest import mock
from unittest.mock import MagicMock

from django.db.models.signals import post_save
from django.test import TestCase
Expand Down Expand Up @@ -83,6 +84,14 @@ def test_build_webhook_payload(self):
self.assertEqual(payload["type"], WebHookType.PENDING_MULTISIG_TRANSACTION.name)
self.assertEqual(payload["chainId"], str(EthereumNetwork.GANACHE.value))

payload = build_webhook_payload(
MultisigTransaction,
MultisigTransactionFactory(ethereum_tx=None),
deleted=True,
)[0]
self.assertEqual(payload["type"], WebHookType.DELETED_MULTISIG_TRANSACTION.name)
self.assertEqual(payload["chainId"], str(EthereumNetwork.GANACHE.value))

safe_address = self.deploy_test_safe().address
safe_message = SafeMessageFactory(safe=safe_address)
payload = build_webhook_payload(SafeMessage, safe_message)[0]
Expand Down Expand Up @@ -166,3 +175,52 @@ def test_is_relevant_notification_multisig_transaction(self):
self.assertFalse(
is_relevant_notification(multisig_tx.__class__, multisig_tx, created=False)
)

@mock.patch.object(send_webhook_task, "apply_async")
@mock.patch.object(send_event_to_queue_task, "delay")
def test_signals_are_correctly_fired(
self,
send_event_to_queue_task_mock: MagicMock,
webhook_task_mock: MagicMock,
):
# Not trusted txs should not fire any event
MultisigTransactionFactory(trusted=False)
webhook_task_mock.assert_not_called()
send_event_to_queue_task_mock.assert_not_called()

# Trusted txs should fire an event
multisig_tx: MultisigTransaction = MultisigTransactionFactory(trusted=True)
pending_multisig_transaction_payload = {
"address": multisig_tx.safe,
"safeTxHash": multisig_tx.safe_tx_hash,
"type": WebHookType.EXECUTED_MULTISIG_TRANSACTION.name,
"failed": "false",
"txHash": multisig_tx.ethereum_tx_id,
"chainId": str(EthereumNetwork.GANACHE.value),
}
webhook_task_mock.assert_called_with(
args=(multisig_tx.safe, pending_multisig_transaction_payload), priority=2
)
send_event_to_queue_task_mock.assert_called_with(
pending_multisig_transaction_payload
)

# Deleting a tx should fire an event
webhook_task_mock.reset_mock()
send_event_to_queue_task_mock.reset_mock()
safe_tx_hash = multisig_tx.safe_tx_hash
multisig_tx.delete()

deleted_multisig_transaction_payload = {
"address": multisig_tx.safe,
"safeTxHash": safe_tx_hash,
"type": WebHookType.DELETED_MULTISIG_TRANSACTION.name,
"chainId": str(EthereumNetwork.GANACHE.value),
}

webhook_task_mock.assert_called_with(
args=(multisig_tx.safe, deleted_multisig_transaction_payload), priority=2
)
send_event_to_queue_task_mock.assert_called_with(
deleted_multisig_transaction_payload
)
9 changes: 5 additions & 4 deletions safe_transaction_service/safe_messages/tests/test_signals.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from unittest import mock
from unittest.mock import MagicMock

from django.db.models.signals import post_save
from django.test import TestCase
Expand Down Expand Up @@ -28,8 +29,8 @@ class TestSafeMessageSignals(SafeTestCaseMixin, TestCase):
@mock.patch.object(send_event_to_queue_task, "delay")
def test_process_webhook(
self,
send_event_to_queue_task_mock,
webhook_task_mock,
send_event_to_queue_task_mock: MagicMock,
webhook_task_mock: MagicMock,
):
safe_address = self.deploy_test_safe().address
safe_message = SafeMessageFactory(safe=safe_address)
Expand Down Expand Up @@ -64,8 +65,8 @@ def test_process_webhook(
@mock.patch.object(send_event_to_queue_task, "delay")
def test_signals_are_correctly_fired(
self,
send_event_to_queue_task_mock,
webhook_task_mock,
send_event_to_queue_task_mock: MagicMock,
webhook_task_mock: MagicMock,
):
safe_address = self.deploy_test_safe().address
# Create a confirmation should fire a signal and webhooks should be sended
Expand Down

0 comments on commit 034bfde

Please sign in to comment.