|
2 | 2 |
|
3 | 3 | from typing import NamedTuple |
4 | 4 |
|
5 | | -from brownie import chain, convert, interface, web3 |
| 5 | +from brownie import accounts, chain, convert, interface, reverts, web3 |
| 6 | +from eth_abi import encode as encode_abi |
6 | 7 | from brownie.network.event import EventDict |
7 | 8 | from brownie.network.transaction import TransactionReceipt |
8 | 9 |
|
|
20 | 21 | from utils.dual_governance import PROPOSAL_STATUS |
21 | 22 | from utils.test.event_validators.common import validate_events_chain |
22 | 23 | from utils.test.event_validators.dual_governance import validate_dual_governance_submit_event |
| 24 | +from utils.test.event_validators.easy_track import ( |
| 25 | + EVMScriptFactoryAdded, |
| 26 | + validate_evmscript_factory_added_event, |
| 27 | +) |
| 28 | +from utils.easy_track import create_permissions |
23 | 29 | from utils.voting import find_metadata_by_vote_id |
24 | 30 | from utils.ipfs import calculate_vote_ipfs_description, get_lido_vote_cid_from_str |
25 | 31 |
|
|
60 | 66 | DEPOSITOR_BOT_OLD_EOA = "0x9b186cE78Ddd6fF098b4a533Dd17a139e1FFeD76" |
61 | 67 | DEPOSITOR_BOT_DELEGATION_CONTRACT = "0x25636798f6E716b2e6b7dEA8ED52a45271768D7A" |
62 | 68 |
|
| 69 | +ACL = "0x78780e70Eae33e2935814a327f7dB6c01136cc62" |
| 70 | +LIDO = "0x3508A952176b3c15387C97BE809eaffB1982176a" |
| 71 | +EASYTRACK = "0x284D91a7D47850d21A6DEaaC6E538AC7E5E6fc2a" |
| 72 | +EASYTRACK_EVMSCRIPT_EXECUTOR = "0x79a20FD0FA36453B2F45eAbab19bfef43575Ba9E" |
| 73 | +SET_DEPOSITS_RESERVE_TARGET_FACTORY = "0x68009122a394504E8fD7fee58F92Cd73c6A60717" |
| 74 | +SET_DEPOSITS_RESERVE_TARGET_TRUSTED_CALLER = "0x84DffcfB232594975C608DE92544Ff239a24c9E9" |
| 75 | +SET_DEPOSITS_RESERVE_TARGET_MAX = 9600 * 10**18 |
| 76 | + |
63 | 77 | STAKING_MODULE_UNVETTING_ROLE = web3.keccak(text="STAKING_MODULE_UNVETTING_ROLE").hex() |
64 | 78 | TOP_UP_ROLE = web3.keccak(text="TOP_UP_ROLE").hex() |
| 79 | +BUFFER_RESERVE_MANAGER_ROLE = web3.keccak(text="BUFFER_RESERVE_MANAGER_ROLE") |
65 | 80 |
|
66 | 81 | ORACLE_COMMITTEE_QUORUM = 6 |
67 | 82 | NEW_DSM_VERSION = 5 |
@@ -127,10 +142,11 @@ class OracleMemberMapping(NamedTuple): |
127 | 142 | # ============================================================================ |
128 | 143 | EXPECTED_VOTE_ID = None |
129 | 144 | EXPECTED_DG_PROPOSAL_ID = None |
130 | | -EXPECTED_VOTE_EVENTS_COUNT = 1 |
| 145 | +EXPECTED_VOTE_EVENTS_COUNT = 2 |
131 | 146 | # 4 committees * 10 members * 2 (remove + add) + locator upgrade |
132 | 147 | # + unvetting role revoke + grant + top-up role revoke + grant |
133 | | -EXPECTED_DG_EVENTS_COUNT = 85 |
| 148 | +# + buffer reserve manager role grant |
| 149 | +EXPECTED_DG_EVENTS_COUNT = 86 |
134 | 150 | IPFS_DESCRIPTION_HASH = None |
135 | 151 |
|
136 | 152 |
|
@@ -303,6 +319,11 @@ def runtime_upgrade_context(): |
303 | 319 | # Cross-check the deploy addresses against the vote script copies |
304 | 320 | assert NEW_DEPOSIT_SECURITY_MODULE.lower() == vote_script.NEW_DEPOSIT_SECURITY_MODULE.lower() |
305 | 321 | assert NEW_LIDO_LOCATOR_IMPLEMENTATION.lower() == vote_script.NEW_LIDO_LOCATOR_IMPLEMENTATION.lower() |
| 322 | + assert SET_DEPOSITS_RESERVE_TARGET_FACTORY.lower() == vote_script.SET_DEPOSITS_RESERVE_TARGET_FACTORY.lower() |
| 323 | + assert ( |
| 324 | + SET_DEPOSITS_RESERVE_TARGET_TRUSTED_CALLER.lower() |
| 325 | + == vote_script.SET_DEPOSITS_RESERVE_TARGET_TRUSTED_CALLER.lower() |
| 326 | + ) |
306 | 327 | for test_mapping, script_mapping in zip(ORACLE_MEMBER_MAPPINGS, vote_script.ORACLE_MEMBER_MAPPINGS): |
307 | 328 | assert test_mapping.old_member.lower() == script_mapping.old_member.lower() |
308 | 329 | assert test_mapping.delegation_contract.lower() == script_mapping.delegation_contract.lower() |
@@ -369,6 +390,11 @@ def test_vote( |
369 | 390 | # ======================================================================= |
370 | 391 | # ========================= Before voting checks ======================== |
371 | 392 | # ======================================================================= |
| 393 | + acl = interface.ACL(ACL) |
| 394 | + easy_track = interface.EasyTrack(EASYTRACK) |
| 395 | + assert not acl.hasPermission(EASYTRACK_EVMSCRIPT_EXECUTOR, LIDO, BUFFER_RESERVE_MANAGER_ROLE) |
| 396 | + assert SET_DEPOSITS_RESERVE_TARGET_FACTORY not in easy_track.getEVMScriptFactories() |
| 397 | + |
372 | 398 | assert get_lido_vote_cid_from_str(find_metadata_by_vote_id(vote_id)) == expected_ipfs_description_hash |
373 | 399 |
|
374 | 400 | vote_tx: TransactionReceipt = helpers.execute_vote(vote_id=vote_id, accounts=accounts, dao_voting=voting) |
@@ -396,6 +422,16 @@ def test_vote( |
396 | 422 | proposal_calls=dual_governance_proposal_calls, |
397 | 423 | ) |
398 | 424 |
|
| 425 | + # 2. Add SetDepositsReserveTarget factory to Easy Track |
| 426 | + validate_evmscript_factory_added_event( |
| 427 | + vote_events[1], |
| 428 | + EVMScriptFactoryAdded( |
| 429 | + factory_addr=SET_DEPOSITS_RESERVE_TARGET_FACTORY, |
| 430 | + permissions=create_permissions(interface.Lido(LIDO), "setDepositsReserveTarget"), |
| 431 | + ), |
| 432 | + emitted_by=EASYTRACK, |
| 433 | + ) |
| 434 | + |
399 | 435 | # ========================================================================= |
400 | 436 | # ======================= Execute DG Proposal ============================= |
401 | 437 | # ========================================================================= |
@@ -531,17 +567,31 @@ def test_vote( |
531 | 567 | event_index += 1 |
532 | 568 |
|
533 | 569 | # 1.85. Grant TOP_UP_ROLE to the depositor bot DelegationContract |
534 | | - # (the last inner call group also carries the Agent.forward service events) |
535 | 570 | validate_role_grant_event( |
536 | 571 | dg_events[event_index], |
537 | 572 | role_hash=TOP_UP_ROLE, |
538 | 573 | account=DEPOSITOR_BOT_DELEGATION_CONTRACT, |
539 | 574 | sender=AGENT, |
540 | 575 | emitted_by=TOP_UP_GATEWAY, |
541 | | - events_chain=["LogScriptCall", "RoleGranted", "ScriptResult", "Executed"], |
542 | 576 | ) |
543 | 577 | event_index += 1 |
544 | 578 |
|
| 579 | + # 1.86. Grant BUFFER_RESERVE_MANAGER_ROLE to the Easy Track EVMScriptExecutor |
| 580 | + # (the last inner call group also carries the Agent.forward service events) |
| 581 | + validate_events_chain( |
| 582 | + [e.name for e in dg_events[event_index]], |
| 583 | + ["LogScriptCall", "SetPermission", "ScriptResult", "Executed"], |
| 584 | + ) |
| 585 | + set_permission_event = _single_event(dg_events[event_index], "SetPermission") |
| 586 | + assert convert.to_address(set_permission_event["entity"]) == convert.to_address( |
| 587 | + EASYTRACK_EVMSCRIPT_EXECUTOR |
| 588 | + ) |
| 589 | + assert convert.to_address(set_permission_event["app"]) == convert.to_address(LIDO) |
| 590 | + assert _normalize_role(set_permission_event["role"]) == BUFFER_RESERVE_MANAGER_ROLE.hex().replace("0x", "") |
| 591 | + assert set_permission_event["allowed"] is True |
| 592 | + _assert_emitted_by(set_permission_event, ACL) |
| 593 | + event_index += 1 |
| 594 | + |
545 | 595 | assert event_index == EXPECTED_DG_EVENTS_COUNT |
546 | 596 |
|
547 | 597 | # ========================================================================= |
@@ -574,6 +624,54 @@ def test_vote( |
574 | 624 | else: |
575 | 625 | assert after_value == before_value, f"Locator entry {name} changed unexpectedly" |
576 | 626 |
|
| 627 | + # Easy Track factory for deposit reserve target management |
| 628 | + acl = interface.ACL(ACL) |
| 629 | + easy_track = interface.EasyTrack(EASYTRACK) |
| 630 | + factory = interface.SetDepositsReserveTarget(SET_DEPOSITS_RESERVE_TARGET_FACTORY) |
| 631 | + lido = interface.Lido(LIDO) |
| 632 | + |
| 633 | + assert acl.hasPermission(EASYTRACK_EVMSCRIPT_EXECUTOR, LIDO, BUFFER_RESERVE_MANAGER_ROLE) |
| 634 | + assert SET_DEPOSITS_RESERVE_TARGET_FACTORY in easy_track.getEVMScriptFactories() |
| 635 | + assert easy_track.evmScriptFactoryPermissions(SET_DEPOSITS_RESERVE_TARGET_FACTORY) == create_permissions( |
| 636 | + lido, "setDepositsReserveTarget" |
| 637 | + ) |
| 638 | + assert convert.to_address(factory.trustedCaller()) == convert.to_address( |
| 639 | + SET_DEPOSITS_RESERVE_TARGET_TRUSTED_CALLER |
| 640 | + ) |
| 641 | + assert convert.to_address(factory.lido()) == convert.to_address(LIDO) |
| 642 | + assert factory.MAX_DEPOSITS_RESERVE_TARGET() == SET_DEPOSITS_RESERVE_TARGET_MAX |
| 643 | + |
| 644 | + # Happy path: the granted role lets the EVMScriptExecutor move the target, |
| 645 | + # and the factory builds a script for exactly that call |
| 646 | + chain.snapshot() |
| 647 | + try: |
| 648 | + new_target = lido.getDepositsReserveTarget() + 10**18 |
| 649 | + assert new_target <= SET_DEPOSITS_RESERVE_TARGET_MAX |
| 650 | + |
| 651 | + # the factory builds a script for the new target and guards its limits |
| 652 | + call_data = encode_abi(["uint256"], [new_target]) |
| 653 | + assert factory.decodeEVMScriptCallData(call_data) == new_target |
| 654 | + factory.createEVMScript(SET_DEPOSITS_RESERVE_TARGET_TRUSTED_CALLER, call_data) |
| 655 | + |
| 656 | + with reverts("CALLER_IS_FORBIDDEN"): |
| 657 | + factory.createEVMScript(stranger, call_data) |
| 658 | + |
| 659 | + with reverts("DEPOSITS_RESERVE_TARGET_TOO_HIGH"): |
| 660 | + factory.createEVMScript( |
| 661 | + SET_DEPOSITS_RESERVE_TARGET_TRUSTED_CALLER, |
| 662 | + encode_abi(["uint256"], [SET_DEPOSITS_RESERVE_TARGET_MAX + 1]), |
| 663 | + ) |
| 664 | + |
| 665 | + # the granted role lets the EVMScriptExecutor apply the new target |
| 666 | + executor = accounts.at(EASYTRACK_EVMSCRIPT_EXECUTOR, force=True) |
| 667 | + lido.setDepositsReserveTarget(new_target, {"from": executor}) |
| 668 | + assert lido.getDepositsReserveTarget() == new_target |
| 669 | + |
| 670 | + with reverts("SAME_DEPOSITS_RESERVE_TARGET"): |
| 671 | + factory.createEVMScript(SET_DEPOSITS_RESERVE_TARGET_TRUSTED_CALLER, call_data) |
| 672 | + finally: |
| 673 | + chain.revert() |
| 674 | + |
577 | 675 | assert not staking_router.hasRole(STAKING_MODULE_UNVETTING_ROLE, OLD_DEPOSIT_SECURITY_MODULE) |
578 | 676 | assert staking_router.hasRole(STAKING_MODULE_UNVETTING_ROLE, NEW_DEPOSIT_SECURITY_MODULE) |
579 | 677 | assert not top_up_gateway.hasRole(TOP_UP_ROLE, DEPOSITOR_BOT_OLD_EOA) |
|
0 commit comments