diff --git a/src/modules/accounting/accounting.py b/src/modules/accounting/accounting.py index 03f4ecb2c..827bd6d65 100644 --- a/src/modules/accounting/accounting.py +++ b/src/modules/accounting/accounting.py @@ -39,6 +39,7 @@ from src.services.bunker import BunkerService from src.types import BlockStamp, Gwei, ReferenceBlockStamp, StakingModuleId, NodeOperatorGlobalIndex, FinalizationBatches from src.utils.cache import global_lru_cache as lru_cache +from src.utils.units import gwei_to_wei from src.variables import ALLOW_REPORTING_IN_BUNKER_MODE from src.web3py.types import Web3 from src.web3py.extensions.lido_validators import StakingModule @@ -289,7 +290,7 @@ def simulate_rebase_after_report( self._get_slots_elapsed_from_last_report(blockstamp) * chain_conf.seconds_per_slot, # _timeElapsed # CL values validators_count, # _clValidators - Web3.to_wei(cl_balance, 'gwei'), # _clBalance + gwei_to_wei(cl_balance), # _clBalance # EL values self.w3.lido_contracts.get_withdrawal_balance(blockstamp), # _withdrawalVaultBalance el_rewards, # _elRewardsVaultBalance diff --git a/src/modules/ejector/ejector.py b/src/modules/ejector/ejector.py index 28e5971f8..2c8b1ec71 100644 --- a/src/modules/ejector/ejector.py +++ b/src/modules/ejector/ejector.py @@ -31,6 +31,7 @@ from src.services.validator_state import LidoValidatorStateService from src.types import BlockStamp, EpochNumber, Gwei, NodeOperatorGlobalIndex, ReferenceBlockStamp from src.utils.cache import global_lru_cache as lru_cache +from src.utils.units import gwei_to_wei, wei_to_gwei from src.utils.validator_state import ( compute_activation_exit_epoch, get_activation_exit_churn_limit, @@ -126,16 +127,16 @@ def get_validators_to_eject(self, blockstamp: ReferenceBlockStamp) -> list[tuple validators_iterator = iter(self.get_validators_iterator(consensus_version, blockstamp)) validators_to_eject: list[tuple[NodeOperatorGlobalIndex, LidoValidator]] = [] - validator_to_eject_balance_sum = 0 + total_balance_to_eject_wei = 0 try: while expected_balance < to_withdraw_amount: gid, next_validator = next(validators_iterator) validators_to_eject.append((gid, next_validator)) - validator_to_eject_balance_sum += self.w3.to_wei(self._get_predicted_withdrawable_balance(next_validator), "gwei") - expected_balance = ( + total_balance_to_eject_wei += self._get_predicted_withdrawable_balance(next_validator) + expected_balance = Wei( self._get_total_expected_balance([v for (_, v) in validators_to_eject], blockstamp) - + validator_to_eject_balance_sum + + total_balance_to_eject_wei ) except StopIteration: pass @@ -155,7 +156,7 @@ def get_validators_to_eject(self, blockstamp: ReferenceBlockStamp) -> list[tuple return validators_to_eject - def _get_total_expected_balance(self, vals_to_exit: list[Validator], blockstamp: ReferenceBlockStamp): + def _get_total_expected_balance(self, vals_to_exit: list[Validator], blockstamp: ReferenceBlockStamp) -> Wei: chain_config = self.get_chain_config(blockstamp) validators_going_to_exit = self.validators_state_service.get_recently_requested_but_not_exited_validators(blockstamp, chain_config) @@ -182,7 +183,7 @@ def _get_total_expected_balance(self, vals_to_exit: list[Validator], blockstamp: total_available_balance = self._get_total_el_balance(blockstamp) logger.info({'msg': 'Calculate el balance.', 'value': total_available_balance}) - return future_rewards + future_withdrawals + total_available_balance + going_to_withdraw_balance + return Wei(future_rewards + future_withdrawals + total_available_balance + going_to_withdraw_balance) def get_validators_iterator(self, consensus_version: int, blockstamp: ReferenceBlockStamp): chain_config = self.get_chain_config(blockstamp) @@ -209,16 +210,17 @@ def is_reporting_allowed(self, blockstamp: ReferenceBlockStamp) -> bool: @lru_cache(maxsize=1) def _get_withdrawable_lido_validators_balance(self, on_epoch: EpochNumber, blockstamp: BlockStamp) -> Wei: lido_validators = self.w3.lido_validators.get_lido_validators(blockstamp=blockstamp) - return Wei( - sum( + return sum( + ( self._get_predicted_withdrawable_balance(v) for v in lido_validators if is_fully_withdrawable_validator(v.validator, Gwei(int(v.balance)), on_epoch) - ) + ), + Wei(0), ) - def _get_predicted_withdrawable_balance(self, validator: Validator) -> Gwei: - return Gwei(min(int(validator.balance), get_max_effective_balance(validator.validator))) + def _get_predicted_withdrawable_balance(self, validator: Validator) -> Wei: + return gwei_to_wei(min(Gwei(int(validator.balance)), get_max_effective_balance(validator.validator))) @lru_cache(maxsize=1) def _get_total_el_balance(self, blockstamp: BlockStamp) -> Wei: @@ -278,7 +280,9 @@ def _get_predicted_withdrawable_epoch_post_electra( earliest_exit_epoch = state_view.earliest_exit_epoch exit_balance_to_consume = state_view.exit_balance_to_consume - exit_balance = sum(self._get_predicted_withdrawable_balance(v) for v in validators_to_eject) + exit_balance = wei_to_gwei( + sum((self._get_predicted_withdrawable_balance(v) for v in validators_to_eject), Wei(0)) + ) balance_to_process = max(0, exit_balance - exit_balance_to_consume) additional_epochs = math.ceil(balance_to_process / per_epoch_churn) diff --git a/src/services/bunker.py b/src/services/bunker.py index 9e2791373..34f8b2dd5 100644 --- a/src/services/bunker.py +++ b/src/services/bunker.py @@ -1,6 +1,8 @@ import logging -from src.constants import TOTAL_BASIS_POINTS, GWEI_TO_WEI +from web3.types import Wei + +from src.constants import TOTAL_BASIS_POINTS from src.metrics.prometheus.validators import ( ALL_VALIDATORS, LIDO_VALIDATORS, @@ -16,6 +18,7 @@ from src.services.bunker_cases.types import BunkerConfig from src.services.safe_border import filter_slashed_validators from src.types import BlockStamp, ReferenceBlockStamp, Gwei +from src.utils.units import wei_to_gwei from src.utils.web3converter import Web3Converter from src.web3py.types import Web3 @@ -96,11 +99,11 @@ def get_cl_rebase_for_current_report(self, blockstamp: BlockStamp, simulated_cl_ """ logger.info({"msg": "Getting CL rebase for frame"}) before_report_total_pooled_ether = self.w3.lido_contracts.lido.total_supply(blockstamp.block_hash) + rebase_diff = Wei(simulated_cl_rebase.post_total_pooled_ether - before_report_total_pooled_ether) - # Can't use from_wei - because rebase can be negative - frame_cl_rebase = (simulated_cl_rebase.post_total_pooled_ether - before_report_total_pooled_ether) // GWEI_TO_WEI + frame_cl_rebase = wei_to_gwei(rebase_diff) logger.info({"msg": f"Simulated CL rebase for frame: {frame_cl_rebase} Gwei"}) - return Gwei(frame_cl_rebase) + return frame_cl_rebase def _get_config(self, blockstamp: BlockStamp) -> BunkerConfig: """Get config values from OracleDaemonConfig contract""" diff --git a/src/services/bunker_cases/abnormal_cl_rebase.py b/src/services/bunker_cases/abnormal_cl_rebase.py index 16d5c046f..e29020451 100644 --- a/src/services/bunker_cases/abnormal_cl_rebase.py +++ b/src/services/bunker_cases/abnormal_cl_rebase.py @@ -14,6 +14,7 @@ from src.types import ReferenceBlockStamp, Gwei, BlockNumber, SlotNumber, BlockStamp, EpochNumber from src.utils.events import get_events_in_range from src.utils.slot import get_blockstamp, get_reference_blockstamp +from src.utils.units import wei_to_gwei from src.utils.validator_state import calculate_active_effective_balance_sum from src.web3py.extensions.lido_validators import LidoValidator, LidoValidatorsProvider from src.web3py.types import Web3 @@ -219,9 +220,7 @@ def _get_lido_validators_balance_with_vault( Get Lido validator balance with withdrawals vault balance """ real_cl_balance = AbnormalClRebase.calculate_validators_balance_sum(lido_validators) - withdrawals_vault_balance = int( - self.w3.from_wei(self.w3.lido_contracts.get_withdrawal_balance_no_cache(blockstamp), "gwei") - ) + withdrawals_vault_balance = wei_to_gwei(self.w3.lido_contracts.get_withdrawal_balance_no_cache(blockstamp)) total_balance = real_cl_balance + withdrawals_vault_balance consensus_version = self.w3.lido_contracts.accounting_oracle.get_consensus_version(blockstamp.block_hash) @@ -265,10 +264,10 @@ def _get_withdrawn_from_vault_between_blocks( logger.info({"msg": "No ETHDistributed event found. Vault withdrawals: 0 Gwei."}) return Gwei(0) - vault_withdrawals = int(self.w3.from_wei(events[0]['args']['withdrawalsWithdrawn'], 'gwei')) + vault_withdrawals = wei_to_gwei(events[0]['args']['withdrawalsWithdrawn']) logger.info({"msg": f"Vault withdrawals: {vault_withdrawals} Gwei"}) - return Gwei(vault_withdrawals) + return vault_withdrawals def _get_eth_distributed_events(self, from_block: BlockNumber, to_block: BlockNumber) -> list[EventData]: """Get ETHDistributed events between blocks""" diff --git a/src/utils/units.py b/src/utils/units.py new file mode 100644 index 000000000..f2ba96973 --- /dev/null +++ b/src/utils/units.py @@ -0,0 +1,16 @@ +"""A set of utils to convert ether units""" + +from web3.types import Wei + +from src.constants import GWEI_TO_WEI +from src.types import Gwei + + +def wei_to_gwei(amount: Wei) -> Gwei: + """Converts Wei to Gwei rounding down""" + return Gwei(amount // GWEI_TO_WEI) + + +def gwei_to_wei(amount: Gwei) -> Wei: + """Converts Gwei to Wei""" + return Wei(amount * GWEI_TO_WEI) diff --git a/src/web3py/extensions/lido_validators.py b/src/web3py/extensions/lido_validators.py index d9bcac737..f68780cda 100644 --- a/src/web3py/extensions/lido_validators.py +++ b/src/web3py/extensions/lido_validators.py @@ -172,7 +172,7 @@ def merge_validators_with_keys(keys: list[LidoKey], validators: list[Validator]) return lido_validators @staticmethod - def calculate_total_eth1_bridge_deposits_amount(lido_validators: list[LidoValidator], pending_deposits: list[PendingDeposit]) -> int: + def calculate_total_eth1_bridge_deposits_amount(lido_validators: list[LidoValidator], pending_deposits: list[PendingDeposit]) -> Gwei: total_eth1_bridge_deposits_amount = 0 for v in lido_validators: if ( diff --git a/tests/modules/ejector/test_ejector.py b/tests/modules/ejector/test_ejector.py index ca9eb87a7..d7feeb56e 100644 --- a/tests/modules/ejector/test_ejector.py +++ b/tests/modules/ejector/test_ejector.py @@ -7,6 +7,7 @@ from src import constants from src.constants import ( EFFECTIVE_BALANCE_INCREMENT, + GWEI_TO_WEI, MAX_EFFECTIVE_BALANCE, MAX_EFFECTIVE_BALANCE_ELECTRA, MAX_SEED_LOOKAHEAD, @@ -402,7 +403,7 @@ def test_get_withdrawable_lido_validators_balance( ) result = ejector._get_withdrawable_lido_validators_balance(42, ref_blockstamp) - assert result == 42, "Unexpected withdrawable amount" + assert result == 42 * GWEI_TO_WEI, "Unexpected withdrawable amount" ejector._get_withdrawable_lido_validators_balance(42, ref_blockstamp) ejector.w3.lido_validators.get_lido_validators.assert_called_once() @@ -416,18 +417,18 @@ def test_get_predicted_withdrawable_balance(ejector: Ejector) -> None: validator = LidoValidatorFactory.build_with_balance(Gwei(42)) result = ejector._get_predicted_withdrawable_balance(validator) - assert result == 42, "Expected validator's balance in gwei" + assert result == 42 * GWEI_TO_WEI, "Expected validator's balance in gwei" validator = LidoValidatorFactory.build_with_balance(Gwei(MAX_EFFECTIVE_BALANCE + 1)) result = ejector._get_predicted_withdrawable_balance(validator) - assert result == MAX_EFFECTIVE_BALANCE, "Expect MAX_EFFECTIVE_BALANCE" + assert result == MAX_EFFECTIVE_BALANCE * GWEI_TO_WEI, "Expect MAX_EFFECTIVE_BALANCE" validator = LidoValidatorFactory.build_with_balance( Gwei(MAX_EFFECTIVE_BALANCE + 1), meb=MAX_EFFECTIVE_BALANCE_ELECTRA, ) result = ejector._get_predicted_withdrawable_balance(validator) - assert result == MAX_EFFECTIVE_BALANCE + 1, "Expect MAX_EFFECTIVE_BALANCE + 1" + assert result == (MAX_EFFECTIVE_BALANCE + 1) * GWEI_TO_WEI, "Expect MAX_EFFECTIVE_BALANCE + 1" @pytest.mark.unit