def _get_vault_withdraw_events( self, vault: YearnVault, address: ChecksumEthAddress, from_block: int, to_block: int, ) -> List[YearnVaultEvent]: """Get all withdraw events of the underlying token to the vault""" events: List[YearnVaultEvent] = [] argument_filters = {'from': vault.contract.address, 'to': address} withdraw_events = self.ethereum.get_logs( contract_address=vault.underlying_token.ethereum_address, abi=ERC20TOKEN_ABI, event_name='Transfer', argument_filters=argument_filters, from_block=from_block, to_block=to_block, ) for withdraw_event in withdraw_events: timestamp = self.ethereum.get_event_timestamp(withdraw_event) withdraw_amount = token_normalized_value( token_amount=hex_or_bytes_to_int(withdraw_event['data']), token=vault.token, ) tx_hash = withdraw_event['transactionHash'] tx_receipt = self.ethereum.get_transaction_receipt(tx_hash) withdraw_index = deserialize_int_from_hex_or_int( withdraw_event['logIndex'], 'yearn withdraw log index', ) burn_amount = None for log in tx_receipt['logs']: found_event = ( log['topics'][0] == '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' and # noqa: E501 log['topics'][1] == address_to_bytes32(address) and log['topics'][2] == address_to_bytes32(ZERO_ADDRESS)) if found_event: # found the burn log burn_amount = token_normalized_value( token_amount=hex_or_bytes_to_int(log['data']), token=vault.token, ) if burn_amount is None: self.msg_aggregator.add_error( f'Ignoring yearn withdraw event with tx_hash {tx_hash} and log index ' f'{withdraw_index} due to inability to find corresponding burn event', ) continue withdraw_usd_price = get_usd_price_zero_if_error( asset=vault.underlying_token, time=timestamp, location='yearn vault withdraw', msg_aggregator=self.msg_aggregator, ) burn_usd_price = get_usd_price_zero_if_error( asset=vault.token, time=timestamp, location='yearn vault withdraw', msg_aggregator=self.msg_aggregator, ) events.append( YearnVaultEvent( event_type='withdraw', block_number=deserialize_blocknumber( withdraw_event['blockNumber']), timestamp=timestamp, from_asset=vault.token, from_value=Balance( amount=burn_amount, usd_value=burn_amount * burn_usd_price, ), to_asset=vault.underlying_token, to_value=Balance( amount=withdraw_amount, usd_value=withdraw_amount * withdraw_usd_price, ), realized_pnl=None, tx_hash=tx_hash, log_index=withdraw_index, )) return events
def test_add_and_get_yearn_vault_events(data_dir, username): """Test that get yearn vault events works fine and returns only events for what we need""" msg_aggregator = MessagesAggregator() data = DataHandler(data_dir, msg_aggregator) data.unlock(username, '123', create_new=True) addr1 = make_ethereum_address() addr1_events = [YearnVaultEvent( event_type='deposit', from_asset=A_DAI, from_value=Balance(amount=FVal(1), usd_value=FVal(1)), to_asset=Asset('yDAI'), to_value=Balance(amount=FVal(1), usd_value=FVal(1)), realized_pnl=None, block_number=1, timestamp=Timestamp(1), tx_hash='0x01653e88600a6492ad6e9ae2af415c990e623479057e4e93b163e65cfb2d4436', log_index=1, ), YearnVaultEvent( event_type='withdraw', from_asset=Asset('yDAI'), from_value=Balance(amount=FVal(1), usd_value=FVal(1)), to_asset=A_DAI, to_value=Balance(amount=FVal(1), usd_value=FVal(1)), realized_pnl=Balance(amount=FVal('0.01'), usd_value=FVal('0.01')), block_number=2, timestamp=Timestamp(2), tx_hash='0x4147da3e5d3c0565a99192ce0b32182ab30b8e1067921d9b2a8ef3bd60b7e2ce', log_index=2, )] data.db.add_yearn_vaults_events(address=addr1, events=addr1_events) addr2 = make_ethereum_address() addr2_events = [YearnVaultEvent( event_type='deposit', from_asset=A_DAI, from_value=Balance(amount=FVal(1), usd_value=FVal(1)), to_asset=Asset('yDAI'), to_value=Balance(amount=FVal(1), usd_value=FVal(1)), realized_pnl=None, block_number=1, timestamp=Timestamp(1), tx_hash='0x8c094d58f33e8dedcd348cb33b58f3bd447602f1fecb99e51b1c2868029eab55', log_index=1, ), YearnVaultEvent( event_type='withdraw', from_asset=Asset('yDAI'), from_value=Balance(amount=FVal(1), usd_value=FVal(1)), to_asset=A_DAI, to_value=Balance(amount=FVal(1), usd_value=FVal(1)), realized_pnl=Balance(amount=FVal('0.01'), usd_value=FVal('0.01')), block_number=2, timestamp=Timestamp(2), tx_hash='0x58c67445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de', log_index=2, )] data.db.add_yearn_vaults_events(address=addr2, events=addr2_events) events = data.db.get_yearn_vaults_events(address=addr1, vault=YEARN_VAULTS['yDAI']) assert events == addr1_events events = data.db.get_yearn_vaults_events(address=addr2, vault=YEARN_VAULTS['yDAI']) assert events == addr2_events
def _process_event( self, events: List[Dict[str, Any]], event_type: Literal['deposit', 'withdraw'], ) -> List[YearnVaultEvent]: result = [] for entry in events: # The id returned is a composition of hash + '-' + log_index try: _, tx_hash, log_index, _ = entry['id'].split('-') except ValueError as e: log.debug( f'Failed to extract transaction hash and log index from {event_type} event ' f'in yearn vaults v2 graph query. Got {entry["id"]}. {str(e)}.', ) self.msg_aggregator.add_warning( f'Ignoring {event_type} in yearn vault V2. Failed to read id {entry["id"]}', ) continue try: if event_type == 'deposit': from_asset = EthereumToken(entry['vault']['token']['id']) to_asset = EthereumToken( entry['vault']['shareToken']['id']) elif event_type == 'withdraw': from_asset = EthereumToken( entry['vault']['shareToken']['id']) to_asset = EthereumToken(entry['vault']['token']['id']) except UnknownAsset: if event_type == 'deposit': from_str = entry['vault']['token']['symbol'] to_str = entry['vault']['shareToken']['symbol'] elif event_type == 'withdraw': from_str = entry['vault']['shareToken']['symbol'] to_str = entry['vault']['token']['symbol'] self.msg_aggregator.add_warning( f'Ignoring {event_type} in yearn vaults V2 from {from_str} to ' f'{to_str} because the token is not recognized.', ) continue except KeyError as e: log.debug( f'Failed to extract token information from {event_type} event ' f'in yearn vaults v2 graph query. {str(e)}.', ) self.msg_aggregator.add_warning( f'Ignoring {event_type} {tx_hash} in yearn vault V2 Failed to decode' f' remote information. ', ) continue try: from_asset_usd_price = get_usd_price_zero_if_error( asset=from_asset, time=Timestamp(int(entry['timestamp']) // 1000), location=f'yearn vault v2 deposit {tx_hash}', msg_aggregator=self.msg_aggregator, ) to_asset_usd_price = get_usd_price_zero_if_error( asset=to_asset, time=Timestamp(int(entry['timestamp']) // 1000), location=f'yearn v2 vault deposit {tx_hash}', msg_aggregator=self.msg_aggregator, ) if event_type == 'deposit': from_asset_amount = token_normalized_value( token_amount=int(entry['tokenAmount']), token=from_asset, ) to_asset_amount = token_normalized_value( token_amount=int(entry['sharesMinted']), token=to_asset, ) elif event_type == 'withdraw': from_asset_amount = token_normalized_value( token_amount=int(entry['sharesBurnt']), token=from_asset, ) to_asset_amount = token_normalized_value( token_amount=int(entry['tokenAmount']), token=to_asset, ) result.append( YearnVaultEvent( event_type=event_type, block_number=int(entry['blockNumber']), timestamp=Timestamp(int(entry['timestamp']) // 1000), from_asset=from_asset, from_value=Balance( amount=from_asset_amount, usd_value=from_asset_amount * from_asset_usd_price, ), to_asset=to_asset, to_value=Balance( amount=to_asset_amount, usd_value=to_asset_amount * to_asset_usd_price, ), realized_pnl=None, tx_hash=tx_hash, log_index=int(log_index), version=2, )) except (KeyError, ValueError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' log.error( f'Failed to read {event_type} from yearn vaults v2 graph because the response' f' does not have the expected output.', error=msg, ) self.msg_aggregator.add_warning( f'Ignoring {event_type} {tx_hash} in yearn vault V2 from {from_asset} to ' f'{to_asset} because the remote information is not correct.', ) continue return result