Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/normal_vote_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ jobs:
runs-on: "ubuntu-latest"
timeout-minutes: 100

services:
hardhat-node:
image: ghcr.io/lidofinance/hardhat-node:2.26.0
ports:
- 8545:8545
env:
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }}
HARDFORK: "cancun"

steps:
- uses: actions/checkout@v4
- name: Main action
Expand All @@ -30,6 +39,15 @@ jobs:
runs-on: "ubuntu-latest"
timeout-minutes: 100

services:
hardhat-node:
image: ghcr.io/lidofinance/hardhat-node:2.26.0
ports:
- 8546:8545
env:
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }}
HARDFORK: "cancun"

steps:
- uses: actions/checkout@v4
- name: Main action
Expand Down
3 changes: 2 additions & 1 deletion configs/config_mainnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
USDT_TOKEN = "0xdac17f958d2ee523a2206206994597c13d831ec7"
USDC_TOKEN = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
WETH_TOKEN = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
MATIC_TOKEN = "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"

#
# Lido V2 upgrade entries
Expand Down Expand Up @@ -161,7 +162,7 @@
SIMPLE_DVT_ARAGON_APP_NAME = "simple-dvt"
SIMPLE_DVT_ARAGON_APP_ID = "0xe1635b63b5f7b5e545f2a637558a4029dea7905361a2f0fc28c66e9136cf86a4"
SIMPLE_DVT_MODULE_STUCK_PENALTY_DELAY = 0
SIMPLE_DVT_MODULE_TARGET_SHARE_BP = 400
SIMPLE_DVT_MODULE_TARGET_SHARE_BP = 430
SIMPLE_DVT_MODULE_MODULE_FEE_BP = 800
SIMPLE_DVT_MODULE_TREASURY_FEE_BP = 200
SIMPLE_DVT_MODULE_ID = 2
Expand Down
151 changes: 151 additions & 0 deletions scripts/vote_2025_11_24.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
"""
# Vote 2025_11_24

=== 1. DG PROPOPSAL ===
I. Decrease Easy Track TRP limit
1.1. Set spent amount for Easy Track TRP registry 0x231Ac69A1A37649C6B06a71Ab32DdD92158C80b8 to 0 LDO
1.2. Set limit for Easy Track TRP registry 0x231Ac69A1A37649C6B06a71Ab32DdD92158C80b8 to 15'000'000 LDO with unchanged period duration of 12 months

II. Increase SDVT target share
1.3. Increase SDVT (MODULE_ID = 2) share limit from 400 bps to 430 bps in Staking Router 0xFdDf38947aFB03C621C71b06C9C70bce73f12999

=== NON-DG ITEMS ===
III. Transfer MATIC from Lido Treasury to Lido Labs Foundation
2. Transfer 508,106 MATIC 0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0 from Aragon Agent 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c to Lido Labs Foundation 0x95B521B4F55a447DB89f6a27f951713fC2035f3F

# TODO (after vote) Vote #{vote number} passed & executed on ${date+time}, block ${blockNumber}.
"""

from typing import Dict, List, Tuple

from utils.finance import make_matic_payout
from utils.voting import bake_vote_items, confirm_vote_script, create_vote
from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description
from utils.config import get_deployer_account, get_is_live, get_priority_fee
from utils.mainnet_fork import pass_and_exec_dao_vote
from utils.dual_governance import submit_proposals
from utils.agent import agent_forward
from brownie import interface
from utils.allowed_recipients_registry import (
unsafe_set_spent_amount,
set_limit_parameters,
)


# ============================== Addresses ===================================
ET_TRP_REGISTRY = "0x231Ac69A1A37649C6B06a71Ab32DdD92158C80b8"
STAKING_ROUTER = "0xFdDf38947aFB03C621C71b06C9C70bce73f12999"
LIDO_LABS_MS = "0x95B521B4F55a447DB89f6a27f951713fC2035f3F"


# ============================== Constants ===================================
SDVT_MODULE_ID = 2
SDVT_MODULE_NEW_TARGET_SHARE_BP = 430
SDVT_MODULE_PRIORITY_EXIT_THRESHOLD_BP = 444
SDVT_MODULE_MODULE_FEE_BP = 800
SDVT_MODULE_TREASURY_FEE_BP = 200
SDVT_MODULE_MAX_DEPOSITS_PER_BLOCK = 150
SDVT_MODULE_MIN_DEPOSIT_BLOCK_DISTANCE = 25

TRP_PERIOD_DURATION_MONTHS = 12
TRP_NEW_LIMIT = 15_000_000 * 10**18
TRP_NEW_SPENT_AMOUNT = 0

MATIC_FOR_TRANSFER = 508_106 * 10**18


# ============================= Description ==================================
# TODO <a description for IPFS (will appear in the voting description on vote.lido.fi)>
IPFS_DESCRIPTION = "omni nov 2025"


