Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: timelock tests #116

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ETHEREUM_RPC_URL=YOUR_ETH_RPC_URL
PRIVATE_KEY=YOUR_PRIVATE_KEY
INFURA_PROJECT_ID=YOUR_INFURA_PROJECT_ID
ETHERSCAN_API_KEY=YOUR_ETHERSCAN_API_KEY
Expand Down
38 changes: 38 additions & 0 deletions script/DeployTimelock.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: BSD 3-Clause License
pragma solidity ^0.8.24;

import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";

import {ContractAddresses} from "script/ContractAddresses.sol";
import {BaseScript} from "script/BaseScript.s.sol";
import {ActorAddresses} from "script/Actors.sol";
import {console} from "lib/forge-std/src/console.sol";

contract DeployTimelock is BaseScript {

uint256 public privateKey; // dev: assigned in test setup

TimelockController public timelock;

function run() public {

ActorAddresses.Actors memory _actors = getActors();

privateKey == 0 ? vm.envUint("PRIVATE_KEY") : privateKey;
vm.startBroadcast(privateKey);

address[] memory _proposers = new address[](2);
_proposers[0] = _actors.admin.ADMIN;
_proposers[1] = _actors.eoa.DEFAULT_SIGNER;
address[] memory _executors = new address[](1);
_executors[0] = _actors.admin.ADMIN;
timelock = new TimelockController(
3 days, // delay
_proposers,
_executors,
_actors.admin.ADMIN // admin
);

vm.stopBroadcast();
}
}
3 changes: 2 additions & 1 deletion test/scenarios/ScenarioBaseTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {IDelegationManager} from "lib/eigenlayer-contracts/src/contracts/interfa
import {IStakingNodesManager} from "src/interfaces/IStakingNodesManager.sol";
import {IRewardsDistributor} from "src/interfaces/IRewardsDistributor.sol";
import {IynETH} from "src/interfaces/IynETH.sol";
import {Test} from "forge-std/Test.sol";
import {ynETH} from "src/ynETH.sol";
import {ynLSD} from "src/ynLSD.sol";
import {YieldNestOracle} from "src/YieldNestOracle.sol";
Expand All @@ -29,6 +28,8 @@ import {Utils} from "script/Utils.sol";
import {ActorAddresses} from "script/Actors.sol";
import {TestAssetUtils} from "test/utils/TestAssetUtils.sol";

import "forge-std/Test.sol";

contract ScenarioBaseTest is Test, Utils {

// Utils
Expand Down
159 changes: 159 additions & 0 deletions test/scenarios/Timelock.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";

import {DeployTimelock} from "script/DeployTimelock.s.sol";

import "./ScenarioBaseTest.sol";

contract TimelockTest is ScenarioBaseTest, DeployTimelock {

event Upgraded(address indexed implementation);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

uint256 public constant DELAY = 3 days;

address[] public proxyContracts;

// ============================================================================================
// Setup
// ============================================================================================

function setUp() public override {
ScenarioBaseTest.setUp();

proxyContracts = [
address(yneth),
address(stakingNodesManager),
address(rewardsDistributor),
address(executionLayerReceiver),
address(consensusLayerReceiver)
];

(, privateKey) = makeAddrAndKey("deployer");
DeployTimelock.run();
}

// ============================================================================================
// Tests
// ============================================================================================

function testScheduleAndExecuteUpgrade() public {
_updateProxyAdminOwnersToTimelock();

// operation data
address _target = getTransparentUpgradeableProxyAdminAddress(address(yneth)); // proxy admin
address _implementation = getTransparentUpgradeableProxyImplementationAddress(address(yneth)); // implementation (not changed)
uint256 _value = 0;
bytes memory _data = abi.encodeWithSignature(
"upgradeAndCall(address,address,bytes)",
address(yneth), // proxy
_implementation, // implementation
"" // no data
);
bytes32 _predecessor = bytes32(0);
bytes32 _salt = bytes32(0);
uint256 _delay = 3 days;

vm.startPrank(actors.admin.ADMIN);

// schedule
timelock.schedule(
_target,
_value,
_data,
_predecessor,
_salt,
_delay
);

// skip delay duration
skip(DELAY);

vm.expectEmit(address(yneth));
emit Upgraded(_implementation);

// execute
timelock.execute(
_target,
_value,
_data,
_predecessor,
_salt
);

vm.stopPrank();
}

// @note: change the owner of the contract from the timelock to the default signer
function testSwapTimelockOwnership() public {
_updateProxyAdminOwnersToTimelock();

// operation data
address _newOwner = actors.eoa.DEFAULT_SIGNER;
address _target = getTransparentUpgradeableProxyAdminAddress(address(yneth)); // proxy admin
assertEq(Ownable(_target).owner(), address(timelock), "testSwapTimelockOwnership: E0"); // check current owner

uint256 _value = 0;
bytes memory _data = abi.encodeWithSignature(
"transferOwnership(address)",
_newOwner, // new owner
"" // no data
);
bytes32 _predecessor = bytes32(0);
bytes32 _salt = bytes32(0);
uint256 _delay = 3 days;

vm.startPrank(actors.admin.ADMIN);

// schedule
timelock.schedule(
_target,
_value,
_data,
_predecessor,
_salt,
_delay
);

// skip delay duration
skip(DELAY);

vm.expectEmit(address(_target));
emit OwnershipTransferred(
address(timelock), // oldOwner
_newOwner // newOwner
);

// execute
timelock.execute(
_target,
_value,
_data,
_predecessor,
_salt
);

vm.stopPrank();

assertEq(Ownable(_target).owner(), _newOwner, "testSwapTimelockOwnership: E1");
}

// ============================================================================================
// Internal helpers
// ============================================================================================

function _updateProxyAdminOwnersToTimelock() internal {
for (uint256 i = 0; i < proxyContracts.length; i++) {

// get proxy admin
Ownable _proxyAdmin = Ownable(getTransparentUpgradeableProxyAdminAddress(address(proxyContracts[i])));

// transfer ownership to timelock
vm.prank(_proxyAdmin.owner());
_proxyAdmin.transferOwnership(address(timelock));
}
}
}
Loading