Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decode Liquidity additions & removal for Uniswap(V2 & V3) & Sushiswap #5337

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions rotkehlchen/chain/ethereum/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@

RAY_DIGITS = 27
RAY = 10**RAY_DIGITS

ETH_MANTISSA = 10 ** DEFAULT_TOKEN_DECIMALS
17 changes: 9 additions & 8 deletions rotkehlchen/chain/ethereum/decoding/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,20 @@ def _maybe_enrich_transfers(
decoded_events: list[HistoryBaseEntry],
action_items: list[ActionItem], # pylint: disable=unused-argument
all_logs: list[EvmTxReceiptLog], # pylint: disable=unused-argument
) -> Optional[HistoryBaseEntry]:
) -> tuple[Optional[HistoryBaseEntry], list[ActionItem]]:
if tx_log.topics[0] == GTC_CLAIM and tx_log.address == '0xDE3e5a990bCE7fC60a6f017e7c4a95fc4939299E': # noqa: E501
for event in decoded_events:
if event.asset == A_GTC and event.event_type == HistoryEventType.RECEIVE:
event.event_subtype = HistoryEventSubType.AIRDROP
event.notes = f'Claim {event.balance.amount} GTC from the GTC airdrop'
return None
return None, []

if tx_log.topics[0] == ONEINCH_CLAIM and tx_log.address == '0xE295aD71242373C37C5FdA7B57F26f9eA1088AFe': # noqa: E501
for event in decoded_events:
if event.asset == A_1INCH and event.event_type == HistoryEventType.RECEIVE:
event.event_subtype = HistoryEventSubType.AIRDROP
event.notes = f'Claim {event.balance.amount} 1INCH from the 1INCH airdrop' # noqa: E501
return None
return None, []

if tx_log.topics[0] == GNOSIS_CHAIN_BRIDGE_RECEIVE and tx_log.address == '0x88ad09518695c6c3712AC10a214bE5109a655671': # noqa: E501
for event in decoded_events:
Expand All @@ -100,7 +100,7 @@ def _maybe_enrich_transfers(
f'Bridge {event.balance.amount} {crypto_asset.symbol} from gnosis chain'
)

return None
return None, []