# ================================ Main ======================================
def get_vote_items() -> Tuple[List[str], List[Tuple[str, str]]]:

staking_router = interface.StakingRouter(STAKING_ROUTER)

dg_items = [
agent_forward([
# 1.1. Set spent amount for Easy Track TRP registry 0x231Ac69A1A37649C6B06a71Ab32DdD92158C80b8 to 0 LDO
unsafe_set_spent_amount(spent_amount=TRP_NEW_SPENT_AMOUNT, registry_address=ET_TRP_REGISTRY),
]),
agent_forward([
# 1.2. Set limit for Easy Track TRP registry 0x231Ac69A1A37649C6B06a71Ab32DdD92158C80b8 to 15'000'000 LDO with unchanged period duration of 12 months
set_limit_parameters(
limit=TRP_NEW_LIMIT,
period_duration_months=TRP_PERIOD_DURATION_MONTHS,
registry_address=ET_TRP_REGISTRY,
),
]),
agent_forward([
# 1.3. Increase SDVT (MODULE_ID = 2) share limit from 400 bps to 430 bps in Staking Router 0xFdDf38947aFB03C621C71b06C9C70bce73f12999
(
staking_router.address,
staking_router.updateStakingModule.encode_input(
SDVT_MODULE_ID,
SDVT_MODULE_NEW_TARGET_SHARE_BP,
SDVT_MODULE_PRIORITY_EXIT_THRESHOLD_BP,
SDVT_MODULE_MODULE_FEE_BP,
SDVT_MODULE_TREASURY_FEE_BP,
SDVT_MODULE_MAX_DEPOSITS_PER_BLOCK,
SDVT_MODULE_MIN_DEPOSIT_BLOCK_DISTANCE,
),
),
])
]

dg_call_script = submit_proposals([
(dg_items, "TODO DG proposal description")
])

vote_desc_items, call_script_items = zip(
(
"TODO 1. DG submission description",
dg_call_script[0]
),
(
"2. Transfer 508,106 MATIC 0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0 from Aragon Agent 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c to Lido Labs Foundation 0x95B521B4F55a447DB89f6a27f951713fC2035f3F",
make_matic_payout(
target_address=LIDO_LABS_MS,
matic_in_wei=MATIC_FOR_TRANSFER,
reference="Transfer 508,106 MATIC from Treasury to Lido Labs Foundation multisig",
),
)
)

return vote_desc_items, call_script_items


def start_vote(tx_params: Dict[str, str], silent: bool = False):
vote_desc_items, call_script_items = get_vote_items()
vote_items = bake_vote_items(list(vote_desc_items), list(call_script_items))

desc_ipfs = (
calculate_vote_ipfs_description(IPFS_DESCRIPTION)
if silent else upload_vote_ipfs_description(IPFS_DESCRIPTION)
)

vote_id, tx = confirm_vote_script(vote_items, silent, desc_ipfs) and list(
create_vote(vote_items, tx_params, desc_ipfs=desc_ipfs)
)

return vote_id, tx


def main():
tx_params: Dict[str, str] = {"from": get_deployer_account().address}
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}.")


def start_and_execute_vote_on_fork_manual():
if get_is_live():
raise Exception("This script is for local testing only.")

tx_params = {"from": get_deployer_account()}
vote_id, _ = start_vote(tx_params=tx_params, silent=True)
print(f"Vote created: {vote_id}.")
pass_and_exec_dao_vote(int(vote_id), step_by_step=True)
77 changes: 43 additions & 34 deletions tests/_test_2025_MM_DD.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from brownie import chain, interface
from brownie.network.transaction import TransactionReceipt
import pytest

from utils.test.tx_tracing_helpers import (
group_voting_events_from_receipt,
group_dg_events_from_receipt,
count_vote_items_by_events,
display_voting_events,
display_dg_events
)
from utils.evm_script import encode_call_script
from utils.voting import find_metadata_by_vote_id
from utils.ipfs import get_lido_vote_cid_from_str
from utils.dual_governance import PROPOSAL_STATUS
from utils.test.event_validators.dual_governance import validate_dual_governance_submit_event
)


# ============================================================================
Expand All @@ -27,8 +30,10 @@
# NOTE: these addresses might have a different value on other chains

VOTING = "0x2e59A20f205bB85a89C53f1936454680651E618e"
AGENT = "0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c"
EMERGENCY_PROTECTED_TIMELOCK = "0xCE0425301C85c5Ea2A0873A2dEe44d78E02D2316"
DUAL_GOVERNANCE = "0xcdF49b058D606AD34c5789FD8c3BF8B3E54bA2db"
DUAL_GOVERNANCE = "0xC1db28B3301331277e307FDCfF8DE28242A4486E"
DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x23E0B465633FF5178808F4A75186E2F2F9537021"

# TODO Set variable to None if item is not presented
EXPECTED_VOTE_ID = 1
Expand All @@ -40,26 +45,29 @@

