def liquity_staking_balances( self, addresses: List[ChecksumEthAddress], ) -> Dict[ChecksumEthAddress, StakePosition]: staked = self._get_raw_history(addresses, 'stake') lqty_price = Inquirer().find_usd_price(A_LQTY) data = {} for stake in staked['lqtyStakes']: try: owner = to_checksum_address(stake['id']) amount = deserialize_optional_to_fval( value=stake['amount'], name='amount', location='liquity', ) position = AssetBalance( asset=A_LQTY, balance=Balance( amount=amount, usd_value=lqty_price * amount, ), ) data[owner] = StakePosition(position) except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_warning( f'Ignoring Liquity staking information. ' f'Failed to decode remote response. {msg}.', ) continue return data
def get_staking_history( self, addresses: List[ChecksumEthAddress], from_timestamp: Timestamp, to_timestamp: Timestamp, ) -> Dict[ChecksumEthAddress, List[LiquityEvent]]: try: staked = self._get_raw_history(addresses, 'stake') except RemoteError as e: log.error( f'Failed to query stake graph events for liquity. {str(e)}') staked = {} result: Dict[ChecksumEthAddress, List[LiquityEvent]] = defaultdict(list) for stake in staked.get('lqtyStakes', []): owner = to_checksum_address(stake['id']) for change in stake['changes']: try: timestamp = change['transaction']['timestamp'] if timestamp < from_timestamp: continue if timestamp > to_timestamp: break operation_stake = LiquityStakeEventType.deserialize( change['stakeOperation']) lqty_price = PriceHistorian().query_historical_price( from_asset=A_LQTY, to_asset=A_USD, timestamp=timestamp, ) lusd_price = PriceHistorian().query_historical_price( from_asset=A_LUSD, to_asset=A_USD, timestamp=timestamp, ) stake_after = deserialize_optional_to_fval( value=change['stakedAmountAfter'], name='stakedAmountAfter', location='liquity', ) stake_change = deserialize_optional_to_fval( value=change['stakedAmountChange'], name='stakedAmountChange', location='liquity', ) issuance_gain = deserialize_optional_to_fval( value=change['issuanceGain'], name='issuanceGain', location='liquity', ) redemption_gain = deserialize_optional_to_fval( value=change['redemptionGain'], name='redemptionGain', location='liquity', ) stake_event = LiquityStakeEvent( kind='stake', tx=change['transaction']['id'], address=owner, timestamp=timestamp, stake_after=AssetBalance( asset=A_LQTY, balance=Balance( amount=stake_after, usd_value=lqty_price * stake_after, ), ), stake_change=AssetBalance( asset=A_LQTY, balance=Balance( amount=stake_change, usd_value=lqty_price * stake_change, ), ), issuance_gain=AssetBalance( asset=A_LUSD, balance=Balance( amount=issuance_gain, usd_value=lusd_price * issuance_gain, ), ), redemption_gain=AssetBalance( asset=A_LUSD, balance=Balance( amount=redemption_gain, usd_value=lusd_price * redemption_gain, ), ), stake_operation=operation_stake, sequence_number=str( change['transaction']['sequenceNumber']), ) result[owner].append(stake_event) except (DeserializationError, KeyError) as e: msg = str(e) log.debug(f'Failed to deserialize Liquity entry: {change}') if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_warning( f'Ignoring Liquity Stake event in Liquity. ' f'Failed to decode remote information. {msg}.', ) continue return result
def get_trove_history( self, addresses: List[ChecksumEthAddress], from_timestamp: Timestamp, to_timestamp: Timestamp, ) -> Dict[ChecksumEthAddress, List[LiquityEvent]]: addresses_to_query = list(addresses) proxied_addresses = self._get_accounts_having_proxy() proxies_to_address = {v: k for k, v in proxied_addresses.items()} addresses_to_query += proxied_addresses.values() try: query = self._get_raw_history(addresses_to_query, 'trove') except RemoteError as e: log.error( f'Failed to query trove graph events for liquity. {str(e)}') query = {} result: Dict[ChecksumEthAddress, List[LiquityEvent]] = defaultdict(list) for trove in query.get('troves', []): owner = to_checksum_address(trove['owner']['id']) if owner in proxies_to_address: owner = proxies_to_address[owner] for change in trove['changes']: try: timestamp = change['transaction']['timestamp'] if timestamp < from_timestamp: continue if timestamp > to_timestamp: break operation = TroveOperation.deserialize( change['troveOperation']) collateral_change = deserialize_optional_to_fval( value=change['collateralChange'], name='collateralChange', location='liquity', ) debt_change = deserialize_optional_to_fval( value=change['debtChange'], name='debtChange', location='liquity', ) lusd_price = PriceHistorian().query_historical_price( from_asset=A_LUSD, to_asset=A_USD, timestamp=timestamp, ) eth_price = PriceHistorian().query_historical_price( from_asset=A_ETH, to_asset=A_USD, timestamp=timestamp, ) debt_after_amount = deserialize_optional_to_fval( value=change['debtAfter'], name='debtAfter', location='liquity', ) collateral_after_amount = deserialize_optional_to_fval( value=change['collateralAfter'], name='collateralAfter', location='liquity', ) event = LiquityTroveEvent( kind='trove', tx=change['transaction']['id'], address=owner, timestamp=timestamp, debt_after=AssetBalance( asset=A_LUSD, balance=Balance( amount=debt_after_amount, usd_value=lusd_price * debt_after_amount, ), ), collateral_after=AssetBalance( asset=A_ETH, balance=Balance( amount=collateral_after_amount, usd_value=eth_price * collateral_after_amount, ), ), debt_delta=AssetBalance( asset=A_LUSD, balance=Balance( amount=debt_change, usd_value=lusd_price * debt_change, ), ), collateral_delta=AssetBalance( asset=A_ETH, balance=Balance( amount=collateral_change, usd_value=eth_price * collateral_change, ), ), trove_operation=operation, sequence_number=str(change['sequenceNumber']), ) result[owner].append(event) except (DeserializationError, KeyError) as e: log.debug( f'Failed to deserialize Liquity trove event: {change}') msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_warning( f'Ignoring Liquity Trove event in Liquity. ' f'Failed to decode remote information. {msg}.', ) continue return result
def deserialize_from_db(cls, result: YEARN_EVENT_DB_TUPLE) -> 'YearnVaultEvent': """ Turns a tuple read from the DB into an appropriate YearnVaultEvent May raise a DeserializationError if there is an issue with information in the database """ location = 'deserialize yearn vault event from db' realized_pnl = None if result[8] is not None and result[9] is not None: pnl_amount = deserialize_optional_to_fval( value=result[8], name='pnl_amount', location=location, ) pnl_usd_value = deserialize_optional_to_fval( value=result[9], name='pnl_usd_value', location=location, ) realized_pnl = Balance(amount=pnl_amount, usd_value=pnl_usd_value) from_value_amount = deserialize_optional_to_fval( value=result[3], name='from_value_amount', location=location, ) from_value_usd_value = deserialize_optional_to_fval( result[4], name='from_value_usd_value', location=location, ) to_value_amount = deserialize_optional_to_fval( value=result[6], name='to_value_amount', location=location, ) to_value_usd_value = deserialize_optional_to_fval( value=result[7], name='to_value_usd_value', location=location, ) try: block_number = int(result[10]) except ValueError as e: raise DeserializationError( f'Failed to deserialize block number {result[10]} in yearn vault event: {str(e)}', ) from e from_asset = Asset(result[2]) to_asset = Asset(result[5]) return cls( event_type=result[1], from_asset=from_asset, from_value=Balance(amount=from_value_amount, usd_value=from_value_usd_value), to_asset=to_asset, to_value=Balance(amount=to_value_amount, usd_value=to_value_usd_value), realized_pnl=realized_pnl, block_number=block_number, timestamp=deserialize_timestamp(result[11]), tx_hash=result[12], log_index=result[13], version=result[14], )
def aave_event_from_db(event_tuple: AAVE_EVENT_DB_TUPLE) -> AaveEvent: """Turns a tuple read from the DB into an appropriate AaveEvent May raise a DeserializationError if something is wrong with the DB data """ event_type = event_tuple[1] block_number = event_tuple[2] timestamp = Timestamp(event_tuple[3]) tx_hash = event_tuple[4] log_index = event_tuple[5] asset2 = None if event_tuple[9] is not None: try: asset2 = Asset(event_tuple[9]) except UnknownAsset as e: raise DeserializationError( f'Unknown asset {event_tuple[6]} encountered during deserialization ' f'of Aave event from DB for asset2', ) from e try: asset1 = Asset(event_tuple[6]) except UnknownAsset as e: raise DeserializationError( f'Unknown asset {event_tuple[6]} encountered during deserialization ' f'of Aave event from DB for asset1', ) from e asset1_amount = FVal(event_tuple[7]) asset1_usd_value = FVal(event_tuple[8]) if event_type in ('deposit', 'withdrawal'): return AaveDepositWithdrawalEvent( event_type=event_type, block_number=block_number, timestamp=timestamp, tx_hash=tx_hash, log_index=log_index, asset=asset1, atoken=EthereumToken.from_asset( asset2), # type: ignore # should be a token value=Balance(amount=asset1_amount, usd_value=asset1_usd_value), ) if event_type == 'interest': return AaveInterestEvent( event_type=event_type, block_number=block_number, timestamp=timestamp, tx_hash=tx_hash, log_index=log_index, asset=asset1, value=Balance(amount=asset1_amount, usd_value=asset1_usd_value), ) if event_type == 'borrow': if event_tuple[12] not in ('stable', 'variable'): raise DeserializationError( f'Invalid borrow rate mode encountered in the DB: {event_tuple[12]}', ) borrow_rate_mode: Literal['stable', 'variable'] = event_tuple[12] # type: ignore borrow_rate = deserialize_optional_to_fval( value=event_tuple[10], name='borrow_rate', location='reading aave borrow event from DB', ) accrued_borrow_interest = deserialize_optional_to_fval( value=event_tuple[11], name='accrued_borrow_interest', location='reading aave borrow event from DB', ) return AaveBorrowEvent( event_type=event_type, block_number=block_number, timestamp=timestamp, tx_hash=tx_hash, log_index=log_index, asset=asset1, value=Balance(amount=asset1_amount, usd_value=asset1_usd_value), borrow_rate_mode=borrow_rate_mode, borrow_rate=borrow_rate, accrued_borrow_interest=accrued_borrow_interest, ) if event_type == 'repay': fee_amount = deserialize_optional_to_fval( value=event_tuple[10], name='fee_amount', location='reading aave repay event from DB', ) fee_usd_value = deserialize_optional_to_fval( value=event_tuple[11], name='fee_usd_value', location='reading aave repay event from DB', ) return AaveRepayEvent( event_type=event_type, block_number=block_number, timestamp=timestamp, tx_hash=tx_hash, log_index=log_index, asset=asset1, value=Balance(amount=asset1_amount, usd_value=asset1_usd_value), fee=Balance(amount=fee_amount, usd_value=fee_usd_value), ) if event_type == 'liquidation': if asset2 is None: raise DeserializationError( 'Did not find asset2 in an aave liquidation event fom the DB.', ) principal_amount = deserialize_optional_to_fval( value=event_tuple[10], name='principal_amount', location='reading aave liquidation event from DB', ) principal_usd_value = deserialize_optional_to_fval( value=event_tuple[11], name='principal_usd_value', location='reading aave liquidation event from DB', ) return AaveLiquidationEvent( event_type=event_type, block_number=block_number, timestamp=timestamp, tx_hash=tx_hash, log_index=log_index, collateral_asset=asset1, collateral_balance=Balance(amount=asset1_amount, usd_value=asset1_usd_value), principal_asset=asset2, principal_balance=Balance( amount=principal_amount, usd_value=principal_usd_value, ), ) # else raise DeserializationError( f'Unknown event type {event_type} encountered during ' f'deserialization of Aave event from DB', )