def test_deserialize_int_from_hex_or_int(): # Etherscan can return logIndex 0x if it's the 0th log in the hash # https://etherscan.io/tx/0x6f1370cd9fa19d550031a30290b062dd3b56f44caf6344c05545ef15428de7ef assert deserialize_int_from_hex_or_int("0x", 'whatever') == 0 assert deserialize_int_from_hex_or_int("0x1", 'whatever') == 1 assert deserialize_int_from_hex_or_int("0x33", 'whatever') == 51 assert deserialize_int_from_hex_or_int(66, 'whatever') == 66
def _get_comp_events( self, address: ChecksumEthAddress, from_ts: Timestamp, to_ts: Timestamp, ) -> List[CompoundEvent]: self.ethereum.etherscan.get_blocknumber_by_time(from_ts) from_block = max( COMP_DEPLOYED_BLOCK, self.ethereum.etherscan.get_blocknumber_by_time(from_ts), ) argument_filters = { 'from': COMPTROLLER_PROXY.address, 'to': address, } comp_events = self.ethereum.get_logs( contract_address=A_COMP.ethereum_address, abi=ERC20TOKEN_ABI, event_name='Transfer', argument_filters=argument_filters, from_block=from_block, to_block=self.ethereum.etherscan.get_blocknumber_by_time(to_ts), ) events = [] for event in comp_events: timestamp = self.ethereum.get_event_timestamp(event) amount = token_normalized_value(hex_or_bytes_to_int(event['data']), A_COMP) usd_price = query_usd_price_zero_if_error( asset=A_COMP, time=timestamp, location='comp_claim', msg_aggregator=self.msg_aggregator, ) value = Balance(amount, amount * usd_price) events.append( CompoundEvent( event_type='comp', address=address, block_number=deserialize_blocknumber(event['blockNumber']), timestamp=timestamp, asset=A_COMP, value=value, to_asset=None, to_value=None, realized_pnl=value, tx_hash=event['transactionHash'], log_index=deserialize_int_from_hex_or_int( event['logIndex'], 'comp log index'), )) return events
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 get_events_for_atoken_and_address( self, user_address: ChecksumEthAddress, atoken: EthereumToken, deposit_events: List[Dict[str, Any]], withdraw_events: List[Dict[str, Any]], from_block: int, to_block: int, ) -> List[AaveEvent]: """This function should be entered while holding the history_lock semaphore""" argument_filters = { 'from': ZERO_ADDRESS, 'to': user_address, } mint_events = self.ethereum.get_logs( contract_address=atoken.ethereum_address, abi=ATOKEN_ABI, event_name='Transfer', argument_filters=argument_filters, from_block=from_block, to_block=to_block, ) mint_data = set() mint_data_to_log_index = {} for event in mint_events: amount = hex_or_bytes_to_int(event['data']) if amount == 0: continue # first mint can be for 0. Ignore entry = ( deserialize_blocknumber(event['blockNumber']), amount, self.ethereum.get_event_timestamp(event), event['transactionHash'], ) mint_data.add(entry) mint_data_to_log_index[entry] = deserialize_int_from_hex_or_int( event['logIndex'], 'aave log index', ) reserve_asset = _atoken_to_reserve_asset(atoken) reserve_address, decimals = _get_reserve_address_decimals( reserve_asset.identifier) aave_events = [] for event in deposit_events: if hex_or_bytes_to_address(event['topics'][1]) == reserve_address: # first 32 bytes of the data are the amount deposit = hex_or_bytes_to_int(event['data'][:66]) block_number = deserialize_blocknumber(event['blockNumber']) timestamp = self.ethereum.get_event_timestamp(event) tx_hash = event['transactionHash'] log_index = deserialize_int_from_hex_or_int( event['logIndex'], 'aave log index') # If there is a corresponding deposit event remove the minting event data entry = (block_number, deposit, timestamp, tx_hash) if entry in mint_data: mint_data.remove(entry) del mint_data_to_log_index[entry] usd_price = query_usd_price_zero_if_error( asset=reserve_asset, time=timestamp, location='aave deposit', msg_aggregator=self.msg_aggregator, ) deposit_amount = deposit / (FVal(10)**FVal(decimals)) aave_events.append( AaveEvent( event_type='deposit', asset=reserve_asset, value=Balance( amount=deposit_amount, usd_value=deposit_amount * usd_price, ), block_number=block_number, timestamp=timestamp, tx_hash=tx_hash, log_index=log_index, )) for data in mint_data: usd_price = query_usd_price_zero_if_error( asset=atoken, time=data[2], location='aave interest profit', msg_aggregator=self.msg_aggregator, ) interest_amount = data[1] / (FVal(10)**FVal(decimals)) aave_events.append( AaveEvent( event_type='interest', asset=atoken, value=Balance( amount=interest_amount, usd_value=interest_amount * usd_price, ), block_number=data[0], timestamp=data[2], tx_hash=data[3], log_index=mint_data_to_log_index[data], )) for event in withdraw_events: if hex_or_bytes_to_address(event['topics'][1]) == reserve_address: # first 32 bytes of the data are the amount withdrawal = hex_or_bytes_to_int(event['data'][:66]) block_number = deserialize_blocknumber(event['blockNumber']) timestamp = self.ethereum.get_event_timestamp(event) tx_hash = event['transactionHash'] usd_price = query_usd_price_zero_if_error( asset=reserve_asset, time=timestamp, location='aave withdrawal', msg_aggregator=self.msg_aggregator, ) withdrawal_amount = withdrawal / (FVal(10)**FVal(decimals)) aave_events.append( AaveEvent( event_type='withdrawal', asset=reserve_asset, value=Balance( amount=withdrawal_amount, usd_value=withdrawal_amount * usd_price, ), block_number=block_number, timestamp=timestamp, tx_hash=tx_hash, log_index=deserialize_int_from_hex_or_int( event['logIndex'], 'aave log index'), )) return aave_events