Skip to content

Commit

Permalink
Merge pull request #34 from lidofinance/fix/specs-differences
Browse files Browse the repository at this point in the history
Fix the major part of spec and code differences
  • Loading branch information
Psirex authored Jun 19, 2024
2 parents 2f7047e + 3faa735 commit 74327cf
Show file tree
Hide file tree
Showing 23 changed files with 924 additions and 367 deletions.
78 changes: 52 additions & 26 deletions contracts/Configuration.sol
Original file line number Diff line number Diff line change
@@ -1,39 +1,47 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {IConfiguration} from "./interfaces/IConfiguration.sol";
import {IConfiguration, DualGovernanceConfig} from "./interfaces/IConfiguration.sol";

uint256 constant PERCENT = 10 ** 16;

contract Configuration is IConfiguration {
error MaxSealablesLimitOverflow(uint256 count, uint256 limit);

// ---
// Dual Governance State Properties
// ---
uint256 public immutable FIRST_SEAL_RAGE_QUIT_SUPPORT = 3 * PERCENT;
uint256 public immutable SECOND_SEAL_RAGE_QUIT_SUPPORT = 15 * PERCENT;

uint256 public immutable DYNAMIC_TIMELOCK_MIN_DURATION = 3 days;
uint256 public immutable DYNAMIC_TIMELOCK_MAX_DURATION = 30 days;

uint256 public immutable VETO_SIGNALLING_MIN_ACTIVE_DURATION = 5 hours;
uint256 public immutable VETO_SIGNALLING_DEACTIVATION_MAX_DURATION = 5 days;
uint256 public immutable RAGE_QUIT_ACCUMULATION_MAX_DURATION = 3 days;

uint256 public immutable VETO_COOLDOWN_DURATION = 4 days;

uint256 public immutable RAGE_QUIT_EXTENSION_DELAY = 7 days;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_MIN_TIMELOCK = 60 days;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_START_SEQ_NUMBER = 2;

uint256 public immutable RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_A = 0;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_B = 0;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_C = 0;
// ---

address public immutable ADMIN_EXECUTOR;
address public immutable EMERGENCY_GOVERNANCE;

uint256 public immutable AFTER_SUBMIT_DELAY = 3 days;
uint256 public immutable AFTER_SCHEDULE_DELAY = 2 days;

uint256 public immutable RAGE_QUIT_ETH_WITHDRAWAL_TIMELOCK = 30 days;

uint256 public immutable SIGNALLING_COOLDOWN_DURATION = 4 days;
uint256 public immutable SIGNALLING_DEACTIVATION_DURATION = 5 days;
uint256 public immutable SIGNALLING_MIN_PROPOSAL_REVIEW_DURATION = 30 days;

uint256 public immutable SIGNALLING_MIN_DURATION = 3 days;
uint256 public immutable SIGNALLING_MAX_DURATION = 30 days;

uint256 public immutable FIRST_SEAL_THRESHOLD = 3 * PERCENT;
uint256 public immutable SECOND_SEAL_THRESHOLD = 15 * PERCENT;
uint256 public immutable SIGNALLING_ESCROW_MIN_LOCK_TIME = 5 hours;

uint256 public immutable TIE_BREAK_ACTIVATION_TIMEOUT = 365 days;

uint256 public immutable RAGE_QUIT_EXTRA_TIMELOCK = 14 days;
uint256 public immutable RAGE_QUIT_EXTENSION_DELAY = 7 days;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_MIN_TIMELOCK = 60 days;
uint256 public immutable MIN_STATE_DURATION = 5 hours;
uint256 public immutable ESCROW_ASSETS_UNLOCK_DELAY = 5 hours;

// Sealables Array Representation
uint256 private immutable MAX_SELABLES_COUNT = 5;

Expand Down Expand Up @@ -74,15 +82,33 @@ contract Configuration is IConfiguration {
external
view
returns (
uint256 firstSealThreshold,
uint256 secondSealThreshold,
uint256 signallingMinDuration,
uint256 signallingMaxDuration
uint256 firstSealRageQuitSupport,
uint256 secondSealRageQuitSupport,
uint256 dynamicTimelockMinDuration,
uint256 dynamicTimelockMaxDuration
)
{
firstSealThreshold = FIRST_SEAL_THRESHOLD;
secondSealThreshold = SECOND_SEAL_THRESHOLD;
signallingMinDuration = SIGNALLING_MIN_DURATION;
signallingMaxDuration = SIGNALLING_MAX_DURATION;
firstSealRageQuitSupport = FIRST_SEAL_RAGE_QUIT_SUPPORT;
secondSealRageQuitSupport = SECOND_SEAL_RAGE_QUIT_SUPPORT;
dynamicTimelockMinDuration = DYNAMIC_TIMELOCK_MIN_DURATION;
dynamicTimelockMaxDuration = DYNAMIC_TIMELOCK_MAX_DURATION;
}

function getDualGovernanceConfig() external view returns (DualGovernanceConfig memory config) {
config.firstSealRageQuitSupport = FIRST_SEAL_RAGE_QUIT_SUPPORT;
config.secondSealRageQuitSupport = SECOND_SEAL_RAGE_QUIT_SUPPORT;
config.dynamicTimelockMinDuration = DYNAMIC_TIMELOCK_MIN_DURATION;
config.dynamicTimelockMaxDuration = DYNAMIC_TIMELOCK_MAX_DURATION;
config.vetoSignallingMinActiveDuration = VETO_SIGNALLING_MIN_ACTIVE_DURATION;
config.vetoSignallingDeactivationMaxDuration = VETO_SIGNALLING_DEACTIVATION_MAX_DURATION;
config.vetoCooldownDuration = VETO_COOLDOWN_DURATION;
config.rageQuitExtensionDelay = RAGE_QUIT_EXTENSION_DELAY;
config.rageQuitEthClaimMinTimelock = RAGE_QUIT_ETH_CLAIM_MIN_TIMELOCK;
config.rageQuitEthClaimTimelockGrowthStartSeqNumber = RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_START_SEQ_NUMBER;
config.rageQuitEthClaimTimelockGrowthCoeffs = [
RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_A,
RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_B,
RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_C
];
}
}
41 changes: 20 additions & 21 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {ConfigurationProvider} from "./ConfigurationProvider.sol";
import {Proposers, Proposer} from "./libraries/Proposers.sol";
import {ExecutorCall} from "./libraries/Proposals.sol";
import {EmergencyProtection} from "./libraries/EmergencyProtection.sol";
import {DualGovernanceState, State as GovernanceState} from "./libraries/DualGovernanceState.sol";
import {State, DualGovernanceState} from "./libraries/DualGovernanceState.sol";

contract DualGovernance is IGovernance, ConfigurationProvider {
using Proposers for Proposers.State;
Expand All @@ -26,7 +26,6 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
Proposers.State internal _proposers;
DualGovernanceState.Store internal _dgState;
EmergencyProtection.State internal _emergencyProtection;
mapping(uint256 proposalId => uint256 executableAfter) internal _scheduledProposals;

constructor(
address config,
Expand All @@ -40,33 +39,32 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
_proposers.register(adminProposer, CONFIG.ADMIN_EXECUTOR());
}

function submit(ExecutorCall[] calldata calls) external returns (uint256 proposalId) {
function submitProposal(ExecutorCall[] calldata calls) external returns (uint256 proposalId) {
_proposers.checkProposer(msg.sender);
_dgState.activateNextState(CONFIG);
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
_dgState.checkProposalsCreationAllowed();
_dgState.setLastProposalCreationTimestamp();
Proposer memory proposer = _proposers.get(msg.sender);
proposalId = TIMELOCK.submit(proposer.executor, calls);
}

function schedule(uint256 proposalId) external {
_dgState.activateNextState(CONFIG);
_dgState.checkProposalsAdoptionAllowed();
TIMELOCK.schedule(proposalId);
function scheduleProposal(uint256 proposalId) external {
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
uint256 proposalSubmissionTime = TIMELOCK.schedule(proposalId);
_dgState.checkCanScheduleProposal(proposalSubmissionTime);
emit ProposalScheduled(proposalId);
}

function cancelAll() external {
function cancelAllPendingProposals() external {
_proposers.checkAdminProposer(CONFIG, msg.sender);
TIMELOCK.cancelAll();
TIMELOCK.cancelAllNonExecutedProposals();
}

function signallingEscrow() external view returns (address) {
function vetoSignallingEscrow() external view returns (address) {
return address(_dgState.signallingEscrow);
}

function isScheduled(uint256 proposalId) external view returns (bool) {
return _scheduledProposals[proposalId] != 0;
function rageQuitEscrow() external view returns (address) {
return address(_dgState.rageQuitEscrow);
}

function canSchedule(uint256 proposalId) external view returns (bool) {
Expand All @@ -78,10 +76,10 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
// ---

function activateNextState() external {
_dgState.activateNextState(CONFIG);
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
}

function currentState() external view returns (GovernanceState) {
function currentState() external view returns (State) {
return _dgState.currentState();
}

Expand All @@ -90,19 +88,19 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
view
returns (bool isActive, uint256 duration, uint256 activatedAt, uint256 enteredAt)
{
(isActive, duration, activatedAt, enteredAt) = _dgState.getVetoSignallingState(CONFIG);
(isActive, duration, activatedAt, enteredAt) = _dgState.getVetoSignallingState(CONFIG.getDualGovernanceConfig());
}

function getVetoSignallingDeactivationState()
external
view
returns (bool isActive, uint256 duration, uint256 enteredAt)
{
(isActive, duration, enteredAt) = _dgState.getVetoSignallingDeactivationState(CONFIG);
(isActive, duration, enteredAt) = _dgState.getVetoSignallingDeactivationState(CONFIG.getDualGovernanceConfig());
}

function getVetoSignallingDuration() external view returns (uint256) {
return _dgState.getVetoSignallingDuration(CONFIG);
return _dgState.getVetoSignallingDuration(CONFIG.getDualGovernanceConfig());
}

function isSchedulingEnabled() external view returns (bool) {
Expand Down Expand Up @@ -143,13 +141,14 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
// Tiebreaker Protection
// ---

function tiebreakerSchedule(uint256 proposalId) external {
function tiebreakerScheduleProposal(uint256 proposalId) external {
_checkTiebreakerCommittee(msg.sender);
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
_dgState.checkTiebreak(CONFIG);
TIMELOCK.schedule(proposalId);
}

function setTiebreakerProtection(address newTiebreaker) external {
function setTiebreakerCommittee(address newTiebreaker) external {
_checkAdminExecutor(msg.sender);
address oldTiebreaker = _tiebreaker;
if (newTiebreaker != oldTiebreaker) {
Expand Down
10 changes: 5 additions & 5 deletions contracts/EmergencyProtectedTimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ contract EmergencyProtectedTimelock is ConfigurationProvider {
emit ProposalLaunched(msg.sender, executor, newProposalId);
}

function schedule(uint256 proposalId) external {
function schedule(uint256 proposalId) external returns (uint256 submittedAt) {
_checkGovernance(msg.sender);
_proposals.schedule(proposalId, CONFIG.AFTER_SUBMIT_DELAY());
submittedAt = _proposals.schedule(proposalId, CONFIG.AFTER_SUBMIT_DELAY());
}

function execute(uint256 proposalId) external {
_emergencyProtection.checkEmergencyModeActive(false);
_proposals.execute(proposalId, CONFIG.AFTER_SCHEDULE_DELAY());
}

function cancelAll() external {
function cancelAllNonExecutedProposals() external {
_checkGovernance(msg.sender);
_proposals.cancelAll();
}
Expand All @@ -62,7 +62,7 @@ contract EmergencyProtectedTimelock is ConfigurationProvider {
// Emergency Protection Functionality
// ---

function emergencyActivate() external {
function activateEmergencyMode() external {
_emergencyProtection.checkActivationCommittee(msg.sender);
_emergencyProtection.checkEmergencyModeActive(false);
_emergencyProtection.activate();
Expand All @@ -74,7 +74,7 @@ contract EmergencyProtectedTimelock is ConfigurationProvider {
_proposals.execute(proposalId, /* afterScheduleDelay */ 0);
}

function emergencyDeactivate() external {
function deactivateEmergencyMode() external {
_emergencyProtection.checkEmergencyModeActive(true);
if (!_emergencyProtection.isEmergencyModePassed()) {
_checkAdminExecutor(msg.sender);
Expand Down
38 changes: 32 additions & 6 deletions contracts/Escrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {IWithdrawalQueue, WithdrawalRequestStatus} from "./interfaces/IWithdrawa

import {AssetsAccounting, LockedAssetsStats, LockedAssetsTotals} from "./libraries/AssetsAccounting.sol";

import {ArrayUtils} from "./utils/arrays.sol";

interface IDualGovernance {
function activateNextState() external;
}
Expand Down Expand Up @@ -77,6 +79,9 @@ contract Escrow is IEscrow {

_escrowState = EscrowState.SignallingEscrow;
_dualGovernance = IDualGovernance(dualGovernance);

ST_ETH.approve(address(WITHDRAWAL_QUEUE), type(uint256).max);
WST_ETH.approve(address(WITHDRAWAL_QUEUE), type(uint256).max);
}

// ---
Expand All @@ -91,11 +96,24 @@ contract Escrow is IEscrow {
}

function unlockStETH() external {
uint256 sharesUnlocked = _accounting.accountStETHUnlock(CONFIG.ESCROW_ASSETS_UNLOCK_DELAY(), msg.sender);
_accounting.checkAssetsUnlockDelayPassed(msg.sender, CONFIG.SIGNALLING_ESCROW_MIN_LOCK_TIME());
uint256 sharesUnlocked = _accounting.accountStETHUnlock(msg.sender);
ST_ETH.transferShares(msg.sender, sharesUnlocked);
_activateNextGovernanceState();
}

function requestWithdrawalsStETH(uint256[] calldata amounts) external returns (uint256[] memory unstETHIds) {
unstETHIds = WITHDRAWAL_QUEUE.requestWithdrawals(amounts, address(this));
WithdrawalRequestStatus[] memory statuses = WITHDRAWAL_QUEUE.getWithdrawalStatus(unstETHIds);

uint256 sharesTotal = 0;
for (uint256 i = 0; i < statuses.length; ++i) {
sharesTotal += statuses[i].amountOfShares;
}
_accounting.accountStETHUnlock(msg.sender, sharesTotal);
_accounting.accountUnstETHLock(msg.sender, unstETHIds, statuses);
}

// ---
// Lock / Unlock wstETH
// ---
Expand All @@ -107,11 +125,19 @@ contract Escrow is IEscrow {
}

function unlockWstETH() external returns (uint256 wstETHUnlocked) {
wstETHUnlocked = _accounting.accountWstETHUnlock(CONFIG.ESCROW_ASSETS_UNLOCK_DELAY(), msg.sender);
_accounting.checkAssetsUnlockDelayPassed(msg.sender, CONFIG.SIGNALLING_ESCROW_MIN_LOCK_TIME());
wstETHUnlocked = _accounting.accountWstETHUnlock(msg.sender);
WST_ETH.transfer(msg.sender, wstETHUnlocked);
_activateNextGovernanceState();
}

function requestWithdrawalsWstETH(uint256[] calldata amounts) external returns (uint256[] memory unstETHIds) {
uint256 totalAmount = ArrayUtils.sum(amounts);
_accounting.accountWstETHUnlock(msg.sender, totalAmount);
unstETHIds = WITHDRAWAL_QUEUE.requestWithdrawalsWstETH(amounts, address(this));
_accounting.accountUnstETHLock(msg.sender, unstETHIds, WITHDRAWAL_QUEUE.getWithdrawalStatus(unstETHIds));
}

// ---
// Lock / Unlock unstETH
// ---
Expand All @@ -126,7 +152,7 @@ contract Escrow is IEscrow {
}

function unlockUnstETH(uint256[] memory unstETHIds) external {
_accounting.accountUnstETHUnlock(CONFIG.ESCROW_ASSETS_UNLOCK_DELAY(), msg.sender, unstETHIds);
_accounting.accountUnstETHUnlock(CONFIG.SIGNALLING_ESCROW_MIN_LOCK_TIME(), msg.sender, unstETHIds);
uint256 unstETHIdsCount = unstETHIds.length;
for (uint256 i = 0; i < unstETHIdsCount; ++i) {
WITHDRAWAL_QUEUE.transferFrom(address(this), msg.sender, unstETHIds[i]);
Expand Down Expand Up @@ -159,7 +185,7 @@ contract Escrow is IEscrow {
ST_ETH.approve(address(WITHDRAWAL_QUEUE), type(uint256).max);
}

function requestWithdrawalsBatch(uint256 maxWithdrawalRequestsCount) external {
function requestNextWithdrawalsBatch(uint256 maxWithdrawalRequestsCount) external {
_checkEscrowState(EscrowState.RageQuitEscrow);

uint256[] memory requestAmounts = _accounting.formWithdrawalBatch(
Expand Down Expand Up @@ -188,7 +214,7 @@ contract Escrow is IEscrow {
}
}

function claimWithdrawalRequests(uint256[] calldata unstETHIds, uint256[] calldata hints) external {
function claimUnstETH(uint256[] calldata unstETHIds, uint256[] calldata hints) external {
_checkEscrowState(EscrowState.RageQuitEscrow);
uint256[] memory claimableAmounts = WITHDRAWAL_QUEUE.getClaimableEther(unstETHIds, hints);

Expand All @@ -210,7 +236,7 @@ contract Escrow is IEscrow {
Address.sendValue(payable(msg.sender), _accounting.accountStETHWithdraw(msg.sender));
}

function withdrawWstETH() external {
function withdrawWstETHAsETH() external {
_checkEscrowState(EscrowState.RageQuitEscrow);
_checkWithdrawalsTimelockPassed();
Address.sendValue(payable(msg.sender), _accounting.accountWstETHWithdraw(msg.sender));
Expand Down
2 changes: 1 addition & 1 deletion contracts/OwnableExecutor.sol → contracts/Executor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

import {IExecutor} from "./interfaces/IExecutor.sol";

contract OwnableExecutor is IExecutor, Ownable {
contract Executor is IExecutor, Ownable {
constructor(address owner) Ownable(owner) {}

function execute(
Expand Down
8 changes: 4 additions & 4 deletions contracts/SingleGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ contract SingleGovernance is IGovernance, ConfigurationProvider {
TIMELOCK = ITimelock(timelock);
}

function submit(ExecutorCall[] calldata calls) external returns (uint256 proposalId) {
function submitProposal(ExecutorCall[] calldata calls) external returns (uint256 proposalId) {
_checkGovernance(msg.sender);
return TIMELOCK.submit(CONFIG.ADMIN_EXECUTOR(), calls);
}

function schedule(uint256 proposalId) external {
function scheduleProposal(uint256 proposalId) external {
TIMELOCK.schedule(proposalId);
}

function canSchedule(uint256 proposalId) external view returns (bool) {
return TIMELOCK.canSchedule(proposalId);
}

function cancelAll() external {
function cancelAllPendingProposals() external {
_checkGovernance(msg.sender);
TIMELOCK.cancelAll();
TIMELOCK.cancelAllNonExecutedProposals();
}

function _checkGovernance(address account) internal view {
Expand Down
Loading

0 comments on commit 74327cf

Please sign in to comment.