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
2 changes: 1 addition & 1 deletion .github/workflows/core_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Run init script
run: docker exec -e CORE_BRANCH tests-runner bash -c 'PYTHONPATH=$PWD make init'
env:
CORE_BRANCH: develop
CORE_BRANCH: master

- name: Run node
run: docker exec -e ETH_RPC_URL -e NODE_OPTIONS --detach tests-runner bash -c 'NODE_PORT=8545 make node'
Expand Down
17 changes: 16 additions & 1 deletion .github/workflows/dual_governance_regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,24 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ETHERSCAN_TOKEN: ${{ secrets.ETHERSCAN_TOKEN }}

- name: Start Anvil
shell: bash
run: |
anvil --fork-url ${{ env.HARDHAT_NODE_URL }} \
--port 8555 \
--no-storage-caching \
--silent &
for i in {1..30}; do
if curl -sf ${{ env.ANVIL_NODE_URL }} ${{ env.CURL_PARAMS }} | grep -q '"result"'; then
echo "Anvil node is ready"
break
fi
sleep 1
done

- name: Run regression tests
run: npm run test:regressions -- --load-accounts
env:
MAINNET_RPC_URL: ${{ env.HARDHAT_NODE_URL }}
MAINNET_RPC_URL: ${{ env.ANVIL_NODE_URL }}
DEPLOY_ARTIFACT_FILE_NAME: deploy-artifact-mainnet.toml
working-directory: dual-governance
74 changes: 74 additions & 0 deletions archive/scripts/vote_2026_04_23.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
Vote 2026_04_23

1. Transfer 2500 stETH 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 from Aragon Agent 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c to Lido Labs Foundation operational multisig 0x95B521B4F55a447DB89f6a27f951713fC2035f3F

