diff --git a/archive/scripts/vote_2023_09_12.py b/archive/scripts/vote_2023_09_12.py
new file mode 100644
index 00000000..3b56c499
--- /dev/null
+++ b/archive/scripts/vote_2023_09_12.py
@@ -0,0 +1,90 @@
+"""
+Voting 164 12/09/2023
+Vote REJECTED
+"""
+
+import time
+
+from typing import Dict
+
+from brownie.network.transaction import TransactionReceipt
+from brownie import web3, interface # type: ignore
+from utils.agent import agent_forward
+
+from utils.voting import bake_vote_items, confirm_vote_script, create_vote
+
+from utils.config import (
+ get_deployer_account,
+ get_is_live,
+ get_priority_fee,
+ contracts,
+)
+
+from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description
+
+description = """
+The proposal is to support **Jump Crypto voluntarily exits from the validator set** by setting `targetValidatorsCount` to 0.
+Algorithm would prioritise exiting Jump Crypto validators in order to fulfil users' withdrawals requests.
+Jump Crypto request on [forum](https://research.lido.fi/t/lido-dao-proposal-to-set-targetvalidatorscount-for-jump-crypto-operator-to-0-to-wind-down-the-jump-crypto-legacy-set/5259).
+"""
+
+
+def start_vote(tx_params: Dict[str, str], silent: bool) -> bool | list[int | TransactionReceipt | None]:
+ """Prepare and run voting."""
+
+ # contracts.node_operators_registry.getNodeOperator(1, True)
+ JUMP_CRYPTO_ID = 1
+ # web3.keccak(text="STAKING_MODULE_MANAGE_ROLE")
+ STAKING_MODULE_MANAGE_ROLE = "0x3105bcbf19d4417b73ae0e58d508a65ecf75665e46c2622d8521732de6080c48"
+
+ call_script_items = [
+ # 1. Support **Jump Crypto voluntarily exits from the validator set** by setting `targetValidatorsCount` to 0.
+ ## 1) Grant STAKING_MODULE_MANAGE_ROLE to Lido Agent
+ agent_forward(
+ [
+ (
+ contracts.staking_router.address,
+ contracts.staking_router.grantRole.encode_input(
+ STAKING_MODULE_MANAGE_ROLE, contracts.agent.address
+ ),
+ )
+ ]
+ ),
+ ## 2) Set Jump Crypto targetValidatorsCount to 0
+ agent_forward(
+ [
+ (
+ contracts.staking_router.address,
+ contracts.staking_router.updateTargetValidatorsLimits.encode_input(1, JUMP_CRYPTO_ID, True, 0),
+ )
+ ]
+ ),
+ ]
+
+ vote_desc_items = [
+ f"1) Grant STAKING_MODULE_MANAGE_ROLE to Lido Agent",
+ f"2) Set Jump Crypto targetValidatorsLimits to 0",
+ ]
+
+ vote_items = bake_vote_items(vote_desc_items, call_script_items)
+
+ if silent:
+ desc_ipfs = calculate_vote_ipfs_description(description)
+ else:
+ desc_ipfs = upload_vote_ipfs_description(description)
+
+ return confirm_vote_script(vote_items, silent, desc_ipfs) and list(
+ create_vote(vote_items, tx_params, desc_ipfs=desc_ipfs)
+ )
+
+
+def main():
+ tx_params = {"from": get_deployer_account()}
+ if get_is_live():
+ tx_params["priority_fee"] = get_priority_fee()
+
+ vote_id, _ = start_vote(tx_params=tx_params, silent=False)
+
+ vote_id >= 0 and print(f"Vote created: {vote_id}.")
+
+ time.sleep(5) # hack for waiting thread #2.
diff --git a/archive/tests/test_2023_09_12.py b/archive/tests/test_2023_09_12.py
new file mode 100644
index 00000000..296c22e5
--- /dev/null
+++ b/archive/tests/test_2023_09_12.py
@@ -0,0 +1,93 @@
+"""
+Tests for voting 12/09/2023
+
+"""
+from archive.scripts.vote_2023_09_12 import start_vote
+
+from utils.config import (
+ network_name,
+ contracts,
+ LDO_HOLDER_ADDRESS_FOR_TESTS,
+)
+from utils.voting import find_metadata_by_vote_id
+from utils.ipfs import get_lido_vote_cid_from_str
+from utils.test.tx_tracing_helpers import *
+
+from utils.test.event_validators.node_operators_registry import (
+ validate_target_validators_count_changed_event,
+ TargetValidatorsCountChanged,
+)
+from utils.test.event_validators.permission import validate_grant_role_event
+
+
+def test_vote(
+ helpers,
+ bypass_events_decoding,
+ vote_ids_from_env,
+ accounts,
+):
+ # params
+ agent = contracts.agent
+ nor = contracts.node_operators_registry
+ staking_router = contracts.staking_router
+ target_NO_id = 1
+ target_validators_count_change_request = TargetValidatorsCountChanged(
+ nodeOperatorId=target_NO_id, targetValidatorsCount=0
+ )
+
+ # web3.keccak(text="STAKING_MODULE_MANAGE_ROLE")
+ STAKING_MODULE_MANAGE_ROLE = "0x3105bcbf19d4417b73ae0e58d508a65ecf75665e46c2622d8521732de6080c48"
+
+ # 1)
+ assert staking_router.hasRole(STAKING_MODULE_MANAGE_ROLE, agent.address) == False
+
+ # 2)
+ NO_summary_before = nor.getNodeOperatorSummary(target_NO_id)
+ assert NO_summary_before[0] == False
+ assert NO_summary_before[1] == 0
+ assert NO_summary_before[2] == 0
+ assert NO_summary_before[3] == 0
+ assert NO_summary_before[4] == 0
+ assert NO_summary_before[5] == 0
+ assert NO_summary_before[6] == 1000
+ assert NO_summary_before[7] == 0
+
+ # START VOTE
+ if len(vote_ids_from_env) > 0:
+ (vote_id,) = vote_ids_from_env
+ else:
+ tx_params = {"from": LDO_HOLDER_ADDRESS_FOR_TESTS}
+ vote_id, _ = start_vote(tx_params, silent=True)
+
+ vote_tx = helpers.execute_vote(accounts, vote_id, contracts.voting)
+
+ print(f"voteId = {vote_id}, gasUsed = {vote_tx.gas_used}")
+
+ # validate vote events
+ assert count_vote_items_by_events(vote_tx, contracts.voting) == 2, "Incorrect voting items count"
+
+ metadata = find_metadata_by_vote_id(vote_id)
+
+ assert get_lido_vote_cid_from_str(metadata) == "bafkreiapvuobyrudww3oqhfopbs2fdmtebi6jnvpeb3plxkajnhafw25im"
+ # 1)
+ assert staking_router.hasRole(STAKING_MODULE_MANAGE_ROLE, agent.address) == True
+
+ # 2)
+ NO_summary_after = nor.getNodeOperatorSummary(target_NO_id)
+ assert NO_summary_after[0] == True
+ assert NO_summary_after[1] == 0
+ assert NO_summary_after[2] == 0
+ assert NO_summary_after[3] == 0
+ assert NO_summary_after[4] == 0
+ assert NO_summary_after[5] == 0
+ assert NO_summary_after[6] == 1000
+ assert NO_summary_after[7] == 0
+
+ if bypass_events_decoding or network_name() in ("goerli", "goerli-fork"):
+ return
+
+ evs = group_voting_events(vote_tx)
+
+ validate_grant_role_event(evs[0], STAKING_MODULE_MANAGE_ROLE, agent.address, agent.address)
+
+ validate_target_validators_count_changed_event(evs[1], target_validators_count_change_request)
diff --git a/tests/README.md b/tests/README.md
index f39099be..a6960693 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -42,7 +42,7 @@ expected changes.
Snapshot tests work as follows:
-1) Go over some protocol use scenario (e. g. stake by use + oracle report)
+1) Go over some protocol use scenario (e.g. stake by use + oracle report)
2) Store the snapshot along the steps
3) Revert the chain changes
4) Execute the vote
@@ -61,3 +61,27 @@ the voting without modification of the common test files
## Internal tests
Internal tests are used to test the tooling itself.
+
+## For test debugging
+How to run one test?
+You need to add file name:
+```shell
+poetry run brownie test tests/
/test_.py -s
+```
+
+How not to raise the network every time you launch test?
+You could to run network in separate terminal window, tests will connect to it:
+```shell
+poetry run brownie console --network mainnet-fork
+```
+
+How to decode unreadable error messages (like 0xb...)?
+1) You need to clone `lido-cli` repo.
+```shell
+git clone https://github.com/lidofinance/lido-cli
+```
+2) install following docs - https://github.com/lidofinance/lido-cli
+3) run
+```shell
+./run.sh tx parse-error
+```
diff --git a/tests/acceptance/test_node_operators_registry.py b/tests/acceptance/test_node_operators_registry.py
index e86afde0..60b4353e 100644
--- a/tests/acceptance/test_node_operators_registry.py
+++ b/tests/acceptance/test_node_operators_registry.py
@@ -106,14 +106,21 @@ def test_nor_state(contract):
assert node_operator["totalVettedValidators"] > 0
assert node_operator["totalVettedValidators"] <= node_operator["totalAddedValidators"]
- if id == 22:
- assert node_operator["totalExitedValidators"] == 11
+ # counts could inc but not dec
+ if id == 2:
+ assert node_operator["totalExitedValidators"] == 252
+ elif id == 4:
+ assert node_operator["totalExitedValidators"] == 174
+ elif id == 5:
+ assert node_operator["totalExitedValidators"] == 36
elif id == 12:
- assert node_operator["totalExitedValidators"] >= 134
+ assert node_operator["totalExitedValidators"] == 2300
elif id == 21:
- assert node_operator["totalExitedValidators"] >= 125
+ assert node_operator["totalExitedValidators"] == 125
+ elif id == 22:
+ assert node_operator["totalExitedValidators"] == 11
else:
- assert node_operator["totalExitedValidators"] == 0
+ assert node_operator["totalExitedValidators"] == 0, f"totalExitedValidators is positive for node {id}"
assert node_operator["totalAddedValidators"] > 0
@@ -122,23 +129,34 @@ def test_nor_state(contract):
assert node_operator["totalDepositedValidators"] <= node_operator["totalAddedValidators"]
node_operator_summary = contract.getNodeOperatorSummary(id)
- if id != 12:
- assert node_operator_summary["isTargetLimitActive"] is False
+ exited_node_operators = [12] # vote 23-05-23
+ if id in exited_node_operators:
+ assert (
+ node_operator_summary["isTargetLimitActive"] is True
+ ), f"isTargetLimitActive is inactive for node {id}"
else:
- assert node_operator_summary["isTargetLimitActive"] is True
+ assert node_operator_summary["isTargetLimitActive"] is False, f"isTargetLimitActive is active for node {id}"
assert node_operator_summary["targetValidatorsCount"] == 0
assert node_operator_summary["stuckValidatorsCount"] == 0
assert node_operator_summary["refundedValidatorsCount"] == 0
assert node_operator_summary["stuckPenaltyEndTimestamp"] == 0
-
- if id == 22:
- assert node_operator_summary["totalExitedValidators"] == 11
+ # counts could inc but not dec
+ if id == 2:
+ assert node_operator_summary["totalExitedValidators"] == 252
+ elif id == 4:
+ assert node_operator_summary["totalExitedValidators"] == 174
+ elif id == 5:
+ assert node_operator_summary["totalExitedValidators"] == 36
elif id == 12:
- assert node_operator_summary["totalExitedValidators"] >= 134
+ assert node_operator_summary["totalExitedValidators"] == 2300
elif id == 21:
- assert node_operator_summary["totalExitedValidators"] >= 125
+ assert node_operator_summary["totalExitedValidators"] == 125
+ elif id == 22:
+ assert node_operator_summary["totalExitedValidators"] == 11
else:
- assert node_operator_summary["totalExitedValidators"] == 0
+ assert (
+ node_operator_summary["totalExitedValidators"] == 0
+ ), f"totalExitedValidators is positive for node {id}"
assert node_operator_summary["totalDepositedValidators"] > 0
assert node_operator_summary["depositableValidatorsCount"] is not None
diff --git a/tests/regression/test_accounting_oracle_extra_data.py b/tests/regression/test_accounting_oracle_extra_data.py
index 411abea3..f4eda0cf 100644
--- a/tests/regression/test_accounting_oracle_extra_data.py
+++ b/tests/regression/test_accounting_oracle_extra_data.py
@@ -3,6 +3,7 @@
from utils.test.oracle_report_helpers import oracle_report
from utils.config import MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT, MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_COUNT
+from utils.config import contracts
@pytest.fixture()
@@ -10,12 +11,24 @@ def extra_data_service():
return ExtraDataService()
+def get_exited_count(node_operator_id):
+ counts = {2: 252, 4: 174, 5: 36}
+ if node_operator_id in counts.keys():
+ return counts[node_operator_id]
+ else:
+ return 0
+
+
def test_accounting_oracle_too_node_ops_per_extra_data_item(extra_data_service):
nos_per_item_count = 10
item_count = MAX_ACCOUNTING_EXTRA_DATA_LIST_ITEMS_COUNT
+ for i in range(nos_per_item_count):
+ no = contracts.node_operators_registry.getNodeOperator(i, True)
+ print(f'{i}-{no["totalExitedValidators"]}')
+
extra_data = extra_data_service.collect(
{(1, i): i for i in range(nos_per_item_count)},
- {(1, i): i for i in range(nos_per_item_count)},
+ {(1, i): get_exited_count(i) for i in range(nos_per_item_count)},
item_count,
nos_per_item_count,
)
diff --git a/tests/regression/test_all_round_happy_path.py b/tests/regression/test_all_round_happy_path.py
index 93ae65f1..7358e29a 100644
--- a/tests/regression/test_all_round_happy_path.py
+++ b/tests/regression/test_all_round_happy_path.py
@@ -13,14 +13,11 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale):
curated_module_id = 1
""" report """
- while (
- contracts.withdrawal_queue.getLastRequestId()
- != contracts.withdrawal_queue.getLastFinalizedRequestId()
- ):
+ while contracts.withdrawal_queue.getLastRequestId() != contracts.withdrawal_queue.getLastFinalizedRequestId():
# finalize all current requests first
report_tx = oracle_report()[0]
# stake new ether to increase buffer
- contracts.lido.submit(ZERO_ADDRESS, { 'from': eth_whale.address, 'value': ETH(10000) })
+ contracts.lido.submit(ZERO_ADDRESS, {"from": eth_whale.address, "value": ETH(10000)})
contracts.lido.approve(contracts.withdrawal_queue.address, 1000, {"from": steth_holder})
contracts.withdrawal_queue.requestWithdrawals([1000], steth_holder, {"from": steth_holder})
@@ -114,7 +111,7 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale):
nor_operators_count = contracts.node_operators_registry.getNodeOperatorsCount()
for i in range(nor_operators_count):
no = contracts.node_operators_registry.getNodeOperator(i, True)
- if (not no["totalDepositedValidators"]):
+ if not no["totalDepositedValidators"] or no["totalDepositedValidators"] == no["totalExitedValidators"]:
nor_operators_count = nor_operators_count - 1
treasury_balance_before_rebase = contracts.lido.sharesOf(treasury)
@@ -126,7 +123,9 @@ def test_all_round_happy_path(accounts, stranger, steth_holder, eth_whale):
token_rebased_event = report_tx.events["TokenRebased"]
transfer_event = report_tx.events["Transfer"]
- assert extra_tx.events.count("Transfer") == nor_operators_count
+ assert (
+ extra_tx.events.count("Transfer") == nor_operators_count
+ ), "extra_tx.events should have Transfer to all active operators, check activity condition above"
assert report_tx.events.count("TokenRebased") == 1
assert report_tx.events.count("WithdrawalsFinalized") == 1
assert report_tx.events.count("StETHBurnt") == 1
diff --git a/tests/regression/test_pause_resume.py b/tests/regression/test_pause_resume.py
index 03ba76b5..158f6890 100644
--- a/tests/regression/test_pause_resume.py
+++ b/tests/regression/test_pause_resume.py
@@ -2,7 +2,7 @@
import brownie
import pytest
-from brownie import ZERO_ADDRESS, web3, chain
+from brownie import ZERO_ADDRESS, web3, chain, Contract
from utils.config import contracts
from utils.evm_script import encode_error
@@ -10,7 +10,7 @@
from utils.test.oracle_report_helpers import oracle_report, prepare_exit_bus_report
from utils.test.helpers import almostEqEth
-DEPOSIT_AMOUNT = 100 * 10 ** 18
+DEPOSIT_AMOUNT = 100 * 10**18
@pytest.fixture()
@@ -18,6 +18,11 @@ def stopped_lido():
contracts.lido.stop({"from": contracts.voting})
+@pytest.fixture(scope="module")
+def burner() -> Contract:
+ return contracts.burner
+
+
@pytest.fixture(scope="function", autouse=is_there_any_vote_scripts())
def autoexecute_vote(helpers, vote_ids_from_env, accounts):
if vote_ids_from_env:
@@ -63,10 +68,15 @@ def test_stop_resume_staking_lido_emit_events(self, helpers):
def test_pause_resume_deposits_staking_module(self, helpers, stranger):
tx = contracts.staking_router.pauseStakingModule(1, {"from": contracts.deposit_security_module})
- helpers.assert_single_event_named("StakingModuleStatusSet", tx,
- {'setBy': contracts.deposit_security_module,
- 'stakingModuleId': 1,
- 'status': StakingModuleStatus.DepositsPaused})
+ helpers.assert_single_event_named(
+ "StakingModuleStatusSet",
+ tx,
+ {
+ "setBy": contracts.deposit_security_module,
+ "stakingModuleId": 1,
+ "status": StakingModuleStatus.DepositsPaused,
+ },
+ )
assert contracts.staking_router.getStakingModuleIsDepositsPaused(1)
contracts.staking_router.grantRole(
@@ -75,10 +85,11 @@ def test_pause_resume_deposits_staking_module(self, helpers, stranger):
{"from": contracts.agent},
)
tx = contracts.staking_router.resumeStakingModule(1, {"from": stranger})
- helpers.assert_single_event_named("StakingModuleStatusSet", tx,
- {'setBy': stranger,
- 'stakingModuleId': 1,
- 'status': StakingModuleStatus.Active})
+ helpers.assert_single_event_named(
+ "StakingModuleStatusSet",
+ tx,
+ {"setBy": stranger, "stakingModuleId": 1, "status": StakingModuleStatus.Active},
+ )
assert contracts.staking_router.getStakingModuleIsActive(1)
def test_stop_staking_module(self, helpers, stranger):
@@ -88,13 +99,12 @@ def test_stop_staking_module(self, helpers, stranger):
{"from": contracts.agent},
)
- tx = contracts.staking_router.setStakingModuleStatus(1,
- StakingModuleStatus.Stopped,
- {"from": stranger})
- helpers.assert_single_event_named("StakingModuleStatusSet", tx,
- {'setBy': stranger,
- 'stakingModuleId': 1,
- 'status': StakingModuleStatus.Stopped})
+ tx = contracts.staking_router.setStakingModuleStatus(1, StakingModuleStatus.Stopped, {"from": stranger})
+ helpers.assert_single_event_named(
+ "StakingModuleStatusSet",
+ tx,
+ {"setBy": stranger, "stakingModuleId": 1, "status": StakingModuleStatus.Stopped},
+ )
assert contracts.staking_router.getStakingModuleIsStopped(1)
@@ -151,9 +161,11 @@ def test_revert_second_stop_resume(self):
with brownie.reverts("CONTRACT_IS_ACTIVE"):
contracts.lido.resume({"from": contracts.voting})
- @pytest.mark.skip(reason="Second call of pause/resume staking is not reverted right now."
- "It maybe should be fixed in the future to be consistent, "
- "there's not a real problem with it.")
+ @pytest.mark.skip(
+ reason="Second call of pause/resume staking is not reverted right now."
+ "It maybe should be fixed in the future to be consistent, "
+ "there's not a real problem with it."
+ )
def test_revert_second_pause_resume_staking(self):
contracts.lido.pauseStaking({"from": contracts.voting})
@@ -168,7 +180,7 @@ def test_revert_second_pause_resume_staking(self):
def test_revert_second_pause_resume_staking_module(self, stranger):
contracts.staking_router.pauseStakingModule(1, {"from": contracts.deposit_security_module})
- with brownie.reverts(encode_error('StakingModuleNotActive()')):
+ with brownie.reverts(encode_error("StakingModuleNotActive()")):
contracts.staking_router.pauseStakingModule(1, {"from": contracts.deposit_security_module})
contracts.staking_router.grantRole(
@@ -178,7 +190,7 @@ def test_revert_second_pause_resume_staking_module(self, stranger):
)
contracts.staking_router.resumeStakingModule(1, {"from": stranger})
- with brownie.reverts(encode_error('StakingModuleNotPaused()')):
+ with brownie.reverts(encode_error("StakingModuleNotPaused()")):
contracts.staking_router.resumeStakingModule(1, {"from": stranger})
def test_revert_second_stop_staking_module(self, helpers, stranger):
@@ -188,13 +200,9 @@ def test_revert_second_stop_staking_module(self, helpers, stranger):
{"from": contracts.agent},
)
- contracts.staking_router.setStakingModuleStatus(1,
- StakingModuleStatus.Stopped,
- {"from": stranger})
- with brownie.reverts(encode_error('StakingModuleStatusTheSame()')):
- contracts.staking_router.setStakingModuleStatus(1,
- StakingModuleStatus.Stopped,
- {"from": stranger})
+ contracts.staking_router.setStakingModuleStatus(1, StakingModuleStatus.Stopped, {"from": stranger})
+ with brownie.reverts(encode_error("StakingModuleStatusTheSame()")):
+ contracts.staking_router.setStakingModuleStatus(1, StakingModuleStatus.Stopped, {"from": stranger})
def test_revert_second_pause_resume_withdrawal_queue(self, helpers, stranger):
inf = contracts.withdrawal_queue.PAUSE_INFINITELY()
@@ -204,7 +212,7 @@ def test_revert_second_pause_resume_withdrawal_queue(self, helpers, stranger):
{"from": contracts.agent},
)
contracts.withdrawal_queue.pauseFor(inf, {"from": stranger})
- with brownie.reverts(encode_error('ResumedExpected()')):
+ with brownie.reverts(encode_error("ResumedExpected()")):
contracts.withdrawal_queue.pauseFor(inf, {"from": stranger})
contracts.withdrawal_queue.grantRole(
@@ -213,7 +221,7 @@ def test_revert_second_pause_resume_withdrawal_queue(self, helpers, stranger):
{"from": contracts.agent},
)
contracts.withdrawal_queue.resume({"from": stranger})
- with brownie.reverts(encode_error('PausedExpected()')):
+ with brownie.reverts(encode_error("PausedExpected()")):
contracts.withdrawal_queue.resume({"from": stranger})
def test_revert_second_pause_resume_validators_exit_bus(self, helpers, stranger):
@@ -224,7 +232,7 @@ def test_revert_second_pause_resume_validators_exit_bus(self, helpers, stranger)
{"from": contracts.agent},
)
contracts.validators_exit_bus_oracle.pauseFor(inf, {"from": stranger})
- with brownie.reverts(encode_error('ResumedExpected()')):
+ with brownie.reverts(encode_error("ResumedExpected()")):
contracts.validators_exit_bus_oracle.pauseFor(inf, {"from": stranger})
contracts.validators_exit_bus_oracle.grantRole(
@@ -233,12 +241,13 @@ def test_revert_second_pause_resume_validators_exit_bus(self, helpers, stranger)
{"from": contracts.agent},
)
contracts.validators_exit_bus_oracle.resume({"from": stranger})
- with brownie.reverts(encode_error('PausedExpected()')):
+ with brownie.reverts(encode_error("PausedExpected()")):
contracts.validators_exit_bus_oracle.resume({"from": stranger})
# Lido contract tests
+
@pytest.mark.usefixtures("stopped_lido")
def test_stopped_lido_cant_stake(stranger):
with brownie.reverts("STAKING_PAUSED"):
@@ -281,26 +290,35 @@ def test_paused_staking_can_report():
contracts.lido.pauseStaking({"from": contracts.voting})
oracle_report()
+
# Staking module tests
+
def test_paused_staking_module_cant_stake():
contracts.staking_router.pauseStakingModule(1, {"from": contracts.deposit_security_module})
- with brownie.reverts(encode_error('StakingModuleNotActive()')):
+ with brownie.reverts(encode_error("StakingModuleNotActive()")):
contracts.lido.deposit(1, 1, "0x", {"from": contracts.deposit_security_module}),
-def test_paused_staking_module_can_reward():
+def test_paused_staking_module_can_reward(burner: Contract):
_, module_address, *_ = contracts.staking_router.getStakingModule(1)
contracts.staking_router.pauseStakingModule(1, {"from": contracts.deposit_security_module})
shares_before = contracts.lido.sharesOf(module_address)
(report_tx, _) = oracle_report()
print(report_tx.events["Transfer"])
- assert report_tx.events["Transfer"][1]["to"] == module_address
- assert report_tx.events["Transfer"][1]["from"] == ZERO_ADDRESS
- assert report_tx.events["Transfer"][2]["to"] == contracts.agent
- assert report_tx.events["Transfer"][2]["from"] == ZERO_ADDRESS
- assert almostEqEth(report_tx.events["Transfer"][1]["value"], report_tx.events["Transfer"][1]["value"])
- assert report_tx.events["Transfer"][1]["value"] > 0
+ module_index = 0
+ if report_tx.events["Transfer"][module_index]["to"] == burner.address:
+ module_index += 1
+
+ agent_index = module_index + 1
+ assert report_tx.events["Transfer"][module_index]["to"] == module_address
+ assert report_tx.events["Transfer"][module_index]["from"] == ZERO_ADDRESS
+ assert report_tx.events["Transfer"][agent_index]["to"] == contracts.agent
+ assert report_tx.events["Transfer"][agent_index]["from"] == ZERO_ADDRESS
+ assert almostEqEth(
+ report_tx.events["Transfer"][module_index]["value"], report_tx.events["Transfer"][agent_index]["value"]
+ )
+ assert report_tx.events["Transfer"][module_index]["value"] > 0
def test_stopped_staking_module_cant_stake(stranger):
@@ -310,9 +328,8 @@ def test_stopped_staking_module_cant_stake(stranger):
{"from": contracts.agent},
)
- contracts.staking_router.setStakingModuleStatus(1, StakingModuleStatus.Stopped,
- {"from": stranger})
- with brownie.reverts(encode_error('StakingModuleNotActive()')):
+ contracts.staking_router.setStakingModuleStatus(1, StakingModuleStatus.Stopped, {"from": stranger})
+ with brownie.reverts(encode_error("StakingModuleNotActive()")):
contracts.lido.deposit(1, 1, "0x", {"from": contracts.deposit_security_module}),
@@ -323,25 +340,28 @@ def test_stopped_staking_module_cant_reward(stranger):
{"from": contracts.agent},
)
_, module_address, *_ = contracts.staking_router.getStakingModule(1)
- contracts.staking_router.setStakingModuleStatus(1, StakingModuleStatus.Stopped,
- {"from": stranger})
+ contracts.staking_router.setStakingModuleStatus(1, StakingModuleStatus.Stopped, {"from": stranger})
shares_before = contracts.lido.sharesOf(module_address)
oracle_report()
assert contracts.lido.sharesOf(module_address) == shares_before
+
def test_stopped_lido_cant_reward(stranger):
contracts.lido.stop({"from": contracts.voting})
with brownie.reverts("CONTRACT_IS_STOPPED"):
oracle_report()
+
# Withdrawal queue tests
+
def make_withdrawal_request(stranger):
stranger.transfer(contracts.lido, DEPOSIT_AMOUNT)
contracts.lido.approve(contracts.withdrawal_queue, DEPOSIT_AMOUNT - 1, {"from": stranger})
contracts.withdrawal_queue.requestWithdrawals([DEPOSIT_AMOUNT - 1], stranger, {"from": stranger})
+
def pause_withdrawal_queue(stranger):
contracts.withdrawal_queue.grantRole(
web3.keccak(text="PAUSE_ROLE"),
@@ -351,20 +371,24 @@ def pause_withdrawal_queue(stranger):
inf = contracts.withdrawal_queue.PAUSE_INFINITELY()
contracts.withdrawal_queue.pauseFor(inf, {"from": stranger})
+
def test_paused_withdrawal_queue_cant_withdraw(stranger):
pause_withdrawal_queue(stranger)
- with brownie.reverts(encode_error('ResumedExpected()')):
+ with brownie.reverts(encode_error("ResumedExpected()")):
make_withdrawal_request(stranger)
+
def test_paused_withdrawal_queue_can_stake(stranger):
pause_withdrawal_queue(stranger)
stranger.transfer(contracts.lido, DEPOSIT_AMOUNT)
+
def test_paused_withdrawal_queue_can_rebase(stranger):
pause_withdrawal_queue(stranger)
oracle_report()
+
def test_stopped_lido_cant_withdraw(stranger):
stranger.transfer(contracts.lido, DEPOSIT_AMOUNT)
contracts.lido.approve(contracts.withdrawal_queue, DEPOSIT_AMOUNT - 1, {"from": stranger})
@@ -377,13 +401,16 @@ def test_stopped_lido_cant_withdraw(stranger):
# Validators exit bus tests
+
def prepare_report():
ref_slot, _ = contracts.hash_consensus_for_validators_exit_bus_oracle.getCurrentFrame()
consensus_version = contracts.validators_exit_bus_oracle.getConsensusVersion()
items, hash = prepare_exit_bus_report([], ref_slot)
fast_lane_members, _ = contracts.hash_consensus_for_validators_exit_bus_oracle.getFastLaneMembers()
for m in fast_lane_members:
- contracts.hash_consensus_for_validators_exit_bus_oracle.submitReport(ref_slot, hash, consensus_version, {"from": m})
+ contracts.hash_consensus_for_validators_exit_bus_oracle.submitReport(
+ ref_slot, hash, consensus_version, {"from": m}
+ )
return items, m
@@ -396,6 +423,7 @@ def pause_validators_exit_bus(stranger):
inf = contracts.validators_exit_bus_oracle.PAUSE_INFINITELY()
contracts.validators_exit_bus_oracle.pauseFor(inf, {"from": stranger})
+
def test_paused_validators_exit_bus_cant_submit_report(stranger):
chain.sleep(2 * 24 * 3600)
chain.mine()
@@ -405,9 +433,10 @@ def test_paused_validators_exit_bus_cant_submit_report(stranger):
pause_validators_exit_bus(stranger)
report, member = prepare_report()
- with brownie.reverts(encode_error('ResumedExpected()')):
+ with brownie.reverts(encode_error("ResumedExpected()")):
contracts.validators_exit_bus_oracle.submitReportData(report, contract_version, {"from": member})
+
def test_stopped_lido_can_exit_validators(stranger):
chain.sleep(2 * 24 * 3600)
chain.mine()
diff --git a/tests/regression/test_validator_exit_bus_happy_path.py b/tests/regression/test_validator_exit_bus_happy_path.py
index 737f7d4d..38b7820d 100644
--- a/tests/regression/test_validator_exit_bus_happy_path.py
+++ b/tests/regression/test_validator_exit_bus_happy_path.py
@@ -5,7 +5,9 @@
from utils.config import contracts
from utils.test.exit_bus_data import LidoValidator
from utils.test.oracle_report_helpers import (
- wait_to_next_available_report_time, reach_consensus, prepare_exit_bus_report
+ wait_to_next_available_report_time,
+ reach_consensus,
+ prepare_exit_bus_report,
)
@@ -19,17 +21,20 @@ class ProcessingState:
requests_count: int
requests_submitted: int
+
def _wait_for_next_ref_slot():
wait_to_next_available_report_time(contracts.hash_consensus_for_validators_exit_bus_oracle)
ref_slot, _ = contracts.hash_consensus_for_validators_exit_bus_oracle.getCurrentFrame()
return ref_slot
+
def send_report_with_consensus(ref_slot, report, report_hash):
consensus_version = contracts.validators_exit_bus_oracle.getConsensusVersion()
contract_version = contracts.validators_exit_bus_oracle.getContractVersion()
- submitter = reach_consensus(ref_slot, report_hash, consensus_version,
- contracts.hash_consensus_for_validators_exit_bus_oracle)
+ submitter = reach_consensus(
+ ref_slot, report_hash, consensus_version, contracts.hash_consensus_for_validators_exit_bus_oracle
+ )
return contracts.validators_exit_bus_oracle.submitReportData(report, contract_version, {"from": submitter})
@@ -52,8 +57,8 @@ def test_send_zero_validators_to_exit(helpers):
processing_state_after = ProcessingState(*contracts.validators_exit_bus_oracle.getProcessingState())
# Asserts
- helpers.assert_single_event_named('ProcessingStarted', tx, {"refSlot": ref_slot, "hash": report_hash_hex})
- helpers.assert_event_not_emitted('ValidatorExitRequest', tx)
+ helpers.assert_single_event_named("ProcessingStarted", tx, {"refSlot": ref_slot, "hash": report_hash_hex})
+ helpers.assert_event_not_emitted("ValidatorExitRequest", tx)
assert total_requests_after == total_requests_before
@@ -83,7 +88,8 @@ def test_send_validator_to_exit(helpers, web3):
last_processing_ref_slot_before = contracts.validators_exit_bus_oracle.getLastProcessingRefSlot()
processing_state_before = ProcessingState(*contracts.validators_exit_bus_oracle.getProcessingState())
last_requested_validator_index_before = contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(
- module_id, [no_id])
+ module_id, [no_id]
+ )
tx = send_report_with_consensus(ref_slot, report, report_hash)
@@ -92,16 +98,22 @@ def test_send_validator_to_exit(helpers, web3):
last_processing_ref_slot_after = contracts.validators_exit_bus_oracle.getLastProcessingRefSlot()
processing_state_after = ProcessingState(*contracts.validators_exit_bus_oracle.getProcessingState())
last_requested_validator_index_after = contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(
- module_id, [no_id])
+ module_id, [no_id]
+ )
# Asserts
- helpers.assert_single_event_named('ProcessingStarted', tx, {"refSlot": ref_slot, "hash": report_hash_hex})
- helpers.assert_single_event_named('ValidatorExitRequest', tx,
- {"stakingModuleId": module_id,
- "nodeOperatorId": no_id,
- "validatorIndex": validator_id,
- "validatorPubkey": validator_key,
- "timestamp": web3.eth.get_block(web3.eth.block_number).timestamp})
+ helpers.assert_single_event_named("ProcessingStarted", tx, {"refSlot": ref_slot, "hash": report_hash_hex})
+ helpers.assert_single_event_named(
+ "ValidatorExitRequest",
+ tx,
+ {
+ "stakingModuleId": module_id,
+ "nodeOperatorId": no_id,
+ "validatorIndex": validator_id,
+ "validatorPubkey": validator_key,
+ "timestamp": web3.eth.get_block(web3.eth.block_number).timestamp,
+ },
+ )
assert total_requests_after == total_requests_before + 1
@@ -122,8 +134,8 @@ def test_send_multiple_validators_to_exit(helpers, web3):
"""
The same as test above but with multiple validators on different node operators
"""
- first_no_global_index = (first_module_id, first_no_id) = (1, 1)
- second_no_global_index = (second_module_id, second_no_id) = (1, 2)
+ first_no_global_index = (first_module_id, first_no_id) = (1, 7)
+ second_no_global_index = (second_module_id, second_no_id) = (1, 8)
first_validator_id = 2
second_validator_id = 3
first_validator_key = contracts.node_operators_registry.getSigningKey(first_no_id, first_validator_id)[0]
@@ -133,7 +145,8 @@ def test_send_multiple_validators_to_exit(helpers, web3):
ref_slot = _wait_for_next_ref_slot()
report, report_hash = prepare_exit_bus_report(
- [(first_no_global_index, first_validator), (second_no_global_index, second_validator)], ref_slot)
+ [(first_no_global_index, first_validator), (second_no_global_index, second_validator)], ref_slot
+ )
report_hash_hex = HexString(report_hash, "bytes")
# Collect state before
@@ -141,9 +154,11 @@ def test_send_multiple_validators_to_exit(helpers, web3):
last_processing_ref_slot_before = contracts.validators_exit_bus_oracle.getLastProcessingRefSlot()
processing_state_before = ProcessingState(*contracts.validators_exit_bus_oracle.getProcessingState())
first_last_requested_validator_index_before = contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(
- first_module_id, [first_no_id])
- second_last_requested_validator_index_before = contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(
- second_module_id, [second_no_id])
+ first_module_id, [first_no_id]
+ )
+ second_last_requested_validator_index_before = (
+ contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(second_module_id, [second_no_id])
+ )
tx = send_report_with_consensus(ref_slot, report, report_hash)
@@ -152,24 +167,30 @@ def test_send_multiple_validators_to_exit(helpers, web3):
last_processing_ref_slot_after = contracts.validators_exit_bus_oracle.getLastProcessingRefSlot()
processing_state_after = ProcessingState(*contracts.validators_exit_bus_oracle.getProcessingState())
first_last_requested_validator_index_after = contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(
- first_module_id, [first_no_id])
+ first_module_id, [first_no_id]
+ )
second_last_requested_validator_index_after = contracts.validators_exit_bus_oracle.getLastRequestedValidatorIndices(
- second_module_id, [second_no_id])
+ second_module_id, [second_no_id]
+ )
# Asserts
- helpers.assert_single_event_named('ProcessingStarted', tx, {"refSlot": ref_slot, "hash": report_hash_hex})
+ helpers.assert_single_event_named("ProcessingStarted", tx, {"refSlot": ref_slot, "hash": report_hash_hex})
events = helpers.filter_events_from(tx.receiver, tx.events["ValidatorExitRequest"])
assert len(events) == 2
- assert dict(events[0]) == {"stakingModuleId": first_module_id,
- "nodeOperatorId": first_no_id,
- "validatorIndex": first_validator_id,
- "validatorPubkey": first_validator_key,
- "timestamp": web3.eth.get_block(web3.eth.block_number).timestamp}
- assert dict(events[1]) == {"stakingModuleId": second_module_id,
- "nodeOperatorId": second_no_id,
- "validatorIndex": second_validator_id,
- "validatorPubkey": second_validator_key,
- "timestamp": web3.eth.get_block(web3.eth.block_number).timestamp}
+ assert dict(events[0]) == {
+ "stakingModuleId": first_module_id,
+ "nodeOperatorId": first_no_id,
+ "validatorIndex": first_validator_id,
+ "validatorPubkey": first_validator_key,
+ "timestamp": web3.eth.get_block(web3.eth.block_number).timestamp,
+ }
+ assert dict(events[1]) == {
+ "stakingModuleId": second_module_id,
+ "nodeOperatorId": second_no_id,
+ "validatorIndex": second_validator_id,
+ "validatorPubkey": second_validator_key,
+ "timestamp": web3.eth.get_block(web3.eth.block_number).timestamp,
+ }
assert total_requests_after == total_requests_before + 2