@pytest.fixture(scope="module")
def dual_governance_proposal_calls():
# TODO list all Dual Governance proposal calls for events checking
# csm = interface.CSModule(CSM)
# staking_router = interface.StakingRouter(STAKING_ROUTER)
# csm_module_manager_role = csm.MODULE_MANAGER_ROLE()
# csm_verifier_role = csm.VERIFIER_ROLE()
#
# return [
# {
# "target": AGENT,
# "value": 0,
# "data": agent_forward(
# [
# (
# dg_item_address_1, dg_item_encoded_input_1,
# )
# ]
# )[1],
# },
# ]
pass
# TODO Create all the dual governance calls that match the voting script
dg_items = [
# # TODO 1.1. DG voting item 1 description
# agent_forward([
# (dg_item_address_1, dg_item_encoded_input_1)
# ]),
# # TODO 1.2. DG voting item 2 description
# agent_forward([
# (dg_item_address_2, dg_item_encoded_input_2)
# ]),
]

# Convert each dg_item to the expected format
proposal_calls = []
for dg_item in dg_items:
target, data = dg_item # agent_forward returns (target, data)
proposal_calls.append({
"target": target,
"value": 0,
"data": data
})

return proposal_calls


def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, stranger, dual_governance_proposal_calls):
Expand All @@ -68,6 +76,7 @@ def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, stranger, dual_g
# ========================= Arrange variables ===========================
# =======================================================================
voting = interface.Voting(VOTING)
agent = interface.Agent(AGENT)
timelock = interface.EmergencyProtectedTimelock(EMERGENCY_PROTECTED_TIMELOCK)
dual_governance = interface.DualGovernance(DUAL_GOVERNANCE)

Expand Down Expand Up @@ -114,20 +123,20 @@ def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, stranger, dual_g


assert len(vote_events) == EXPECTED_VOTE_EVENTS_COUNT
assert count_vote_items_by_events(vote_tx, voting) == EXPECTED_VOTE_EVENTS_COUNT
assert count_vote_items_by_events(vote_tx, voting.address) == EXPECTED_VOTE_EVENTS_COUNT
if EXPECTED_DG_PROPOSAL_ID is not None:
assert EXPECTED_DG_PROPOSAL_ID == timelock.getProposalsCount()

# Validate DG Proposal Submit event
validate_dual_governance_submit_event(
vote_events[0],
proposal_id=EXPECTED_DG_PROPOSAL_ID,
proposer=VOTING,
executor=DUAL_GOVERNANCE_ADMIN_EXECUTOR,
metadata="TODO DG proposal description",
proposal_calls=dual_governance_proposal_calls,
emitted_by=[EMERGENCY_PROTECTED_TIMELOCK, DUAL_GOVERNANCE],
)
# TODO Validate DG Proposal Submit event
# validate_dual_governance_submit_event(
# vote_events[0],
# proposal_id=EXPECTED_DG_PROPOSAL_ID,
# proposer=VOTING,
# executor=DUAL_GOVERNANCE_ADMIN_EXECUTOR,
# metadata="TODO DG proposal description",
# proposal_calls=dual_governance_proposal_calls,
# emitted_by=[EMERGENCY_PROTECTED_TIMELOCK, DUAL_GOVERNANCE],
# )

# TODO validate all other voting events

Expand All @@ -154,7 +163,7 @@ def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, stranger, dual_g
timelock=EMERGENCY_PROTECTED_TIMELOCK,
admin_executor=DUAL_GOVERNANCE_ADMIN_EXECUTOR,
)
assert count_vote_items_by_events(dg_tx, agent) == EXPECTED_DG_EVENTS_COUNT
assert count_vote_items_by_events(dg_tx, agent.address) == EXPECTED_DG_EVENTS_COUNT
assert len(dg_events) == EXPECTED_DG_EVENTS_COUNT

# TODO validate all DG events
Expand Down
3 changes: 2 additions & 1 deletion tests/acceptance/test_accounting_oracle_negative.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,15 +353,16 @@ def test_unexpected_extra_data_item(self, extra_data_service: ExtraDataService)
(1, 5): self.get_nor_operator_exited_keys(5) + 1,
(1, 6): self.get_nor_operator_exited_keys(6) + 1,
(1, 7): self.get_nor_operator_exited_keys(7) + 1,
(1, 8): self.get_nor_operator_exited_keys(8) + 1,
(1, 9): self.get_nor_operator_exited_keys(9) + 1,
(1, 10): self.get_nor_operator_exited_keys(10) + 1,
(1, 11): self.get_nor_operator_exited_keys(11) + 1,
},
MAX_ITEMS_PER_EXTRA_DATA_TRANSACTION,
1,
)

with reverts(
# In case of OUT_OF_RANGE error just remove exited NO (with all validators exited) from list above ^^^
encode_error(
"UnexpectedExtraDataItemsCount(uint256,uint256)",
[
Expand Down
Loading