Skip to content

Commit

Permalink
Fix Eigenlayer pending withdrawal balances
Browse files Browse the repository at this point in the history
If the timing of the eigenlayer pending withdrawal completion came
before the saving of the start of pending withdrawal was in the DB,
then the pending withdrawal was never marked as complete.

This resulted in those cases of completed withdrawals counting again
in the balances.

This commit fixes this by forcing redecoding of completion if a
completed withdrawal is found without having a match to a pending
withdrawal whenever eigenlayer balances are queried.

Signed-off-by: Lefteris Karapetsas <[email protected]>
  • Loading branch information
LefterisJP authored and OjusWiZard committed Aug 9, 2024
1 parent 9c6aa4c commit f5a2992
Show file tree
Hide file tree
Showing 30 changed files with 284 additions and 50 deletions.
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Changelog
=========

* :release:`1.34.1 <2024-07-24>`
* :bug:`-` Eigenlayer LST pending withdrawals that have been completed should no longer count as user balance.
* :bug:`-` Assets section will now show correct number of assets on any page when excluding ignored assets.
* :bug:`-` Windows backend restart will no longer hang when users update their assets.
* :bug:`8262` Prices of HOP LP tokens will now properly show up for all pools.
Expand Down
7 changes: 4 additions & 3 deletions rotkehlchen/chain/aggregator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1098,13 +1098,14 @@ def _query_protocols_with_balance(self, chain_id: CHAIN_IDS_WITH_BALANCE_PROTOCO
needs to be added to the total balance of the account. Examples of such protocols are
Curve, Convex and Velodrome.
"""
chain: SUPPORTED_EVM_CHAINS_TYPE = ChainID.to_blockchain(chain_id) # type: ignore[assignment] # CHAIN_IDS_WITH_BALANCE_PROTOCOLS only contains SUPPORTED_EVM_CHAINS_TYPE
inquirer = self.get_chain_manager(chain).node_inquirer
chain: SUPPORTED_EVM_CHAINS_TYPE = ChainID.to_blockchain(chain_id) # type: ignore # CHAIN_IDS_WITH_BALANCE_PROTOCOLS only contains SUPPORTED_EVM_CHAINS_TYPE
chain_manager = self.get_evm_manager(chain_id)
existing_balances: defaultdict[ChecksumEvmAddress, BalanceSheet] = self.balances.get(chain)
for protocol in CHAIN_TO_BALANCE_PROTOCOLS[chain_id]:
protocol_with_balance: ProtocolWithBalance = protocol(
database=self.database,
evm_inquirer=inquirer,
evm_inquirer=chain_manager.node_inquirer,
tx_decoder=chain_manager.transactions_decoder,
)
try:
protocol_balances = protocol_with_balance.query_balances()
Expand Down
10 changes: 8 additions & 2 deletions rotkehlchen/chain/arbitrum_one/modules/gearbox/balances.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
from typing import TYPE_CHECKING


from rotkehlchen.chain.arbitrum_one.modules.gearbox.constants import (
GEAR_IDENTIFIER_ARB,
GEAR_STAKING_CONTRACT,
)
from rotkehlchen.chain.evm.decoding.gearbox.balances import GearboxCommonBalances

if TYPE_CHECKING:
from rotkehlchen.chain.arbitrum_one.decoding.decoder import ArbitrumOneTransactionDecoder
from rotkehlchen.chain.arbitrum_one.node_inquirer import ArbitrumOneInquirer
from rotkehlchen.db.dbhandler import DBHandler


class GearboxBalances(GearboxCommonBalances):
def __init__(self, database: 'DBHandler', evm_inquirer: 'ArbitrumOneInquirer') -> None:
def __init__(
self,
database: 'DBHandler',
evm_inquirer: 'ArbitrumOneInquirer',
tx_decoder: 'ArbitrumOneTransactionDecoder',
) -> None:
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
staking_contract=GEAR_STAKING_CONTRACT,
native_token_id=GEAR_IDENTIFIER_ARB,
)
9 changes: 8 additions & 1 deletion rotkehlchen/chain/arbitrum_one/modules/gmx/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .constants import CPT_GMX, GMX_READER, GMX_STAKING_REWARD, GMX_USD_DECIMALS, GMX_VAULT_ADDRESS

if TYPE_CHECKING:
from rotkehlchen.chain.arbitrum_one.decoding.decoder import ArbitrumOneTransactionDecoder
from rotkehlchen.chain.arbitrum_one.node_inquirer import ArbitrumOneInquirer
from rotkehlchen.db.dbhandler import DBHandler

Expand All @@ -30,10 +31,16 @@


class GmxBalances(ProtocolWithBalance):
def __init__(self, database: 'DBHandler', evm_inquirer: 'ArbitrumOneInquirer'):
def __init__(
self,
database: 'DBHandler',
evm_inquirer: 'ArbitrumOneInquirer',
tx_decoder: 'ArbitrumOneTransactionDecoder',
):
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
counterparty=CPT_GMX,
deposit_event_types={(HistoryEventType.DEPOSIT, HistoryEventSubType.DEPOSIT_ASSET)},
)
Expand Down
2 changes: 2 additions & 0 deletions rotkehlchen/chain/arbitrum_one/modules/hop/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
),
}

# Contracts from https://github.com/hop-protocol/hop/blob/92b1fb24f672c102e9fe557a2b8c2aa808a24e80/packages/frontend/src/config/addresses.ts # noqa: E501
# There is no on chain registry or api to check against it so they need to be manually updated
REWARD_CONTRACTS: Final = {
string_to_evm_address('0xb0CabFE930642AD3E7DECdc741884d8C3F7EbC70'), # HOP (USDC.e)
string_to_evm_address('0x9Dd8685463285aD5a94D2c128bda3c5e8a6173c8'), # HOP (USDT)
Expand Down
3 changes: 3 additions & 0 deletions rotkehlchen/chain/arbitrum_one/modules/thegraph/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rotkehlchen.constants.assets import A_GRT_ARB

if TYPE_CHECKING:
from rotkehlchen.chain.arbitrum_one.decoding.decoder import ArbitrumOneTransactionDecoder
from rotkehlchen.chain.arbitrum_one.node_inquirer import ArbitrumOneInquirer
from rotkehlchen.db.dbhandler import DBHandler

Expand All @@ -16,10 +17,12 @@ def __init__(
self,
database: 'DBHandler',
evm_inquirer: 'ArbitrumOneInquirer',
tx_decoder: 'ArbitrumOneTransactionDecoder',
) -> None:
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
native_asset=A_GRT_ARB,
staking_contract=CONTRACT_STAKING,
)
Expand Down
1 change: 1 addition & 0 deletions rotkehlchen/chain/base/decoding/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(
),
dbevmtx_class=DBL2WithL1FeesTx,
)
self.evm_inquirer: BaseInquirer # re-affirm type

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

Expand Down
3 changes: 3 additions & 0 deletions rotkehlchen/chain/base/modules/aerodrome/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from rotkehlchen.db.dbhandler import DBHandler

if TYPE_CHECKING:
from rotkehlchen.chain.base.decoding.decoder import BaseTransactionDecoder
from rotkehlchen.chain.base.node_inquirer import BaseInquirer


Expand All @@ -13,9 +14,11 @@ def __init__(
self,
database: DBHandler,
evm_inquirer: 'BaseInquirer',
tx_decoder: 'BaseTransactionDecoder',
):
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
counterparty=CPT_AERODROME,
)
6 changes: 5 additions & 1 deletion rotkehlchen/chain/ethereum/interfaces/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from rotkehlchen.utils.misc import get_chunks

if TYPE_CHECKING:
from rotkehlchen.chain.evm.decoding.decoder import EVMTransactionDecoder
from rotkehlchen.chain.evm.node_inquirer import EvmNodeInquirer
from rotkehlchen.db.dbhandler import DBHandler
from rotkehlchen.history.events.structures.evm_event import EvmEvent
Expand Down Expand Up @@ -59,12 +60,14 @@ def __init__(
self,
database: 'DBHandler',
evm_inquirer: 'EvmNodeInquirer',
tx_decoder: 'EVMTransactionDecoder',
counterparty: PROTOCOLS_WITH_BALANCES,
deposit_event_types: set[tuple[HistoryEventType, HistoryEventSubType]],
):
self.counterparty = counterparty
self.event_db = DBHistoryEvents(database)
self.evm_inquirer = evm_inquirer
self.tx_decoder = tx_decoder
self.deposit_event_types = deposit_event_types

def addresses_with_activity(
Expand Down Expand Up @@ -122,11 +125,12 @@ def __init__(
self,
database: 'DBHandler',
evm_inquirer: 'EvmNodeInquirer',
tx_decoder: 'EVMTransactionDecoder',
counterparty: PROTOCOLS_WITH_BALANCES,
deposit_event_types: set[tuple[HistoryEventType, HistoryEventSubType]],
gauge_deposit_event_types: set[tuple[HistoryEventType, HistoryEventSubType]],
):
super().__init__(database=database, evm_inquirer=evm_inquirer, counterparty=counterparty, deposit_event_types=deposit_event_types) # noqa: E501
super().__init__(database=database, evm_inquirer=evm_inquirer, tx_decoder=tx_decoder, counterparty=counterparty, deposit_event_types=deposit_event_types) # noqa: E501
self.gauge_deposit_event_types = gauge_deposit_event_types

def addresses_with_gauge_deposits(self) -> dict[ChecksumEvmAddress, list['EvmEvent']]:
Expand Down
3 changes: 3 additions & 0 deletions rotkehlchen/chain/ethereum/modules/aave/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from rotkehlchen.logging import RotkehlchenLogsAdapter

if TYPE_CHECKING:
from rotkehlchen.chain.ethereum.decoding.decoder import EthereumTransactionDecoder
from rotkehlchen.chain.ethereum.node_inquirer import EthereumInquirer
from rotkehlchen.db.dbhandler import DBHandler

Expand All @@ -29,10 +30,12 @@ def __init__(
self,
database: 'DBHandler',
evm_inquirer: 'EthereumInquirer',
tx_decoder: 'EthereumTransactionDecoder',
):
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
counterparty=CPT_AAVE,
deposit_event_types={(HistoryEventType.STAKING, HistoryEventSubType.DEPOSIT_ASSET)},
)
Expand Down
3 changes: 3 additions & 0 deletions rotkehlchen/chain/ethereum/modules/blur/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from rotkehlchen.logging import RotkehlchenLogsAdapter

if TYPE_CHECKING:
from rotkehlchen.chain.ethereum.decoding.decoder import EthereumTransactionDecoder
from rotkehlchen.chain.evm.node_inquirer import EvmNodeInquirer
from rotkehlchen.db.dbhandler import DBHandler
from rotkehlchen.types import ChecksumEvmAddress
Expand All @@ -32,10 +33,12 @@ def __init__(
self,
database: 'DBHandler',
evm_inquirer: 'EvmNodeInquirer',
tx_decoder: 'EthereumTransactionDecoder',
):
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
counterparty=CPT_BLUR,
deposit_event_types={(HistoryEventType.STAKING, HistoryEventSubType.DEPOSIT_ASSET)},
)
Expand Down
7 changes: 5 additions & 2 deletions rotkehlchen/chain/ethereum/modules/convex/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
from .constants import CPT_CONVEX, CVX_LOCKER_V2, CVX_REWARDS

if TYPE_CHECKING:
from rotkehlchen.chain.evm.node_inquirer import EvmNodeInquirer
from rotkehlchen.chain.ethereum.decoding.decoder import EthereumTransactionDecoder
from rotkehlchen.chain.ethereum.node_inquirer import EthereumInquirer
from rotkehlchen.history.events.structures.evm_event import EvmEvent

logger = logging.getLogger(__name__)
Expand All @@ -31,11 +32,13 @@ class ConvexBalances(ProtocolWithGauges):
def __init__(
self,
database: DBHandler,
evm_inquirer: 'EvmNodeInquirer',
evm_inquirer: 'EthereumInquirer',
tx_decoder: 'EthereumTransactionDecoder',
):
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
counterparty=CPT_CONVEX,
deposit_event_types={(HistoryEventType.DEPOSIT, HistoryEventSubType.DEPOSIT_ASSET)},
gauge_deposit_event_types={(HistoryEventType.DEPOSIT, HistoryEventSubType.DEPOSIT_ASSET)}, # noqa: E501
Expand Down
11 changes: 9 additions & 2 deletions rotkehlchen/chain/ethereum/modules/curve/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from rotkehlchen.types import ChecksumEvmAddress

if TYPE_CHECKING:
from rotkehlchen.chain.evm.node_inquirer import EvmNodeInquirer
from rotkehlchen.chain.ethereum.decoding.decoder import EthereumTransactionDecoder
from rotkehlchen.chain.ethereum.node_inquirer import EthereumInquirer
from rotkehlchen.history.events.structures.evm_event import EvmEvent


Expand All @@ -17,10 +18,16 @@ class CurveBalances(ProtocolWithGauges):
LP tokens are already queried by the normal token detection.
"""

def __init__(self, database: DBHandler, evm_inquirer: 'EvmNodeInquirer'):
def __init__(
self,
database: DBHandler,
evm_inquirer: 'EthereumInquirer',
tx_decoder: 'EthereumTransactionDecoder',
):
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
counterparty=CPT_CURVE,
deposit_event_types={(HistoryEventType.DEPOSIT, HistoryEventSubType.DEPOSIT_ASSET)},
gauge_deposit_event_types={(HistoryEventType.DEPOSIT, HistoryEventSubType.DEPOSIT_ASSET)}, # noqa: E501
Expand Down
39 changes: 36 additions & 3 deletions rotkehlchen/chain/ethereum/modules/eigenlayer/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
from rotkehlchen.constants.assets import A_ETH
from rotkehlchen.constants.misc import ZERO
from rotkehlchen.db.dbhandler import DBHandler
from rotkehlchen.db.filtering import EvmEventFilterQuery
from rotkehlchen.errors.misc import NotERC20Conformant, RemoteError
from rotkehlchen.fval import FVal
from rotkehlchen.history.events.structures.evm_event import EvmProduct
from rotkehlchen.history.events.structures.types import HistoryEventSubType, HistoryEventType
from rotkehlchen.inquirer import Inquirer
from rotkehlchen.logging import RotkehlchenLogsAdapter
from rotkehlchen.types import ChecksumEvmAddress
from rotkehlchen.utils.misc import from_wei
from rotkehlchen.types import ChecksumEvmAddress, Location
from rotkehlchen.utils.misc import from_wei, ts_now

from .constants import (
CPT_EIGENLAYER,
Expand All @@ -27,6 +28,7 @@

if TYPE_CHECKING:
from rotkehlchen.assets.asset import EvmToken
from rotkehlchen.chain.ethereum.decoding.decoder import EthereumTransactionDecoder
from rotkehlchen.chain.ethereum.node_inquirer import EthereumInquirer


Expand Down Expand Up @@ -77,10 +79,12 @@ def __init__(
self,
database: DBHandler,
evm_inquirer: 'EthereumInquirer',
tx_decoder: 'EthereumTransactionDecoder',
):
super().__init__(
database=database,
evm_inquirer=evm_inquirer,
tx_decoder=tx_decoder,
counterparty=CPT_EIGENLAYER,
deposit_event_types={(HistoryEventType.STAKING, HistoryEventSubType.DEPOSIT_ASSET)},
)
Expand Down Expand Up @@ -123,10 +127,39 @@ def _query_lst_deposits(self, balances: 'BalancesSheetType') -> 'BalancesSheetTy

def _query_token_pending_withdrawals(self, balances: 'BalancesSheetType') -> 'BalancesSheetType': # noqa: E501
"""Query any balances that are being withdrawn from Eigenlayer and are on the fly"""
# First find if there is any completed withdrawals unmatched,
# as that would lead to double counting of balances
db_filter = EvmEventFilterQuery.make(
counterparties=[CPT_EIGENLAYER],
location=Location.ETHEREUM,
to_ts=ts_now(),
event_types=[HistoryEventType.INFORMATIONAL],
event_subtypes=[HistoryEventSubType.NONE],
)
with self.event_db.db.conn.read_ctx() as cursor:
completed_withdrawal_events = self.event_db.get_history_events(
cursor=cursor,
filter_query=db_filter,
has_premium=True,
)

for completed_withdrawal in completed_withdrawal_events:
if not completed_withdrawal.notes or 'Complete eigenlayer withdrawal' not in completed_withdrawal.notes: # noqa: E501
continue # not a completed withdrawal. Dirty way but we use INFORMATIONAL/NONE for multiple things in eigenlayer. TODO: Maybe improve this? # noqa: E501

if completed_withdrawal.extra_data and completed_withdrawal.extra_data.get('matched', False): # noqa: E501
continue

# here we are with a completed withdrawal that has not been matched, so redecode to try and match # noqa: E501
self.tx_decoder.decode_transaction_hashes(
tx_hashes=[completed_withdrawal.tx_hash],
ignore_cache=True,
)

# proceed with the counting of all pending withdrawals as balances
addresses_with_withdrawals = self.addresses_with_activity(
event_types={(HistoryEventType.INFORMATIONAL, HistoryEventSubType.REMOVE_ASSET)},
)

for address, event_list in addresses_with_withdrawals.items():
for event in event_list:
if event.extra_data is None:
Expand Down
3 changes: 2 additions & 1 deletion rotkehlchen/chain/ethereum/modules/eigenlayer/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ def _decode_withdrawal_completed(self, context: DecoderContext) -> DecodingOutpu
notes=f'Complete eigenlayer withdrawal of {withdrawal_event.asset.resolve_to_asset_with_symbol().symbol}', # noqa: E501
counterparty=CPT_EIGENLAYER,
address=context.tx_log.address,
extra_data={'matched': True},
)
with self.base.database.user_write() as write_cursor:
dbevents.edit_event_extra_data( # set withdrawal event as completed
Expand Down Expand Up @@ -352,7 +353,7 @@ def _decode_withdrawal_completed(self, context: DecoderContext) -> DecodingOutpu

break # completed the finding event logic

else: # not found. Perhaps transaction and event not pulled for some reason?
else: # not found. Perhaps transaction and event not pulled or timing issue. Is rechecked from time to time when doing balance queries. # noqa: E501
log.debug(f'When decoding eigenlayer WithdrawalCompleted could not find corresponding Withdrawal queued: {context.transaction.tx_hash.hex()}') # noqa: E501

new_event = self.base.make_event_next_index( # so let's just keep minimal info
Expand Down
Loading

0 comments on commit f5a2992

Please sign in to comment.