def _maybe_decode_governance( # pylint: disable=no-self-use
self,
Expand All @@ -110,7 +110,7 @@ def _maybe_decode_governance( # pylint: disable=no-self-use
decoded_events: list[HistoryBaseEntry], # pylint: disable=unused-argument
action_items: list[ActionItem], # pylint: disable=unused-argument
all_logs: list[EvmTxReceiptLog], # pylint: disable=unused-argument
) -> Optional[HistoryBaseEntry]:
) -> tuple[Optional[HistoryBaseEntry], list[ActionItem]]:
if tx_log.topics[0] == GOVERNORALPHA_PROPOSE:
if tx_log.address == '0xDbD27635A534A3d3169Ef0498beB56Fb9c937489':
governance_name = 'Gitcoin'
Expand All @@ -121,12 +121,12 @@ def _maybe_decode_governance( # pylint: disable=no-self-use
_, decoded_data = decode_event_data_abi_str(tx_log, GOVERNORALPHA_PROPOSE_ABI)
except DeserializationError as e:
log.debug(f'Failed to decode governor alpha event due to {str(e)}')
return None
return None, []

proposal_id = decoded_data[0]
proposal_text = decoded_data[8]
notes = f'Create {governance_name} proposal {proposal_id}. {proposal_text}'
return HistoryBaseEntry(
event = HistoryBaseEntry(
event_identifier=transaction.tx_hash,
sequence_index=self.base.get_sequence_index(tx_log),
timestamp=ts_sec_to_ms(transaction.timestamp),
Expand All @@ -139,8 +139,9 @@ def _maybe_decode_governance( # pylint: disable=no-self-use
event_subtype=HistoryEventSubType.GOVERNANCE,
counterparty=governance_name,
)
return event, []

return None
return None, []

# -- methods that need to be implemented by child classes --

Expand Down
2 changes: 1 addition & 1 deletion rotkehlchen/chain/ethereum/modules/compound/compound.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rotkehlchen.accounting.structures.defi import DefiEvent, DefiEventType
from rotkehlchen.assets.asset import CryptoAsset, EvmToken
from rotkehlchen.assets.utils import symbol_to_asset_or_token, symbol_to_ethereum_token
from rotkehlchen.chain.ethereum.constants import ETH_MANTISSA
from rotkehlchen.chain.ethereum.defi.structures import GIVEN_DEFI_BALANCES
from rotkehlchen.chain.ethereum.graph import Graph, get_common_params
from rotkehlchen.chain.ethereum.utils import token_normalized_value
Expand Down Expand Up @@ -34,7 +35,6 @@
ADDRESS_TO_ASSETS = dict[ChecksumEvmAddress, dict[CryptoAsset, Balance]]
BLOCKS_PER_DAY = 4 * 60 * 24
DAYS_PER_YEAR = 365
ETH_MANTISSA = 10**18


LEND_EVENTS_QUERY_PREFIX = """{graph_event_name}
Expand Down
28 changes: 28 additions & 0 deletions rotkehlchen/chain/ethereum/modules/sushiswap/accountant.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,32 @@ def event_settings(self, pot: 'AccountingPot') -> dict[str, 'TxEventSettings']:
take=2,
multitake_treatment=TxMultitakeTreatment.SWAP,
),
get_tx_event_type_identifier(HistoryEventType.SPEND, HistoryEventSubType.RETURN_WRAPPED, CPT_SUSHISWAP_V2): TxEventSettings( # noqa: E501
taxable=False,
count_entire_amount_spend=False,
count_cost_basis_pnl=True,
method='spend',
take=1,
),
get_tx_event_type_identifier(HistoryEventType.RECEIVE, HistoryEventSubType.RECEIVE_WRAPPED, CPT_SUSHISWAP_V2): TxEventSettings( # noqa: E501
taxable=False,
count_entire_amount_spend=False,
count_cost_basis_pnl=True,
method='acquisition',
take=1,
),
get_tx_event_type_identifier(HistoryEventType.WITHDRAWAL, HistoryEventSubType.REMOVE_ASSET, CPT_SUSHISWAP_V2): TxEventSettings( # noqa: E501
taxable=False,
count_entire_amount_spend=False,
count_cost_basis_pnl=True,
method='acquisition',
take=1,
),
get_tx_event_type_identifier(HistoryEventType.DEPOSIT, HistoryEventSubType.DEPOSIT_ASSET, CPT_SUSHISWAP_V2): TxEventSettings( # noqa: E501
taxable=False,
count_entire_amount_spend=False,
count_cost_basis_pnl=True,
method='spend',
take=1,
),
}
76 changes: 73 additions & 3 deletions rotkehlchen/chain/ethereum/modules/sushiswap/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
from rotkehlchen.chain.ethereum.modules.sushiswap.constants import CPT_SUSHISWAP_V2
from rotkehlchen.chain.ethereum.modules.uniswap.v2.common import (
SUSHISWAP_ROUTER,
decode_uniswap_like_deposit_and_withdrawals,
decode_uniswap_v2_like_swap,
enrich_uniswap_v2_like_lp_tokens_transfers,
)
from rotkehlchen.chain.evm.decoding.interfaces import DecoderInterface
from rotkehlchen.chain.evm.decoding.structures import ActionItem
from rotkehlchen.chain.evm.structures import EvmTxReceiptLog
from rotkehlchen.types import EvmTransaction
from rotkehlchen.chain.evm.types import string_to_evm_address
from rotkehlchen.types import SUSHISWAP_PROTOCOL, EvmTransaction

if TYPE_CHECKING:
from rotkehlchen.chain.ethereum.node_inquirer import EthereumInquirer
Expand All @@ -19,6 +22,11 @@

# https://www.4byte.directory/api/v1/event-signatures/?hex_signature=0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822 # noqa: E501
SWAP_SIGNATURE = b'\xd7\x8a\xd9_\xa4l\x99KeQ\xd0\xda\x85\xfc\'_\xe6\x13\xce7e\x7f\xb8\xd5\xe3\xd10\x84\x01Y\xd8"' # noqa: E501
MINT_SIGNATURE = b'L \x9b_\xc8\xadPu\x8f\x13\xe2\xe1\x08\x8b\xa5jV\r\xffi\n\x1co\xef&9OL\x03\x82\x1cO' # noqa: E501
BURN_SIGNATURE = b'\xdc\xcdA/\x0b\x12R\x81\x9c\xb1\xfd3\x0b\x93"L\xa4&\x12\x89+\xb3\xf4\xf7\x89\x97nm\x81\x93d\x96' # noqa: E501

SUSHISWAP_V2_FACTORY = string_to_evm_address('0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac')
SUSHISWAP_V2_INIT_CODE_HASH = '0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303' # noqa: E501


class SushiswapDecoder(DecoderInterface):
Expand All @@ -44,9 +52,9 @@ def _maybe_decode_v2_swap( # pylint: disable=no-self-use
decoded_events: list[HistoryBaseEntry],
action_items: list[ActionItem], # pylint: disable=unused-argument
all_logs: list[EvmTxReceiptLog], # pylint: disable=unused-argument
) -> None:
) -> tuple[Optional[HistoryBaseEntry], list[ActionItem]]:
if tx_log.topics[0] == SWAP_SIGNATURE and transaction.to_address == SUSHISWAP_ROUTER:
decode_uniswap_v2_like_swap(
return decode_uniswap_v2_like_swap(
tx_log=tx_log,
decoded_events=decoded_events,
transaction=transaction,
Expand All @@ -55,12 +63,74 @@ def _maybe_decode_v2_swap( # pylint: disable=no-self-use
ethereum_inquirer=self.ethereum,
notify_user=self.notify_user,
)
return None, []

def _maybe_decode_v2_liquidity_addition_and_removal( # pylint: disable=no-self-use
self,
token: Optional[EvmToken], # pylint: disable=unused-argument
tx_log: EvmTxReceiptLog,
transaction: EvmTransaction, # pylint: disable=unused-argument
decoded_events: list[HistoryBaseEntry],
action_items: list[ActionItem], # pylint: disable=unused-argument
all_logs: list[EvmTxReceiptLog],
) -> tuple[Optional[HistoryBaseEntry], list[ActionItem]]:
if tx_log.topics[0] == MINT_SIGNATURE:
return decode_uniswap_like_deposit_and_withdrawals(
tx_log=tx_log,
decoded_events=decoded_events,
all_logs=all_logs,
event_action_type='addition',
counterparty=CPT_SUSHISWAP_V2,
ethereum_inquirer=self.ethereum,
database=self.ethereum.database,
factory_address=SUSHISWAP_V2_FACTORY,
init_code_hash=SUSHISWAP_V2_INIT_CODE_HASH,
)
if tx_log.topics[0] == BURN_SIGNATURE:
return decode_uniswap_like_deposit_and_withdrawals(
tx_log=tx_log,
decoded_events=decoded_events,
all_logs=all_logs,
event_action_type='removal',
counterparty=CPT_SUSHISWAP_V2,
ethereum_inquirer=self.ethereum,
database=self.ethereum.database,
factory_address=SUSHISWAP_V2_FACTORY,
init_code_hash=SUSHISWAP_V2_INIT_CODE_HASH,
)
return None, []

@staticmethod
def _maybe_enrich_lp_tokens_transfers(
token: EvmToken, # pylint: disable=unused-argument
tx_log: EvmTxReceiptLog, # pylint: disable=unused-argument
transaction: EvmTransaction, # pylint: disable=unused-argument
event: HistoryBaseEntry,
action_items: list[ActionItem], # pylint: disable=unused-argument
all_logs: list[EvmTxReceiptLog], # pylint: disable=unused-argument
) -> bool:
return enrich_uniswap_v2_like_lp_tokens_transfers(
token=token,
tx_log=tx_log,
transaction=transaction,
event=event,
action_items=action_items,
all_logs=all_logs,
counterparty=CPT_SUSHISWAP_V2,
lp_token_symbol=SUSHISWAP_PROTOCOL,
)

# -- DecoderInterface methods

def decoding_rules(self) -> list[Callable]:
return [
self._maybe_decode_v2_swap,
self._maybe_decode_v2_liquidity_addition_and_removal,
]

def enricher_rules(self) -> list[Callable]:
return [
self._maybe_enrich_lp_tokens_transfers,
]

def counterparties(self) -> list[str]:
Expand Down
7 changes: 5 additions & 2 deletions rotkehlchen/chain/ethereum/modules/uniswap/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from rotkehlchen.chain.ethereum.modules.uniswap.constants import CPT_UNISWAP_V2, CPT_UNISWAP_V3
from rotkehlchen.chain.ethereum.utils import asset_normalized_value
from rotkehlchen.chain.evm.contracts import EvmContract
from rotkehlchen.chain.evm.decoding.structures import ActionItem
from rotkehlchen.chain.evm.decoding.utils import maybe_reshuffle_events
from rotkehlchen.chain.evm.types import WeightedNode
from rotkehlchen.constants import ZERO
Expand Down Expand Up @@ -249,7 +250,7 @@ def decode_basic_uniswap_info(
decoded_events: list[HistoryBaseEntry],
counterparty: str,
notify_user: Callable[[HistoryBaseEntry, str], None],
) -> None:
) -> tuple[Optional[HistoryBaseEntry], list[ActionItem]]:
"""
Check last three events and if they are related to the swap, label them as such.
We check three events because potential events are: spend, (optionally) approval, receive.
Expand All @@ -261,7 +262,7 @@ def decode_basic_uniswap_info(
crypto_asset = event.asset.resolve_to_crypto_asset()
except (UnknownAsset, WrongAssetType):
notify_user(event, counterparty)
return
return None, []

if (
event.event_type == HistoryEventType.INFORMATIONAL and
Expand Down Expand Up @@ -323,3 +324,5 @@ def decode_basic_uniswap_info(
maybe_reshuffle_events(out_event=approval_event, in_event=spend_event)
if spend_event is not None and receive_event is not None:
maybe_reshuffle_events(out_event=spend_event, in_event=receive_event)

return None, []
3 changes: 2 additions & 1 deletion rotkehlchen/chain/ethereum/modules/uniswap/v1/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def _maybe_decode_swap( # pylint: disable=no-self-use
decoded_events: list[HistoryBaseEntry],
action_items: list[ActionItem], # pylint: disable=unused-argument
all_logs: list[EvmTxReceiptLog], # pylint: disable=unused-argument
) -> None:
) -> tuple[Optional[HistoryBaseEntry], list[ActionItem]]:
"""Search for both events. Since the order is not guaranteed try reshuffle in both cases"""
out_event = in_event = None
if tx_log.topics[0] == TOKEN_PURCHASE:
Expand Down Expand Up @@ -92,6 +92,7 @@ def _maybe_decode_swap( # pylint: disable=no-self-use
out_event = event

maybe_reshuffle_events(out_event=out_event, in_event=in_event)
return None, []

# -- DecoderInterface methods

Expand Down
28 changes: 28 additions & 0 deletions rotkehlchen/chain/ethereum/modules/uniswap/v2/accountant.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,32 @@ def event_settings(self, pot: 'AccountingPot') -> dict[str, 'TxEventSettings']:
take=2,
multitake_treatment=TxMultitakeTreatment.SWAP,
),
get_tx_event_type_identifier(HistoryEventType.SPEND, HistoryEventSubType.RETURN_WRAPPED, CPT_UNISWAP_V2): TxEventSettings( # noqa: E501
taxable=False,
count_entire_amount_spend=False,
count_cost_basis_pnl=True,
method='spend',
take=1,
),
get_tx_event_type_identifier(HistoryEventType.RECEIVE, HistoryEventSubType.RECEIVE_WRAPPED, CPT_UNISWAP_V2): TxEventSettings( # noqa: E501
taxable=False,
count_entire_amount_spend=False,
count_cost_basis_pnl=True,
method='acquisition',
take=1,
),
get_tx_event_type_identifier(HistoryEventType.WITHDRAWAL, HistoryEventSubType.REMOVE_ASSET, CPT_UNISWAP_V2): TxEventSettings( # noqa: E501
taxable=False,
count_entire_amount_spend=False,
count_cost_basis_pnl=True,
method='acquisition',
take=1,
),
get_tx_event_type_identifier(HistoryEventType.DEPOSIT, HistoryEventSubType.DEPOSIT_ASSET, CPT_UNISWAP_V2): TxEventSettings( # noqa: E501
taxable=False,
count_entire_amount_spend=False,
count_cost_basis_pnl=True,
method='spend',
take=1,
),
}
Loading