Skip to content

Commit

Permalink
Mellow fallback (#250)
Browse files Browse the repository at this point in the history
* Mellow fallback

* Fix unit tests

* Fix DepositorBot constructor in test

* Fix positional argument

* Double max deposit count

* Fix test

* Improve test

* Sender chain in integrations

* Without sender chain

* Reorder

* Remove redundant function

* New metric for mellow

* Cast
  • Loading branch information
hweawer authored Aug 12, 2024
1 parent c32fb05 commit 4a348dd
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 31 deletions.
16 changes: 15 additions & 1 deletion src/blockchain/deposit_strategy/base_deposit_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def deposited_keys_amount(self, module_id: int) -> int:
module_id,
depositable_ether,
)
POSSIBLE_DEPOSITS_AMOUNT.labels(module_id).set(possible_deposits_amount)
POSSIBLE_DEPOSITS_AMOUNT.labels(module_id, 0).set(possible_deposits_amount)
return possible_deposits_amount


Expand All @@ -44,3 +44,17 @@ def _depositable_ether(self) -> Wei:
logger.info({'msg': 'Adding mellow vault balance to the depositable check', 'vault': additional_ether})
depositable_ether += additional_ether
return depositable_ether

def deposited_keys_amount(self, module_id: int) -> int:
depositable_ether = self._depositable_ether()
possible_deposits_amount_assumption = self.w3.lido.staking_router.get_staking_module_max_deposits_count(
module_id,
depositable_ether,
)
possible_deposited_eth = Web3.to_wei(32 * possible_deposits_amount_assumption, 'ether')
possible_deposits_amount = self.w3.lido.staking_router.get_staking_module_max_deposits_count(
module_id,
possible_deposited_eth,
)
POSSIBLE_DEPOSITS_AMOUNT.labels(module_id, 1).set(possible_deposits_amount)
return possible_deposits_amount if possible_deposits_amount_assumption == possible_deposits_amount else 0
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ def _prepare_signs_for_deposit(quorum: list[DepositMessage]) -> tuple[tuple[str,
def prepare_and_send(
self,
quorum: list[DepositMessage],
with_flashbots: bool,
is_mellow: bool,
with_flashbots: bool,
) -> bool:
tx = self._prepare_mellow_tx(quorum) if is_mellow else self._prepare_general_tx(quorum)
return self._send_transaction(tx, with_flashbots)
Expand Down
50 changes: 34 additions & 16 deletions src/bots/depositor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@

def run_depositor(w3):
logger.info({'msg': 'Initialize Depositor bot.'})
gas_price_calculator = GasPriceCalculator(w3)
sender = Sender(w3)
gas_price_calculator = GasPriceCalculator(w3)
mellow_deposit_strategy = MellowDepositStrategy(w3)
base_deposit_strategy = BaseDepositStrategy(w3)
depositor_bot = DepositorBot(w3, sender, gas_price_calculator, mellow_deposit_strategy, base_deposit_strategy)
Expand Down Expand Up @@ -68,8 +68,8 @@ def __init__(
base_deposit_strategy: BaseDepositStrategy,
):
self.w3 = w3
self._gas_price_calculator = gas_price_calcaulator
self._sender = sender
self._gas_price_calculator = gas_price_calcaulator
self._mellow_strategy = mellow_deposit_strategy
self._general_strategy = base_deposit_strategy

Expand Down Expand Up @@ -139,23 +139,29 @@ def _is_mellow_depositable(
{
'msg': 'Mellow module check failed.',
'contract_module': staking_module_contract.get_staking_module_id(),
'tx_module': module_id
'tx_module': module_id,
}
)
return False
balance = self.w3.lido.simple_dvt_staking_strategy.vault_balance()
except Exception as e:
logger.warning(
{
'msg': 'Failed to check if mellow depositable',
'msg': 'Failed to check if mellow depositable.',
'module_id': module_id,
'err': repr(e)
'err': repr(e),
}
)
return False
MELLOW_VAULT_BALANCE.labels(module_id).set(balance)
if balance < variables.VAULT_DIRECT_DEPOSIT_THRESHOLD:
logger.info({'msg': f'{balance} is less than VAULT_DIRECT_DEPOSIT_THRESHOLD while building mellow transaction.'})
logger.info(
{
'msg': f'{balance} is less than VAULT_DIRECT_DEPOSIT_THRESHOLD while building mellow transaction.',
'balance': balance,
'threshold': variables.VAULT_DIRECT_DEPOSIT_THRESHOLD,
}
)
return False
logger.debug({'msg': 'Mellow module check succeeded.', 'tx_module': module_id})
return True
Expand Down Expand Up @@ -188,16 +194,17 @@ def _deposit_to_module(self, module_id: int) -> bool:

if is_depositable and quorum and can_deposit and gas_is_ok and is_deposit_amount_ok:
logger.info({'msg': 'Checks passed. Prepare deposit tx.', 'is_mellow': is_mellow})
success = self.prepare_and_send_tx(quorum, is_mellow, self._flashbots_works)
success = self.prepare_and_send_tx(module_id, quorum, is_mellow)
if not success and is_mellow:
success = self.prepare_and_send_tx(module_id, quorum, False)
self._flashbots_works = not self._flashbots_works or success
self._mellow_works = success
return success

logger.info({'msg': 'Checks failed. Skip deposit.'})
return False

def _select_strategy(self, module_id) -> tuple[BaseDepositStrategy, bool]:
if self._mellow_works and self._is_mellow_depositable(module_id):
if self._is_mellow_depositable(module_id):
return self._mellow_strategy, True
return self._general_strategy, False

Expand Down Expand Up @@ -281,13 +288,24 @@ def message_filter(message: DepositMessage) -> bool:

return message_filter

def prepare_and_send_tx(self, quorum: list[DepositMessage], is_mellow: bool, module_id: int) -> bool:
success = self._sender.prepare_and_send(
quorum,
self._flashbots_works,
is_mellow,
)
def prepare_and_send_tx(self, module_id: int, quorum: list[DepositMessage], is_mellow: bool) -> bool:
if is_mellow:
try:
success = self._sender.prepare_and_send(
quorum,
is_mellow,
self._flashbots_works,
)
except Exception as e:
success = False
logger.warning({'msg': 'Error while sending mellow transaction', 'err': repr(e)})
else:
success = self._sender.prepare_and_send(
quorum,
is_mellow,
self._flashbots_works,
)
logger.info({'msg': f'Tx send. Result is {success}.'})
label = 'success' if success else 'failure'
MODULE_TX_SEND.labels(label, module_id).inc()
MODULE_TX_SEND.labels(label, module_id, int(is_mellow)).inc()
return success
4 changes: 2 additions & 2 deletions src/metrics/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
MODULE_TX_SEND = Counter(
'transactions',
'Amount of send transaction from bot with per module distribution.',
['status', 'module_id'],
['status', 'module_id', 'is_mellow'],
namespace=PROMETHEUS_PREFIX
)

Expand Down Expand Up @@ -55,7 +55,7 @@
POSSIBLE_DEPOSITS_AMOUNT = Gauge(
'possible_deposits_amount',
'Possible deposits amount.',
['module_id'],
['module_id', 'is_mellow'],
namespace=PROMETHEUS_PREFIX,
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from unittest.mock import Mock

import pytest
from web3 import Web3

MODULE_ID = 1

Expand Down Expand Up @@ -32,7 +33,11 @@ def test_deposited_keys_amount_mellow(mellow_deposit_strategy):
mellow_deposit_strategy.w3.lido.staking_router.get_staking_module_max_deposits_count = Mock(return_value=possible_deposits)

assert mellow_deposit_strategy.deposited_keys_amount(MODULE_ID) == possible_deposits
mellow_deposit_strategy.w3.lido.staking_router.get_staking_module_max_deposits_count.assert_called_once_with(
mellow_deposit_strategy.w3.lido.staking_router.get_staking_module_max_deposits_count.assert_any_call(
MODULE_ID,
depositable_eth + vault_balance,
)
mellow_deposit_strategy.w3.lido.staking_router.get_staking_module_max_deposits_count.assert_any_call(
MODULE_ID,
Web3.to_wei(32 * possible_deposits, 'ether'),
)
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from unittest.mock import Mock

import pytest
from blockchain.deposit_strategy.deposit_transaction_sender import Sender
from transport.msg_types.deposit import DepositMessage

MODULE_ID = 1


@pytest.mark.unit
def test_send_deposit_tx_not_mellow(deposit_transaction_sender):
def test_send_deposit_tx_not_mellow(deposit_transaction_sender: Sender):
deposit_transaction_sender._w3.transaction.check = Mock(return_value=False)
messages = [DepositMessage(
type='deposit',
Expand Down
29 changes: 23 additions & 6 deletions tests/bots/test_depositor.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def add_accounts_to_guardian(web3_lido_integration, set_integration_account):
[[19628126, 1], [19628126, 2]],
indirect=['web3_provider_integration'],
)
def test_depositor_bot(
def test_depositor_bot_non_mellow_deposits(
web3_provider_integration,
web3_lido_integration,
deposit_transaction_sender_integration,
Expand All @@ -308,7 +308,12 @@ def test_depositor_bot(
module_id,
add_accounts_to_guardian,
):
# Disable mellow integration
variables.MELLOW_CONTRACT_ADDRESS = None
# Define the whitelist of deposit modules
variables.DEPOSIT_MODULES_WHITELIST = [1, 2]

# Set the balance for the first account
web3_lido_integration.provider.make_request(
'anvil_setBalance',
[
Expand All @@ -317,6 +322,7 @@ def test_depositor_bot(
],
)

# Submit multiple transactions
for _ in range(15):
web3_lido_integration.lido.lido.functions.submit(web3_lido_integration.eth.accounts[0]).transact(
{
Expand All @@ -325,31 +331,42 @@ def test_depositor_bot(
}
)

# Set the maximum number of deposits
web3_lido_integration.lido.deposit_security_module.functions.setMaxDeposits(100).transact({'from': DSM_OWNER})

# Get the latest block
latest = web3_lido_integration.eth.get_block('latest')

# Get the current nonce for the staking module
old_module_nonce = web3_lido_integration.lido.staking_router.get_staking_module_nonce(module_id)

deposit_message_1 = get_deposit_message(web3_lido_integration, COUNCIL_ADDRESS_1, COUNCIL_PK_1, module_id)
deposit_message_2 = get_deposit_message(web3_lido_integration, COUNCIL_ADDRESS_1, COUNCIL_PK_1, module_id)
deposit_message_3 = get_deposit_message(web3_lido_integration, COUNCIL_ADDRESS_2, COUNCIL_PK_2, module_id)
# Create deposit messages
deposit_messages = [
get_deposit_message(web3_lido_integration, COUNCIL_ADDRESS_1, COUNCIL_PK_1, module_id),
get_deposit_message(web3_lido_integration, COUNCIL_ADDRESS_1, COUNCIL_PK_1, module_id),
get_deposit_message(web3_lido_integration, COUNCIL_ADDRESS_2, COUNCIL_PK_2, module_id),
]

# Mine a new block
web3_lido_integration.provider.make_request('anvil_mine', [1])

# Initialize the DepositorBot
db: DepositorBot = DepositorBot(
web3_lido_integration,
deposit_transaction_sender_integration,
gas_price_calculator_integration,
mellow_deposit_strategy_integration,
base_deposit_strategy_integration,
)
db._mellow_works = False

# Clear the message storage and execute the bot without any messages
db.message_storage.messages = []
db.execute(latest)

# Assert that the staking module nonce has not changed
assert web3_lido_integration.lido.staking_router.get_staking_module_nonce(module_id) == old_module_nonce

db.message_storage.messages = [deposit_message_1, deposit_message_2, deposit_message_3]
# Execute the bot with deposit messages and assert that the nonce has increased by 1
db.message_storage.messages = deposit_messages
assert db.execute(latest)
assert web3_lido_integration.lido.staking_router.get_staking_module_nonce(module_id) == old_module_nonce + 1
2 changes: 1 addition & 1 deletion tests/fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@
'gas_price_calculator_integration',
'deposit_transaction_sender_integration',
'mellow_deposit_strategy',
'mellow_deposit_strategy_integration'
'mellow_deposit_strategy_integration',
]
4 changes: 2 additions & 2 deletions tests/fixtures/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ def mellow_deposit_strategy_integration(web3_lido_integration):


@pytest.fixture
def deposit_transaction_sender(web3_lido_unit):
def deposit_transaction_sender(web3_lido_unit) -> Sender:
yield Sender(web3_lido_unit)


@pytest.fixture
def deposit_transaction_sender_integration(web3_lido_integration):
def deposit_transaction_sender_integration(web3_lido_integration) -> Sender:
yield Sender(web3_lido_integration)


Expand Down

0 comments on commit 4a348dd

Please sign in to comment.