Repository: rotki/rotki

Issue:
Support custom swap events in accounting
## Problem

Currently, custom swap events are ignored during accounting because:
1.  there's no default accounting setting to handle it
2. the accountant for the specified counterparty does not support it.

Discussion is here -> https://discord.com/channels/657906918408585217/683245846070165504/1080865282396004423

## Specification

- Create a default accounting setting to handle such cases/scenario(swaps, etc.) 


Discussion:
Note: "custom swap events are ignored during accounting" is not true. Only swaps that have incorrect counterparty set get ignored.

> Create a default accounting setting to handle such cases/scenario(swaps, etc.)

There already is a default setting. What needs to change (and I would say to change not only for swaps, but for other events as well) is that if there is no setting found for a particular counterparty -- fallback to default settings.
Custom swap events means any counterparty so the description is correct.

For swaps it should fall back to default settings, but for other events it depends on the type of event


Resolving patch:
diff --git a/rotkehlchen/accounting/structures/base.py b/rotkehlchen/accounting/structures/base.py
--- a/rotkehlchen/accounting/structures/base.py
+++ b/rotkehlchen/accounting/structures/base.py
@@ -289,10 +289,14 @@ def __str__(self) -> str:
     def get_timestamp_in_sec(self) -> Timestamp:
         return ts_ms_to_sec(self.timestamp)
 
-    def get_type_identifier(self) -> str:
-        """A unique type identifier for known event types"""
+    def get_type_identifier(self, include_counterparty: bool = True) -> str:
+        """
+        A unique type identifier for known event types.
+        Computes the identifier from event type, event subtype and counterparty if
+        `include_counterparty` is True.
+        """
         identifier = str(self.event_type) + '__' + str(self.event_subtype)
-        if self.counterparty and not self.counterparty.startswith('0x'):
+        if include_counterparty and self.counterparty and not self.counterparty.startswith('0x'):
             identifier += '__' + self.counterparty
 
         return identifier
diff --git a/rotkehlchen/accounting/transactions.py b/rotkehlchen/accounting/transactions.py
--- a/rotkehlchen/accounting/transactions.py
+++ b/rotkehlchen/accounting/transactions.py
@@ -60,11 +60,18 @@ def process(
         type_identifier = event.get_type_identifier()
         event_settings = self.tx_event_settings.get(type_identifier, None)
         if event_settings is None:
-            log.debug(
-                f'During transaction accounting found transaction event {event} '
-                f'with no mapped event settings. Skipping...',
+            event_settings = self.tx_event_settings.get(
+                event.get_type_identifier(include_counterparty=False),
             )
-            return 1
+            if (  # For swaps we have a default treatment but for the rest we bail
+                event_settings is None or
+                event_settings.accounting_treatment != TxAccountingTreatment.SWAP
+            ):
+                log.debug(
+                    f'During transaction accounting found transaction event {event} '
+                    f'with no mapped event settings. Skipping...',
+                )
+                return 1
 
         # if there is any module specific accountant functionality call it
         if event_settings.accountant_cb is not None:


Test patch:
diff --git a/rotkehlchen/tests/unit/accounting/test_default_settings.py b/rotkehlchen/tests/unit/accounting/test_default_settings.py
--- a/rotkehlchen/tests/unit/accounting/test_default_settings.py
+++ b/rotkehlchen/tests/unit/accounting/test_default_settings.py
@@ -265,8 +265,12 @@ def test_accounting_spend_settings(
 
 
 @pytest.mark.parametrize('mocked_price_queries', [MOCKED_PRICES])
-def test_accounting_swap_settings(accounting_pot: 'AccountingPot'):
-    """Test that the default accounting settings for swaps are correct"""
+@pytest.mark.parametrize('counterparty', [None, 'nonexistent_counterparty'])
+def test_accounting_swap_settings(accounting_pot: 'AccountingPot', counterparty: str):
+    """
+    Test that the default accounting settings for swaps are correct.
+    Also checks that if counterparty is not known we fallback to default swaps treatment.
+    """
     _gain_one_ether(transactions=accounting_pot.transactions)
     swap_spend_event = HistoryBaseEntry(
         event_identifier=EXAMPLE_EVM_HASH,
@@ -279,6 +283,7 @@ def test_accounting_swap_settings(accounting_pot: 'AccountingPot'):
         notes='Swap 1 ETH in a uniswap pool',
         event_type=HistoryEventType.TRADE,
         event_subtype=HistoryEventSubType.SPEND,
+        counterparty=counterparty,
     )
     swap_receive_event = HistoryBaseEntry(
         event_identifier=EXAMPLE_EVM_HASH,
@@ -291,6 +296,7 @@ def test_accounting_swap_settings(accounting_pot: 'AccountingPot'):
         notes='Receive 3000 DAI as the result of a swap',
         event_type=HistoryEventType.TRADE,
         event_subtype=HistoryEventSubType.RECEIVE,
+        counterparty=counterparty,
     )
     consumed_num = accounting_pot.transactions.process(
         event=swap_spend_event,