Vote #200 passed & executed on Apr-28-2026 08:09:59 PM UTC, block 24980862.
"""

from typing import Dict, List, Tuple

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.finance import make_steth_payout

# ============================== Constants ===================================

PAYMENT_AMOUNT = 2500 * 10**18
LIDO_LABS_MULTISIG = "0x95B521B4F55a447DB89f6a27f951713fC2035f3F"

# ============================= IPFS Description ==================================
IPFS_DESCRIPTION = """
Transfer 2500 stETH from Aragon Agent to Lido Labs Foundation operational multisig, [as proposed on the forum](https://research.lido.fi/t/lido-dao-contribution-to-coordinated-rseth-relief-effort/11483).
"""


def get_vote_items() -> Tuple[List[str], List[Tuple[str, str]]]:
call_script_items = [
make_steth_payout(
target_address=LIDO_LABS_MULTISIG,
steth_in_wei=PAYMENT_AMOUNT,
reference="Transfer 2500 stETH from Aragon Agent to Lido Labs Foundation operational multisig",
)
]
vote_desc_items = [
"1. Transfer 2500 stETH 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 from Aragon Agent 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c to Lido Labs Foundation operational multisig 0x95B521B4F55a447DB89f6a27f951713fC2035f3F"
]

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)
143 changes: 143 additions & 0 deletions archive/tests/test_2026_04_23.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
from brownie import interface
from brownie.network.transaction import TransactionReceipt

from utils.test.helpers import almostEqWithDiff
from utils.test.tx_tracing_helpers import (
group_voting_events_from_receipt,
count_vote_items_by_events,
display_voting_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.test.event_validators.payout import Payout, validate_token_payout_event

# ============================================================================
# ============================== Import vote =================================
# ============================================================================
from archive.scripts.vote_2026_04_23 import (
start_vote,
get_vote_items,
)


# ============================================================================
# ============================== Constants ===================================
# ============================================================================
VOTING = "0x2e59A20f205bB85a89C53f1936454680651E618e"
AGENT = "0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c"

STETH = "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"
LIDO_LABS_MULTISIG = "0x95B521B4F55a447DB89f6a27f951713fC2035f3F"

LDO = "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32"
USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7"
SUSDS = "0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD"
DAI = "0x6B175474E89094C44Da98b954EedeAC495271d0F"
USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
WSTETH = "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"

PAYMENT_AMOUNT = 2500 * 10**18


# ============================================================================
# ============================= Test params ==================================
# ============================================================================
EXPECTED_VOTE_ID = 200
EXPECTED_VOTE_EVENTS_COUNT = 1

IPFS_DESCRIPTION_HASH = "bafkreidq5uylzwlkr4pahs2pmxwekglv5shmhmvaol7vio6g2sdwkp4efi"


def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env):

# =======================================================================
# ========================= Arrange variables ===========================
# =======================================================================
voting = interface.Voting(VOTING)
steth = interface.Lido(STETH)

treasury_tokens = [
interface.MiniMeToken(LDO),
interface.Usdt(USDT),
interface.Susds(SUSDS),
interface.Dai(DAI),
interface.Usdc(USDC),
interface.WstETH(WSTETH),
]

# =========================================================================
# ======================== Identify or Create vote ========================
# =========================================================================
if vote_ids_from_env:
vote_id = vote_ids_from_env[0]
if EXPECTED_VOTE_ID is not None:
assert vote_id == EXPECTED_VOTE_ID
elif EXPECTED_VOTE_ID is not None and voting.votesLength() > EXPECTED_VOTE_ID:
vote_id = EXPECTED_VOTE_ID
else:
vote_id, _ = start_vote({"from": ldo_holder}, silent=True)

_, call_script_items = get_vote_items()
onchain_script = voting.getVote(vote_id)["script"]
assert str(onchain_script).lower() == encode_call_script(call_script_items).lower()

# =========================================================================
# ============================= Execute Vote ==============================
# =========================================================================
is_executed = voting.getVote(vote_id)["executed"]
if not is_executed:
# =======================================================================
# ========================= Before voting checks ========================
# =======================================================================

treasury_steth_balance_before = steth.balanceOf(AGENT)
lido_labs_multisig_steth_balance_before = steth.balanceOf(LIDO_LABS_MULTISIG)
other_treasury_token_balances_before = [token.balanceOf(AGENT) for token in treasury_tokens]

assert get_lido_vote_cid_from_str(find_metadata_by_vote_id(vote_id)) == IPFS_DESCRIPTION_HASH

assert treasury_steth_balance_before >= PAYMENT_AMOUNT, "Not enough stETH in treasury"

vote_tx: TransactionReceipt = helpers.execute_vote(vote_id=vote_id, accounts=accounts, dao_voting=voting)
display_voting_events(vote_tx)
vote_events = group_voting_events_from_receipt(vote_tx)

# =======================================================================
# ========================= After voting checks =========================
# =======================================================================

treasury_steth_balance_after = steth.balanceOf(AGENT)
lido_labs_multisig_steth_balance_after = steth.balanceOf(LIDO_LABS_MULTISIG)
other_treasury_token_balances_after = [token.balanceOf(AGENT) for token in treasury_tokens]

assert almostEqWithDiff(treasury_steth_balance_after, treasury_steth_balance_before - PAYMENT_AMOUNT, diff=2)
assert almostEqWithDiff(
lido_labs_multisig_steth_balance_after, lido_labs_multisig_steth_balance_before + PAYMENT_AMOUNT, diff=2
)
for token, token_balance_before, token_balance_after in zip(
treasury_tokens, other_treasury_token_balances_before, other_treasury_token_balances_after
):
assert token_balance_before == token_balance_after, f"Treasury balance of the token {token} changed"

# Simulate the refund of the payment back to the Aragon Agent
steth.transfer(AGENT, PAYMENT_AMOUNT // 2, {"from": LIDO_LABS_MULTISIG})
assert almostEqWithDiff(
steth.balanceOf(LIDO_LABS_MULTISIG), lido_labs_multisig_steth_balance_before + PAYMENT_AMOUNT // 2, diff=2
)
assert almostEqWithDiff(steth.balanceOf(AGENT), treasury_steth_balance_before - PAYMENT_AMOUNT // 2, diff=2)

steth.transfer(AGENT, PAYMENT_AMOUNT // 2, {"from": LIDO_LABS_MULTISIG})
assert almostEqWithDiff(steth.balanceOf(LIDO_LABS_MULTISIG), lido_labs_multisig_steth_balance_before, diff=2)
assert almostEqWithDiff(steth.balanceOf(AGENT), treasury_steth_balance_before, diff=2)

assert len(vote_events) == EXPECTED_VOTE_EVENTS_COUNT
assert count_vote_items_by_events(vote_tx, voting.address) == EXPECTED_VOTE_EVENTS_COUNT

validate_token_payout_event(
vote_events[0],
Payout(STETH, AGENT, LIDO_LABS_MULTISIG, PAYMENT_AMOUNT),
is_steth=True,
emitted_by=AGENT,
)
2 changes: 1 addition & 1 deletion tests/regression/test_sanity_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def test_report_deviated_simulated_share_rate(steth_holder):


simulated_share_rate = (
actual_share_rate * (MAX_BASIS_POINTS - SIMULATED_SHARE_RATE_DEVIATION_BP_LIMIT - 1) // MAX_BASIS_POINTS
actual_share_rate * (MAX_BASIS_POINTS - SIMULATED_SHARE_RATE_DEVIATION_BP_LIMIT - 2) // MAX_BASIS_POINTS
)

# TODO: Understand how to calculate actual share rate with penalized node operators
Expand Down
Loading