def test_compound_ether_withdraw(database, ethereum_manager, function_scope_messages_aggregator): """Data taken from: https://etherscan.io/tx/0x024bd402420c3ba2f95b875f55ce2a762338d2a14dac4887b78174254c9ab807 """ # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed tx_hash = deserialize_evm_tx_hash( '0x024bd402420c3ba2f95b875f55ce2a762338d2a14dac4887b78174254c9ab807' ) # noqa: E501 events = get_decoded_events_of_transaction( ethereum_manager=ethereum_manager, database=database, msg_aggregator=function_scope_messages_aggregator, tx_hash=tx_hash, ) expected_events = [ HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=0, timestamp=1598813490000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, asset=A_ETH, balance=Balance(amount=FVal('0.02858544'), usd_value=ZERO), location_label=ADDY, notes=f'Burned 0.02858544 ETH in gas from {ADDY}', counterparty=CPT_GAS, ), HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=1, timestamp=1598813490000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.RETURN_WRAPPED, asset=A_CETH, balance=Balance(amount=FVal('24.97649991'), usd_value=ZERO), location_label=ADDY, notes='Return 24.97649991 cETH to compound', counterparty=CPT_COMPOUND, ), HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=2, timestamp=1598813490000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.WITHDRAWAL, event_subtype=HistoryEventSubType.REMOVE_ASSET, asset=A_ETH, balance=Balance(amount=FVal('0.500003923413507454'), usd_value=ZERO), location_label=ADDY, notes='Withdraw 0.500003923413507454 ETH from compound', counterparty=CPT_COMPOUND, ) ] assert events == expected_events
def test_compound_ether_deposit(database, ethereum_manager, function_scope_messages_aggregator): """Data taken from: https://etherscan.io/tx/0x06a8b9f758b0471886186c2a48dea189b3044916c7f94ee7f559026fefd91c39 """ # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed tx_hash = deserialize_evm_tx_hash( '0x06a8b9f758b0471886186c2a48dea189b3044916c7f94ee7f559026fefd91c39' ) # noqa: E501 events = get_decoded_events_of_transaction( ethereum_manager=ethereum_manager, database=database, msg_aggregator=function_scope_messages_aggregator, tx_hash=tx_hash, ) expected_events = [ HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=0, timestamp=1598639099000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, asset=A_ETH, balance=Balance(amount=FVal('0.014122318'), usd_value=ZERO), location_label=ADDY, notes=f'Burned 0.014122318 ETH in gas from {ADDY}', counterparty=CPT_GAS, ), HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=1, timestamp=1598639099000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.DEPOSIT, event_subtype=HistoryEventSubType.DEPOSIT_ASSET, asset=A_ETH, balance=Balance(amount=FVal('0.5'), usd_value=ZERO), location_label=ADDY, notes='Deposit 0.5 ETH to compound', counterparty=CPT_COMPOUND, ), HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=33, timestamp=1598639099000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.RECEIVE, event_subtype=HistoryEventSubType.RECEIVE_WRAPPED, asset=A_CETH, balance=Balance(amount=FVal('24.97649991'), usd_value=ZERO), location_label=ADDY, notes='Receive 24.97649991 cETH from compound', counterparty=CPT_COMPOUND, ) ] assert events == expected_events
def test_tx_decode(evm_transaction_decoder, database): dbethtx = DBEthTx(database) addr1 = '0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12' approve_tx_hash = deserialize_evm_tx_hash('0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a') # noqa: E501 transactions = dbethtx.get_ethereum_transactions( filter_=ETHTransactionsFilterQuery.make( addresses=[addr1], tx_hash=approve_tx_hash, ), has_premium=True, ) decoder = evm_transaction_decoder with patch.object(decoder, 'decode_transaction', wraps=decoder.decode_transaction) as decode_mock: # noqa: E501 for tx in transactions: receipt = dbethtx.get_receipt(tx.tx_hash) assert receipt is not None, 'all receipts should be queried in the test DB' events = decoder.get_or_decode_transaction_events(tx, receipt, ignore_cache=False) if tx.tx_hash == approve_tx_hash: assert len(events) == 2 assert_events_equal(events[0], HistoryBaseEntry( # The no-member is due to https://github.com/PyCQA/pylint/issues/3162 event_identifier=approve_tx_hash.hex(), # pylint: disable=no-member sequence_index=0, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=addr1, asset=A_ETH, balance=Balance(amount=FVal('0.000030921')), # The no-member is due to https://github.com/PyCQA/pylint/issues/3162 notes=f'Burned 0.000030921 ETH in gas from {addr1}', event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_GAS, )) assert_events_equal(events[1], HistoryBaseEntry( # The no-member is due to https://github.com/PyCQA/pylint/issues/3162 event_identifier=approve_tx_hash.hex(), # pylint: disable=no-member sequence_index=163, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=addr1, asset=A_SAI, balance=Balance(amount=1), notes=f'Approve 1 SAI of {addr1} for spending by 0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE', # noqa: E501 event_type=HistoryEventType.INFORMATIONAL, event_subtype=HistoryEventSubType.APPROVE, counterparty='0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE', )) assert decode_mock.call_count == len(transactions) # now go again, and see that no more decoding happens as it's all pulled from the DB for tx in transactions: receipt = dbethtx.get_receipt(tx.tx_hash) assert receipt is not None, 'all receipts should be queried in the test DB' events = decoder.get_or_decode_transaction_events(tx, receipt, ignore_cache=False) assert decode_mock.call_count == len(transactions)
def test_kraken_staking_events(accountant, google_service): """ Test that staking events from kraken are correctly processed """ history = [ HistoryBaseEntry( event_identifier='XXX', sequence_index=0, timestamp=1640493374000, location=Location.KRAKEN, location_label='Kraken 1', asset=A_ETH2, balance=Balance( amount=FVal(0.0000541090), usd_value=FVal(0.212353475950), ), notes=None, event_type=HistoryEventType.STAKING, event_subtype=HistoryEventSubType.REWARD, ), HistoryBaseEntry( event_identifier='YYY', sequence_index=0, timestamp=1636638550000, location=Location.KRAKEN, location_label='Kraken 1', asset=A_ETH2, balance=Balance( amount=FVal(0.0000541090), usd_value=FVal(0.212353475950), ), notes=None, event_type=HistoryEventType.STAKING, event_subtype=HistoryEventSubType.REWARD, ) ] _, events = accounting_history_process( accountant, start_ts=1636638549, end_ts=1640493376, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.STAKING: PNL(taxable=FVal('0.471505826'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service) assert len(events) == 2 expected_pnls = [FVal('0.25114638241'), FVal('0.22035944359')] for idx, event in enumerate(events): assert event.pnl.taxable == expected_pnls[idx] assert event.type == AccountingEventType.STAKING
def test_gitcoin_old_donation(database, ethereum_manager, function_scope_messages_aggregator): """Data taken from https://etherscan.io/tx/0x811ba23a10c76111289133ec6f90d3c33a604baa50053739210e870687a456d9 """ # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed tx_hash = deserialize_evm_tx_hash('0x811ba23a10c76111289133ec6f90d3c33a604baa50053739210e870687a456d9') # noqa: E501 events = get_decoded_events_of_transaction( ethereum_manager=ethereum_manager, database=database, msg_aggregator=function_scope_messages_aggregator, tx_hash=tx_hash, ) expected_events = [ HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=0, timestamp=1569924574000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, asset=A_ETH, balance=Balance(amount=FVal('0.000055118'), usd_value=ZERO), location_label=ADDY, notes=f'Burned 0.000055118 ETH in gas from {ADDY}', counterparty=CPT_GAS, ), HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=164, timestamp=1569924574000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.DONATE, asset=A_SAI, balance=Balance(amount=FVal('0.95'), usd_value=ZERO), location_label=ADDY, notes='Donate 0.95 SAI to 0xEbDb626C95a25f4e304336b1adcAd0521a1Bdca1 via gitcoin', # noqa: E501 counterparty=CPT_GITCOIN, ), HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=165, timestamp=1569924574000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.DONATE, asset=A_SAI, balance=Balance(amount=FVal('0.05'), usd_value=ZERO), location_label=ADDY, notes='Donate 0.05 SAI to 0x00De4B13153673BCAE2616b67bf822500d325Fc3 via gitcoin', # noqa: E501 counterparty=CPT_GITCOIN, ), ] assert events == expected_events
def _maybe_decode_erc20_approve( self, token: Optional[EthereumToken], tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, decoded_events: List[HistoryBaseEntry], # pylint: disable=unused-argument action_items: List[ActionItem], # pylint: disable=unused-argument ) -> Optional[HistoryBaseEntry]: if tx_log.topics[0] != ERC20_APPROVE or token is None: return None owner_address = hex_or_bytes_to_address(tx_log.topics[1]) spender_address = hex_or_bytes_to_address(tx_log.topics[2]) if not any( self.base.is_tracked(x) for x in (owner_address, spender_address)): return None amount_raw = hex_or_bytes_to_int(tx_log.data) amount = token_normalized_value(token_amount=amount_raw, token=token) notes = f'Approve {amount} {token.symbol} of {owner_address} for spending by {spender_address}' # noqa: E501 return HistoryBaseEntry( event_identifier=transaction.tx_hash.hex(), sequence_index=self.base.get_sequence_index(tx_log), timestamp=ts_sec_to_ms(transaction.timestamp), location=Location.BLOCKCHAIN, location_label=owner_address, asset=token, balance=Balance(amount=amount), notes=notes, event_type=HistoryEventType.INFORMATIONAL, event_subtype=HistoryEventSubType.APPROVE, counterparty=spender_address, )
def decode_proxy_creation( self, tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, decoded_events: List[HistoryBaseEntry], # pylint: disable=unused-argument all_logs: List[EthereumTxReceiptLog], # pylint: disable=unused-argument action_items: List[ActionItem], # pylint: disable=unused-argument ) -> Tuple[Optional[HistoryBaseEntry], Optional[ActionItem]]: if tx_log.topics[ 0] == b'%\x9b0\xca9\x88\\m\x80\x1a\x0b]\xbc\x98\x86@\xf3\xc2^/7S\x1f\xe18\xc5\xc5\xaf\x89U\xd4\x1b': # noqa: E501 owner_address = hex_or_bytes_to_address(tx_log.topics[2]) if not self.base.is_tracked(owner_address): return None, None proxy_address = hex_or_bytes_to_address(tx_log.data[0:32]) notes = f'Create DSR proxy {proxy_address} with owner {owner_address}' event = HistoryBaseEntry( event_identifier=transaction.tx_hash.hex(), sequence_index=self.base.get_sequence_index(tx_log), timestamp=ts_sec_to_ms(transaction.timestamp), location=Location.BLOCKCHAIN, location_label=owner_address, # TODO: This should be null for proposals and other informational events asset=A_ETH, balance=Balance(), notes=notes, event_type=HistoryEventType.INFORMATIONAL, event_subtype=HistoryEventSubType.DEPLOY, counterparty=proxy_address, ) return event, None return None, None
def edit_history_event(self, event: HistoryBaseEntry) -> Tuple[bool, str]: """Edit a history entry to the DB. Returns the edited entry""" cursor = self.db.conn.cursor() try: cursor.execute( 'UPDATE history_events SET event_identifier=?, sequence_index=?, timestamp=?, ' 'location=?, location_label=?, asset=?, amount=?, usd_value=?, notes=?, ' 'type=?, subtype=?, counterparty=?, extra_data=? WHERE identifier=?', (*event.serialize_for_db(), event.identifier), ) except sqlcipher.IntegrityError: # pylint: disable=no-member msg = ( f'Tried to edit event to have event_identifier {event.event_identifier} and ' f'sequence_index {event.sequence_index} but it already exists' ) return False, msg if cursor.rowcount != 1: msg = f'Tried to edit event with id {event.identifier} but could not find it in the DB' return False, msg cursor.execute( 'INSERT OR IGNORE INTO history_events_mappings(parent_identifier, value) ' 'VALUES(?, ?)', (event.identifier, HISTORY_MAPPING_CUSTOMIZED), ) self.db.update_last_write() return True, ''
def add_history_event( self, event: HistoryBaseEntry, mapping_value: Optional[str] = None, ) -> int: """Insert a single history entry to the DB. Returns its identifier. Optionally map it to a specific value used to map attributes to some events May raise: - DeserializationError if the event could not be serialized for the DB - sqlcipher.DatabaseError: If anything went wrong at insertion """ cursor = self.db.conn.cursor() cursor.execute(HISTORY_INSERT, event.serialize_for_db()) identifier = cursor.lastrowid if mapping_value is not None: cursor.execute( 'INSERT OR IGNORE INTO history_events_mappings(parent_identifier, value) ' 'VALUES(?, ?)', (identifier, mapping_value), ) self.db.update_last_write() return identifier
def get_history_events( self, filter_query: HistoryEventFilterQuery, has_premium: bool, ) -> List[HistoryBaseEntry]: """ Get history events using the provided query filter """ query, bindings = filter_query.prepare() cursor = self.db.conn.cursor() if has_premium: query = 'SELECT * from history_events ' + query results = cursor.execute(query, bindings) else: query = 'SELECT * FROM (SELECT * from history_events ORDER BY timestamp DESC, sequence_index ASC LIMIT ?) ' + query # noqa: E501 results = cursor.execute(query, [FREE_HISTORY_EVENTS_LIMIT] + bindings) output = [] for entry in results: try: output.append(HistoryBaseEntry.deserialize_from_db(entry)) except (DeserializationError, UnknownAsset) as e: log.debug(f'Failed to deserialize history event {entry} due to {str(e)}') return output
def _decode_vault_creation( self, tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, decoded_events: List[HistoryBaseEntry], # pylint: disable=unused-argument all_logs: List[EthereumTxReceiptLog], # pylint: disable=unused-argument ) -> Tuple[Optional[HistoryBaseEntry], Optional[ActionItem]]: owner_address = self._get_address_or_proxy( hex_or_bytes_to_address(tx_log.topics[2])) if owner_address is None: return None, None if not self.base.is_tracked(owner_address): return None, None cdp_id = hex_or_bytes_to_int(tx_log.topics[3]) notes = f'Create MakerDAO vault with id {cdp_id} and owner {owner_address}' event = HistoryBaseEntry( event_identifier=transaction.tx_hash.hex(), sequence_index=self.base.get_sequence_index(tx_log), timestamp=ts_sec_to_ms(transaction.timestamp), location=Location.BLOCKCHAIN, location_label=owner_address, # TODO: This should be null for proposals and other informational events asset=A_ETH, balance=Balance(), notes=notes, event_type=HistoryEventType.INFORMATIONAL, event_subtype=HistoryEventSubType.DEPLOY, counterparty='makerdao vault', ) return event, None
def test_receiving_value_from_tx(accountant, google_service): """ Test that receiving a transaction that provides value works fine """ addr2 = make_ethereum_address() tx_hash = '0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a' history = [ HistoryBaseEntry( event_identifier=tx_hash, sequence_index=0, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=make_ethereum_address(), asset=A_ETH, balance=Balance(amount=FVal('1.5')), notes=f'Received 1.5 ETH from {addr2}', event_type=HistoryEventType.RECEIVE, event_subtype=HistoryEventSubType.NONE, counterparty=addr2, ) ] accounting_history_process( accountant, start_ts=0, end_ts=1640493376, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRANSACTION_EVENT: PNL(taxable=FVal('242.385'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_hop_l2_deposit(database, ethereum_manager, function_scope_messages_aggregator): """Data taken from https://etherscan.io/tx/0xd46640417a686b399b2f2a920b0c58a35095759365cbe7b795bddec34b8c5eee """ # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed tx_hash = deserialize_evm_tx_hash( '0xd46640417a686b399b2f2a920b0c58a35095759365cbe7b795bddec34b8c5eee' ) # noqa: E501 events = get_decoded_events_of_transaction( ethereum_manager=ethereum_manager, database=database, msg_aggregator=function_scope_messages_aggregator, tx_hash=tx_hash, ) expected_events = [ HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=0, timestamp=1653219722000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, asset=A_ETH, balance=Balance(amount=FVal('0.001964214783875487')), location_label=ADDY, notes=f'Burned 0.001964214783875487 ETH in gas from {ADDY}', counterparty=CPT_GAS, ), HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=1, timestamp=1653219722000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.TRANSFER, event_subtype=HistoryEventSubType.BRIDGE, asset=A_ETH, balance=Balance(amount=FVal('0.2')), location_label=ADDY, notes= 'Bridge 0.2 ETH to Optimism at the same address via Hop protocol', counterparty=CPT_HOP, ) ] assert expected_events == events
def _decode_order_placement( # pylint: disable=no-self-use self, tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, # pylint: disable=unused-argument decoded_events: List[HistoryBaseEntry], # pylint: disable=unused-argument all_logs: List[EthereumTxReceiptLog], # pylint: disable=unused-argument action_items: List[ActionItem], # pylint: disable=unused-argument ) -> Tuple[Optional[HistoryBaseEntry], Optional[ActionItem]]: """Some docs: https://docs.gnosis.io/protocol/docs/tutorial-limit-orders/""" topic_data, log_data = self.contract.decode_event( tx_log=tx_log, event_name='OrderPlacement', argument_names=('owner', 'index', 'buyToken', 'sellToken', 'validFrom', 'validUntil', 'priceNumerator', 'priceDenominator'), # noqa: E501 ) owner = topic_data[0] if not self.base.is_tracked(owner): return None, None result = multicall_specific( ethereum=self.ethereum, contract=self.contract, method_name='tokenIdToAddressMap', arguments=[[topic_data[1]], [topic_data[2]]], ) # The resulting addresses are non checksumed but they can be found in the DB buy_token = ethaddress_to_asset(result[0][0]) if buy_token is None: return None, None sell_token = ethaddress_to_asset(result[1][0]) if sell_token is None: return None, None buy_amount = asset_normalized_value(amount=log_data[3], asset=buy_token) sell_amount = asset_normalized_value(amount=log_data[4], asset=sell_token) event = HistoryBaseEntry( event_identifier=transaction.tx_hash.hex(), sequence_index=self.base.get_sequence_index(tx_log), timestamp=ts_sec_to_ms(transaction.timestamp), location=Location.BLOCKCHAIN, location_label=owner, # Asset means nothing here since the event is informational. TODO: Improve? asset=sell_token, balance=Balance(amount=sell_amount), notes= f'Place an order in DXDao Mesa to sell {sell_amount} {sell_token.symbol} for {buy_amount} {buy_token.symbol}', # noqa: E501 event_type=HistoryEventType.INFORMATIONAL, event_subtype=HistoryEventSubType.PLACE_ORDER, counterparty=CPT_DXDAO_MESA, ) return event, None
def test_claim_aidrop(database, ethereum_manager, function_scope_messages_aggregator): """Data taken from https://etherscan.io/tx/0x1e58aed1baf70b57e6e3e880e1890e7fe607fddc94d62986c38fe70e483e594b """ # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed tx_hash = deserialize_evm_tx_hash('0x1e58aed1baf70b57e6e3e880e1890e7fe607fddc94d62986c38fe70e483e594b') # noqa: E501 events = get_decoded_events_of_transaction( ethereum_manager=ethereum_manager, database=database, msg_aggregator=function_scope_messages_aggregator, tx_hash=tx_hash, ) expected_events = [ HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=0, timestamp=1652910214000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, asset=A_ETH, balance=Balance(amount=FVal('0.0061843862')), location_label=ADDY, notes=f'Burned 0.0061843862 ETH in gas from {ADDY}', counterparty=CPT_GAS, ), HistoryBaseEntry( event_identifier=tx_hash.hex(), # pylint: disable=no-member sequence_index=549, timestamp=1652910214000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.RECEIVE, event_subtype=HistoryEventSubType.AIRDROP, asset=A_ELFI, balance=Balance(amount=FVal('613.8986657935664')), location_label=ADDY, notes='Claim 613.8986657935664 ELFI from element-finance airdrop and delegate it to 0x7BAFC0D5c5892f2041FD9F2415A7611042218e22', # noqa: E501 counterparty=CPT_ELEMENT_FINANCE, ), ] assert events == expected_events
def _maybe_enrich_curve_transfers( # pylint: disable=no-self-use self, token: EthereumToken, # pylint: disable=unused-argument tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, event: HistoryBaseEntry, action_items: List[ActionItem], # pylint: disable=unused-argument ) -> bool: source_address = hex_or_bytes_to_address(tx_log.topics[1]) to_address = hex_or_bytes_to_address(tx_log.topics[2]) if ( # deposit give asset event.event_type == HistoryEventType.RECEIVE and event.event_subtype == HistoryEventSubType.NONE and source_address == CURVE_Y_DEPOSIT and transaction.from_address == to_address ): event.event_type = HistoryEventType.WITHDRAWAL event.event_subtype = HistoryEventSubType.REMOVE_ASSET event.counterparty = CPT_CURVE event.notes = f'Receive {event.balance.amount} {event.asset.symbol} from the curve pool {CURVE_Y_DEPOSIT}' # noqa: E501 return True return False
def _maybe_enrich_gitcoin_transfers( # pylint: disable=no-self-use self, token: EthereumToken, # pylint: disable=unused-argument tx_log: EthereumTxReceiptLog, # pylint: disable=unused-argument transaction: EthereumTransaction, event: HistoryBaseEntry, action_items: List[ActionItem], # pylint: disable=unused-argument ) -> bool: if transaction.to_address not in ( '0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE', '0x7d655c57f71464B6f83811C55D84009Cd9f5221C', ): return False if event.event_type == HistoryEventType.SPEND: to_address = event.counterparty event.notes = f'Donate {event.balance.amount} {event.asset.symbol} to {to_address} via gitcoin' # noqa: E501 else: # can only be RECEIVE from_address = event.counterparty event.notes = f'Receive donation of {event.balance.amount} {event.asset.symbol} from {from_address} via gitcoin' # noqa: E501 event.event_subtype = HistoryEventSubType.DONATE event.counterparty = CPT_GITCOIN return True
def _decode_elfi_claim( # pylint: disable=no-self-use self, tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, # pylint: disable=unused-argument decoded_events: List[HistoryBaseEntry], # pylint: disable=unused-argument all_logs: List[EthereumTxReceiptLog], # pylint: disable=unused-argument action_items: List[ActionItem], # pylint: disable=unused-argument ) -> Tuple[Optional[HistoryBaseEntry], Optional[ActionItem]]: """Example: https://etherscan.io/tx/0x1e58aed1baf70b57e6e3e880e1890e7fe607fddc94d62986c38fe70e483e594b """ if tx_log.topics[0] != ELFI_VOTE_CHANGE: return None, None user_address = hex_or_bytes_to_address(tx_log.topics[1]) delegate_address = hex_or_bytes_to_address(tx_log.topics[2]) raw_amount = hex_or_bytes_to_int(tx_log.data[0:32]) amount = asset_normalized_value(amount=raw_amount, asset=A_ELFI) # now we need to find the transfer, but can't use decoded events # since the transfer is from one of at least 2 airdrop contracts to # vote/locking contract. Since neither the from, nor the to is a # tracked address there won't be a decoded transfer. So we search for # the raw log for other_log in all_logs: if other_log.topics[0] != ERC20_OR_ERC721_TRANSFER: continue transfer_raw = hex_or_bytes_to_int(other_log.data[0:32]) if other_log.address == A_ELFI.ethereum_address and transfer_raw == raw_amount: delegate_str = 'self-delegate' if user_address == delegate_address else f'delegate it to {delegate_address}' # noqa: E501 event = HistoryBaseEntry( event_identifier=transaction.tx_hash.hex(), sequence_index=self.base.get_sequence_index(tx_log), timestamp=ts_sec_to_ms(transaction.timestamp), location=Location.BLOCKCHAIN, location_label=user_address, asset=A_ELFI, balance=Balance(amount=amount), notes= f'Claim {amount} ELFI from element-finance airdrop and {delegate_str}', event_type=HistoryEventType.RECEIVE, event_subtype=HistoryEventSubType.AIRDROP, counterparty=CPT_ELEMENT_FINANCE, ) return event, None return None, None
def _decode_swapped( # pylint: disable=no-self-use self, tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, decoded_events: List[HistoryBaseEntry], # pylint: disable=unused-argument all_logs: List[EthereumTxReceiptLog], # pylint: disable=unused-argument ) -> Tuple[Optional[HistoryBaseEntry], Optional[ActionItem]]: """We use the Swapped event to get the fee kept by 1inch""" to_token_address = hex_or_bytes_to_address(tx_log.topics[2]) to_asset = ethaddress_to_asset(to_token_address) if to_asset is None: return None, None to_raw = hex_or_bytes_to_int(tx_log.data[32:64]) fee_raw = hex_or_bytes_to_int(tx_log.data[96:128]) if fee_raw == 0: return None, None # no need to do anything for zero fee taken full_amount = asset_normalized_value(to_raw + fee_raw, to_asset) sender_address = None for event in decoded_events: # Edit the full amount in the swap's receive event if event.event_type == HistoryEventType.TRADE and event.event_subtype == HistoryEventSubType.RECEIVE and event.counterparty == CPT_ONEINCH_V1: # noqa: E501 event.balance.amount = full_amount event.notes = f'Receive {full_amount} {event.asset.symbol} from {CPT_ONEINCH_V1} swap in {event.location_label}' # noqa: E501 sender_address = event.location_label break if sender_address is None: return None, None # And now create a new event for the fee fee_amount = asset_normalized_value(fee_raw, to_asset) fee_event = HistoryBaseEntry( event_identifier=transaction.tx_hash.hex(), sequence_index=self.base.get_sequence_index(tx_log), timestamp=ts_sec_to_ms(transaction.timestamp), location=Location.BLOCKCHAIN, location_label=sender_address, asset=to_asset, balance=Balance(amount=fee_amount), notes= f'Deduct {fee_amount} {to_asset.symbol} from {sender_address} as {CPT_ONEINCH_V1} fees', # noqa: E501 event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_ONEINCH_V1, ) return fee_event, None
def test_gas_fees_after_year(accountant, google_service): """ Test that for an expense like gas fees after year the "selling" part is tax free PnL, and the expense part is taxable pnl. """ tx_hash = '0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a' history = [ LedgerAction( identifier=0, timestamp=Timestamp(1539713238), # 178.615 EUR/ETH action_type=LedgerActionType. GIFT, # gift so not counting as income location=Location.KRAKEN, amount=FVal(1), asset=A_ETH, rate=None, rate_asset=None, link=None, notes='', ), HistoryBaseEntry( event_identifier=tx_hash, sequence_index=0, timestamp=1640493374000, # 4072.51 EUR/ETH location=Location.BLOCKCHAIN, location_label=make_ethereum_address(), asset=A_ETH, balance=Balance(amount=FVal('0.01')), notes='Burned 0.01 ETH in gas', event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_GAS, ) ] accounting_history_process( accountant, start_ts=0, end_ts=1640493376, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRANSACTION_EVENT: PNL(taxable=FVal('-40.7251'), free=FVal('38.93895')), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def _maybe_decode_internal_transactions( self, tx: EthereumTransaction, tx_receipt: EthereumTxReceipt, events: List[HistoryBaseEntry], tx_hash_hex: str, ts_ms: TimestampMS, ) -> None: """ check for internal transactions if the transaction is not canceled. This function mutates the events argument. """ if tx_receipt.status is False: return internal_txs = self.dbethtx.get_ethereum_internal_transactions( parent_tx_hash=tx.tx_hash, ) for internal_tx in internal_txs: if internal_tx.to_address is None: continue # can that happen? Internal transaction deploying a contract? direction_result = self.base.decode_direction( internal_tx.from_address, internal_tx.to_address) # noqa: E501 if direction_result is None: continue amount = ZERO if internal_tx.value == 0 else from_wei( FVal(internal_tx.value)) if amount == ZERO: continue event_type, location_label, counterparty, verb = direction_result events.append( HistoryBaseEntry( event_identifier=tx_hash_hex, sequence_index=self.base.get_next_sequence_counter(), timestamp=ts_ms, location=Location.BLOCKCHAIN, location_label=location_label, asset=A_ETH, balance=Balance(amount=amount), notes= f'{verb} {amount} ETH {internal_tx.from_address} -> {internal_tx.to_address}', # noqa: E501 event_type=event_type, event_subtype=HistoryEventSubType.NONE, counterparty=counterparty, ))
def test_include_gas_costs(accountant, google_service): addr1 = '0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12' tx_hash = '0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a' history = [ Trade( timestamp=1539388574, location=Location.EXTERNAL, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(10), rate=FVal('168.7'), fee=None, fee_currency=None, link=None, ), HistoryBaseEntry( event_identifier=tx_hash, sequence_index=0, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=addr1, asset=A_ETH, balance=Balance(amount=FVal('0.000030921')), notes=f'Burned 0.000030921 ETH in gas from {addr1}', event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_GAS, ) ] accounting_history_process(accountant, start_ts=1436979735, end_ts=1619693374, history_list=history) # noqa: E501 no_message_errors(accountant.msg_aggregator) expected = ZERO expected_pnls = PnlTotals() if accountant.pots[0].settings.include_gas_costs: expected = FVal('-0.0052163727') expected_pnls[AccountingEventType.TRANSACTION_EVENT] = PNL( taxable=expected, free=ZERO) check_pnls_and_csv(accountant, expected_pnls, google_service)
def make_ethereum_event( index: int, tx_hash: Optional[bytes] = None, asset: Asset = A_USD, counterparty: Optional[str] = None, ) -> HistoryBaseEntry: if tx_hash is None: tx_hash = make_random_bytes(42) return HistoryBaseEntry( event_identifier=make_evm_tx_hash(tx_hash).hex(), # pylint: disable=no-member sequence_index=index, identifier=index, timestamp=TimestampMS(0), location=Location.KRAKEN, event_type=HistoryEventType.UNKNOWN, event_subtype=HistoryEventSubType.NONE, asset=asset, balance=Balance(amount=ONE, usd_value=ONE), counterparty=counterparty, )
def _maybe_decode_governance( # pylint: disable=no-self-use self, token: Optional[EthereumToken], # pylint: disable=unused-argument tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, decoded_events: List[HistoryBaseEntry], # pylint: disable=unused-argument action_items: List[ActionItem], # pylint: disable=unused-argument ) -> Optional[HistoryBaseEntry]: if tx_log.topics[0] == GOVERNORALPHA_PROPOSE: if tx_log.address == '0xDbD27635A534A3d3169Ef0498beB56Fb9c937489': governance_name = 'Gitcoin' else: governance_name = tx_log.address try: _, 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 proposal_id = decoded_data[0] proposal_text = decoded_data[8] notes = f'Create {governance_name} proposal {proposal_id}. {proposal_text}' return HistoryBaseEntry( event_identifier=transaction.tx_hash.hex(), sequence_index=self.base.get_sequence_index(tx_log), timestamp=ts_sec_to_ms(transaction.timestamp), location=Location.BLOCKCHAIN, location_label=transaction.from_address, # TODO: This should be null for proposals and other informational events asset=A_ETH, balance=Balance(), notes=notes, event_type=HistoryEventType.INFORMATIONAL, event_subtype=HistoryEventSubType.GOVERNANCE_PROPOSE, counterparty=governance_name, ) return None
def _process_dsr_withdraw( self, pot: 'AccountingPot', # pylint: disable=unused-argument event: HistoryBaseEntry, other_events: List[HistoryBaseEntry], # pylint: disable=unused-argument ) -> None: address = cast(ChecksumEthAddress, event.location_label) # should always exist self.dsr_balances[address] -= event.balance.amount if self.dsr_balances[address] < ZERO: profit = -1 * self.dsr_balances[address] pot.add_acquisition( event_type=AccountingEventType.TRANSACTION_EVENT, notes=f'Gained {profit} DAI from Makerdao DSR', location=event.location, timestamp=event.get_timestamp_in_sec(), asset=A_DAI, amount=profit, taxable=True, extra_data={'tx_hash': event.event_identifier}, ) self.dsr_balances[address] = ZERO
def _process_vault_dai_payback( self, pot: 'AccountingPot', # pylint: disable=unused-argument event: HistoryBaseEntry, other_events: List[HistoryBaseEntry], # pylint: disable=unused-argument ) -> None: cdp_id = event.extra_data[ 'cdp_id'] # type: ignore # this event should have extra_data self.vault_balances[cdp_id] -= event.balance.amount if self.vault_balances[cdp_id] < ZERO: loss = -1 * self.vault_balances[cdp_id] pot.add_spend( event_type=AccountingEventType.TRANSACTION_EVENT, notes=f'Lost {loss} DAI as debt during payback to CDP {cdp_id}', location=event.location, timestamp=event.get_timestamp_in_sec(), asset=A_DAI, amount=loss, taxable=True, extra_data={'tx_hash': event.event_identifier}, ) self.vault_balances[cdp_id] = ZERO
def _decode_withdraw_request( # pylint: disable=no-self-use self, tx_log: EthereumTxReceiptLog, transaction: EthereumTransaction, # pylint: disable=unused-argument decoded_events: List[HistoryBaseEntry], # pylint: disable=unused-argument all_logs: List[EthereumTxReceiptLog], # pylint: disable=unused-argument action_items: List[ActionItem], # pylint: disable=unused-argument ) -> Tuple[Optional[HistoryBaseEntry], Optional[ActionItem]]: topic_data, log_data = self.contract.decode_event( tx_log=tx_log, event_name='WithdrawRequest', argument_names=('user', 'token', 'amount', 'batchId'), ) user = topic_data[0] if not self.base.is_tracked(user): return None, None token = ethaddress_to_asset(topic_data[1]) if token is None: return None, None amount = asset_normalized_value(amount=log_data[0], asset=token) event = HistoryBaseEntry( event_identifier=transaction.tx_hash.hex(), sequence_index=self.base.get_sequence_index(tx_log), timestamp=ts_sec_to_ms(transaction.timestamp), location=Location.BLOCKCHAIN, location_label=user, # Asset means nothing here since the event is informational. TODO: Improve? asset=token, balance=Balance(amount=amount), notes= f'Request a withdrawal of {amount} {token.symbol} from DXDao Mesa', event_type=HistoryEventType.INFORMATIONAL, event_subtype=HistoryEventSubType.REMOVE_ASSET, counterparty=CPT_DXDAO_MESA, ) return event, None
def _swap_event_indices(event1: HistoryBaseEntry, event2: HistoryBaseEntry) -> None: old_event1_index = event1.sequence_index event1.sequence_index = event2.sequence_index event2.sequence_index = old_event1_index
def test_kyber_legacy_old_contract(database, ethereum_manager, eth_transactions): """Data for trade taken from https://etherscan.io/tx/0xe9cc9f27ef2a09fe23abc886a0a0f7ae19d9e2eb73663e1e41e07a3e0c011b87 """ msg_aggregator = MessagesAggregator() tx_hex = '0xe9cc9f27ef2a09fe23abc886a0a0f7ae19d9e2eb73663e1e41e07a3e0c011b87' evmhash = deserialize_evm_tx_hash(tx_hex) transaction = EthereumTransaction( tx_hash=evmhash, timestamp=1591043988, block_number=10182160, from_address='0x6d379cb5BA04c09293b21Bf314E7aba3FfEAaF5b', to_address='0x818E6FECD516Ecc3849DAf6845e3EC868087B755', value=0, gas=600000, gas_price=22990000000, gas_used=527612, input_data=hexstring_to_bytes( '0xcb3c28c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000002aea540000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000006d379cb5ba04c09293b21bf314e7aba3ffeaaf5b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5f611df3b9ac000000000000000000000000f1aa99c69715f423086008eb9d06dc1e35cc504d' ), # noqa: E501 nonce=1, ) receipt = EthereumTxReceipt( tx_hash=evmhash, contract_address=None, status=True, type=0, logs=[ EthereumTxReceiptLog( log_index=87, data=hexstring_to_bytes( '0x0000000000000000000000000000000000000000000000000000000002aea540' ), # noqa: E501 address=string_to_ethereum_address( '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'), removed=False, topics=[ hexstring_to_bytes( '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000006d379cb5ba04c09293b21bf314e7aba3ffeaaf5b' ), # noqa: E501 hexstring_to_bytes( '0x00000000000000000000000065bf64ff5f51272f729bdcd7acfb00677ced86cd' ), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=93, data=hexstring_to_bytes( '0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000002aea540000000000000000000000000000000000000000000000000029a80338e28df730000000000000000000000006d379cb5ba04c09293b21bf314e7aba3ffeaaf5b000000000000000000000000000000000000000000000000029a80338e28df730000000000000000000000001670dfb52806de7789d5cf7d5c005cf7083f9a5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000' ), # noqa: E501 address=string_to_ethereum_address( '0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd'), removed=False, topics=[ hexstring_to_bytes( '0xd30ca399cb43507ecec6a629a35cf45eb98cda550c27696dcb0d8c4a3873ce6c' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000006d379cb5ba04c09293b21bf314e7aba3ffeaaf5b' ), # noqa: E501 ], ), ], ) internal_tx = EthereumInternalTransaction( parent_tx_hash=evmhash, trace_id=27, timestamp=Timestamp(1591043988), block_number=10182160, from_address='0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd', to_address='0x6d379cb5BA04c09293b21Bf314E7aba3FfEAaF5b', value=187603293406027635, ) dbethtx = DBEthTx(database) dbethtx.add_ethereum_transactions([transaction], relevant_address=None) dbethtx.add_ethereum_internal_transactions( [internal_tx], relevant_address='0x6d379cb5BA04c09293b21Bf314E7aba3FfEAaF5b' ) # noqa: E501 decoder = EVMTransactionDecoder( database=database, ethereum_manager=ethereum_manager, eth_transactions=eth_transactions, msg_aggregator=msg_aggregator, ) events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt) assert len(events) == 3 expected_events = [ HistoryBaseEntry( event_identifier= '0xe9cc9f27ef2a09fe23abc886a0a0f7ae19d9e2eb73663e1e41e07a3e0c011b87', sequence_index=0, timestamp=1591043988000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, asset=A_ETH, balance=Balance( amount=FVal(0.01212979988), usd_value=FVal(0), ), location_label='0x6d379cb5BA04c09293b21Bf314E7aba3FfEAaF5b', notes= 'Burned 0.01212979988 ETH in gas from 0x6d379cb5BA04c09293b21Bf314E7aba3FfEAaF5b', # noqa: E501 counterparty=CPT_GAS, ), HistoryBaseEntry( event_identifier= '0xe9cc9f27ef2a09fe23abc886a0a0f7ae19d9e2eb73663e1e41e07a3e0c011b87', sequence_index=1, timestamp=1591043988000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.TRADE, event_subtype=HistoryEventSubType.SPEND, asset=A_USDC, balance=Balance(amount=FVal(45), usd_value=FVal(0)), location_label='0x6d379cb5BA04c09293b21Bf314E7aba3FfEAaF5b', notes='Swap 45 USDC in kyber', counterparty='kyber legacy', ), HistoryBaseEntry( event_identifier= '0xe9cc9f27ef2a09fe23abc886a0a0f7ae19d9e2eb73663e1e41e07a3e0c011b87', sequence_index=89, timestamp=1591043988000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.TRADE, event_subtype=HistoryEventSubType.RECEIVE, asset=A_ETH, balance=Balance( amount=FVal('0.187603293406027635'), usd_value=FVal(0), ), location_label='0x6d379cb5BA04c09293b21Bf314E7aba3FfEAaF5b', notes='Receive 0.187603293406027635 ETH from kyber swap', counterparty='kyber legacy', ) ] assert events == expected_events
def test_kyber_legacy_new_contract(database, ethereum_manager, eth_transactions): """Data for trade taken from https://etherscan.io/tx/0xe80928d5e21f9628c047af1f8b191cbffbb6b8b9945adb502cfb3af152552f22 """ msg_aggregator = MessagesAggregator() tx_hex = '0xe80928d5e21f9628c047af1f8b191cbffbb6b8b9945adb502cfb3af152552f22' evmhash = deserialize_evm_tx_hash(tx_hex) transaction = EthereumTransaction( tx_hash=evmhash, timestamp=1644182638, block_number=14154915, from_address='0x5340F6faff9BF55F66C16Db6Bf9E020d987F87D0', to_address='0x9AAb3f75489902f3a48495025729a0AF77d4b11e', value=0, gas=2784000, gas_price=71000000000, gas_used=938231, input_data=hexstring_to_bytes( '0xae591d54000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000001e52b2aa0000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd520000000000000000000000005340f6faff9bf55f66c16db6bf9e020d987f87d0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000003ef569a751d10e0000000000000000000000000de63aef60307655405835da74ba02ce4db1a42fb000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000' ), # noqa: E501 nonce=41, ) receipt = EthereumTxReceipt( tx_hash=evmhash, contract_address=None, status=True, type=0, logs=[ EthereumTxReceiptLog( log_index=349, data=hexstring_to_bytes( '0x00000000000000000000000000000000000000000000000000000001e52b2aa0' ), # noqa: E501 address=string_to_ethereum_address( '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'), removed=False, topics=[ hexstring_to_bytes( '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000005340f6faff9bf55f66c16db6bf9e020d987f87d0' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000007c66550c9c730b6fdd4c03bc2e73c5462c5f7acc' ), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=369, data=hexstring_to_bytes( '0x000000000000000000000000000000000000000000000083a3ee0140f345d2d8' ), # noqa: E501 address=string_to_ethereum_address( '0xD533a949740bb3306d119CC777fa900bA034cd52'), removed=False, topics=[ hexstring_to_bytes( '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000007c66550c9c730b6fdd4c03bc2e73c5462c5f7acc' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000005340f6faff9bf55f66c16db6bf9e020d987f87d0' ), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=372, data=hexstring_to_bytes( '0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd520000000000000000000000005340f6faff9bf55f66c16db6bf9e020d987f87d000000000000000000000000000000000000000000000000000000001e52b2aa0000000000000000000000000000000000000000000000083a3ee0140f345d2d8000000000000000000000000de63aef60307655405835da74ba02ce4db1a42fb0000000000000000000000000000000000000000000000000000000000000012' ), # noqa: E501 address=string_to_ethereum_address( '0x9AAb3f75489902f3a48495025729a0AF77d4b11e'), removed=False, topics=[ hexstring_to_bytes( '0xf724b4df6617473612b53d7f88ecc6ea983074b30960a049fcd0657ffe808083' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000005340f6faff9bf55f66c16db6bf9e020d987f87d0' ), # noqa: E501 ], ), ], ) dbethtx = DBEthTx(database) dbethtx.add_ethereum_transactions([transaction], relevant_address=None) decoder = EVMTransactionDecoder( database=database, ethereum_manager=ethereum_manager, eth_transactions=eth_transactions, msg_aggregator=msg_aggregator, ) events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt) assert len(events) == 3 expected_events = [ HistoryBaseEntry( event_identifier= '0xe80928d5e21f9628c047af1f8b191cbffbb6b8b9945adb502cfb3af152552f22', sequence_index=0, timestamp=1644182638000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, asset=A_ETH, balance=Balance( amount=FVal(0.066614401), usd_value=FVal(0), ), location_label='0x5340F6faff9BF55F66C16Db6Bf9E020d987F87D0', notes= 'Burned 0.066614401 ETH in gas from 0x5340F6faff9BF55F66C16Db6Bf9E020d987F87D0', # noqa: E501 counterparty=CPT_GAS, ), HistoryBaseEntry( event_identifier= '0xe80928d5e21f9628c047af1f8b191cbffbb6b8b9945adb502cfb3af152552f22', sequence_index=350, timestamp=1644182638000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.TRADE, event_subtype=HistoryEventSubType.SPEND, asset=A_USDC, balance=Balance(amount=FVal(8139.77872), usd_value=FVal(0)), location_label='0x5340F6faff9BF55F66C16Db6Bf9E020d987F87D0', notes='Swap 8139.77872 USDC in kyber', counterparty='kyber legacy', ), HistoryBaseEntry( event_identifier= '0xe80928d5e21f9628c047af1f8b191cbffbb6b8b9945adb502cfb3af152552f22', sequence_index=370, timestamp=1644182638000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.TRADE, event_subtype=HistoryEventSubType.RECEIVE, asset=A_CRV, balance=Balance(amount=FVal('2428.33585390706162556'), usd_value=FVal(0)), location_label='0x5340F6faff9BF55F66C16Db6Bf9E020d987F87D0', notes='Receive 2428.33585390706162556 CRV from kyber swap', counterparty='kyber legacy', ), ] assert events == expected_events