Skip to content

Commit

Permalink
Escrow interface refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Psirex committed Dec 2, 2024
1 parent 39e74b5 commit 4a9350f
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 161 deletions.
29 changes: 13 additions & 16 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {Timestamp} from "./types/Timestamp.sol";
import {IStETH} from "./interfaces/IStETH.sol";
import {IWstETH} from "./interfaces/IWstETH.sol";
import {IWithdrawalQueue} from "./interfaces/IWithdrawalQueue.sol";
import {IEscrow} from "./interfaces/IEscrow.sol";
import {IEscrowBase} from "./interfaces/IEscrow.sol";
import {ITimelock} from "./interfaces/ITimelock.sol";
import {ITiebreaker} from "./interfaces/ITiebreaker.sol";
import {IDualGovernance} from "./interfaces/IDualGovernance.sol";
Expand Down Expand Up @@ -52,8 +52,8 @@ contract DualGovernance is IDualGovernance {

event CancelAllPendingProposalsSkipped();
event CancelAllPendingProposalsExecuted();
event EscrowMasterCopyDeployed(IEscrow escrowMasterCopy);
event ProposalsCancellerSet(address proposalsCanceller);
event EscrowMasterCopyDeployed(IEscrowBase escrowMasterCopy);

// ---
// Sanity Check Parameters & Immutables
Expand Down Expand Up @@ -112,10 +112,6 @@ contract DualGovernance is IDualGovernance {
/// @notice The address of the Timelock contract.
ITimelock public immutable TIMELOCK;

/// @notice The address of the Escrow contract used as the implementation for the Signalling and Rage Quit
/// instances of the Escrows managed by the DualGovernance contract.
IEscrow public immutable ESCROW_MASTER_COPY;

// ---
// Aspects
// ---
Expand Down Expand Up @@ -151,16 +147,17 @@ contract DualGovernance is IDualGovernance {
MAX_TIEBREAKER_ACTIVATION_TIMEOUT = sanityCheckParams.maxTiebreakerActivationTimeout;
MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT = sanityCheckParams.maxSealableWithdrawalBlockersCount;

ESCROW_MASTER_COPY = new Escrow({
IEscrowBase escrowMasterCopy = new Escrow({
dualGovernance: this,
stETH: dependencies.stETH,
wstETH: dependencies.wstETH,
withdrawalQueue: dependencies.withdrawalQueue,
minWithdrawalsBatchSize: sanityCheckParams.minWithdrawalsBatchSize
});
emit EscrowMasterCopyDeployed(ESCROW_MASTER_COPY);

_stateMachine.initialize(dependencies.configProvider, ESCROW_MASTER_COPY);
emit EscrowMasterCopyDeployed(escrowMasterCopy);

_stateMachine.initialize(dependencies.configProvider, escrowMasterCopy);
_resealer.setResealManager(address(dependencies.resealManager));
}

Expand All @@ -183,7 +180,7 @@ contract DualGovernance is IDualGovernance {
ExternalCall[] calldata calls,
string calldata metadata
) external returns (uint256 proposalId) {
_stateMachine.activateNextState(ESCROW_MASTER_COPY);
_stateMachine.activateNextState();
if (!_stateMachine.canSubmitProposal({useEffectiveState: false})) {
revert ProposalSubmissionBlocked();
}
Expand All @@ -198,7 +195,7 @@ contract DualGovernance is IDualGovernance {
/// @param proposalId The unique identifier of the proposal to be scheduled. This ID is obtained when the proposal
/// is initially submitted to the Dual Governance system.
function scheduleProposal(uint256 proposalId) external {
_stateMachine.activateNextState(ESCROW_MASTER_COPY);
_stateMachine.activateNextState();
Timestamp proposalSubmittedAt = TIMELOCK.getProposalDetails(proposalId).submittedAt;
if (!_stateMachine.canScheduleProposal({useEffectiveState: false, proposalSubmittedAt: proposalSubmittedAt})) {
revert ProposalSchedulingBlocked(proposalId);
Expand All @@ -214,7 +211,7 @@ contract DualGovernance is IDualGovernance {
/// @return isProposalsCancelled A boolean indicating whether the proposals were successfully canceled (`true`)
/// or the cancellation was skipped due to an inappropriate state (`false`).
function cancelAllPendingProposals() external returns (bool) {
_stateMachine.activateNextState(ESCROW_MASTER_COPY);
_stateMachine.activateNextState();

if (msg.sender != _proposalsCanceller) {
revert CallerIsNotProposalsCanceller(msg.sender);
Expand Down Expand Up @@ -282,7 +279,7 @@ contract DualGovernance is IDualGovernance {
/// @dev This function should be called when the `persisted` and `effective` states of the system are not equal.
/// If the states are already synchronized, the function will complete without making any changes to the system state.
function activateNextState() external {
_stateMachine.activateNextState(ESCROW_MASTER_COPY);
_stateMachine.activateNextState();
}

/// @notice Updates the address of the configuration provider for the Dual Governance system.
Expand Down Expand Up @@ -472,7 +469,7 @@ contract DualGovernance is IDualGovernance {
/// @param sealable The address of the sealable contract to be resumed.
function tiebreakerResumeSealable(address sealable) external {
_tiebreaker.checkCallerIsTiebreakerCommittee();
_stateMachine.activateNextState(ESCROW_MASTER_COPY);
_stateMachine.activateNextState();
_tiebreaker.checkTie(_stateMachine.getPersistedState(), _stateMachine.normalOrVetoCooldownExitedAt);
_resealer.resealManager.resume(sealable);
}
Expand All @@ -482,7 +479,7 @@ contract DualGovernance is IDualGovernance {
/// @param proposalId The unique identifier of the proposal to be scheduled.
function tiebreakerScheduleProposal(uint256 proposalId) external {
_tiebreaker.checkCallerIsTiebreakerCommittee();
_stateMachine.activateNextState(ESCROW_MASTER_COPY);
_stateMachine.activateNextState();
_tiebreaker.checkTie(_stateMachine.getPersistedState(), _stateMachine.normalOrVetoCooldownExitedAt);
TIMELOCK.schedule(proposalId);
}
Expand All @@ -507,7 +504,7 @@ contract DualGovernance is IDualGovernance {
/// the ResealManager contract.
/// @param sealable The address of the sealable contract to be resealed.
function resealSealable(address sealable) external {
_stateMachine.activateNextState(ESCROW_MASTER_COPY);
_stateMachine.activateNextState();
if (_stateMachine.getPersistedState() == State.Normal) {
revert ResealIsNotAllowedInNormalState();
}
Expand Down
82 changes: 53 additions & 29 deletions contracts/Escrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ import {ETHValue, ETHValues} from "./types/ETHValue.sol";
import {SharesValue, SharesValues} from "./types/SharesValue.sol";
import {PercentD16, PercentsD16} from "./types/PercentD16.sol";

import {IEscrow} from "./interfaces/IEscrow.sol";
import {IEscrowBase, ISignallingEscrow, IRageQuitEscrow} from "./interfaces/IEscrow.sol";
import {IStETH} from "./interfaces/IStETH.sol";
import {IWstETH} from "./interfaces/IWstETH.sol";
import {IWithdrawalQueue} from "./interfaces/IWithdrawalQueue.sol";
import {IDualGovernance} from "./interfaces/IDualGovernance.sol";

import {EscrowState} from "./libraries/EscrowState.sol";
import {EscrowState, State} from "./libraries/EscrowState.sol";
import {WithdrawalsBatchesQueue} from "./libraries/WithdrawalsBatchesQueue.sol";
import {HolderAssets, StETHAccounting, UnstETHAccounting, AssetsAccounting} from "./libraries/AssetsAccounting.sol";

/// @notice This contract is used to accumulate stETH, wstETH, unstETH, and withdrawn ETH from vetoers during the
/// veto signalling and rage quit processes.
/// @dev This contract is intended to be used behind a minimal proxy deployed by the DualGovernance contract.
contract Escrow is IEscrow {
contract Escrow is ISignallingEscrow, IRageQuitEscrow {
using EscrowState for EscrowState.Context;
using AssetsAccounting for AssetsAccounting.Context;
using WithdrawalsBatchesQueue for WithdrawalsBatchesQueue.Context;
Expand Down Expand Up @@ -77,7 +77,7 @@ contract Escrow is IEscrow {

/// @dev Reference to the address of the implementation contract, used to distinguish whether the call
/// is made to the proxy or directly to the implementation.
address private immutable _SELF;
IEscrowBase public immutable ESCROW_MASTER_COPY;

/// @dev The address of the Dual Governance contract.
IDualGovernance public immutable DUAL_GOVERNANCE;
Expand Down Expand Up @@ -106,7 +106,7 @@ contract Escrow is IEscrow {
IDualGovernance dualGovernance,
uint256 minWithdrawalsBatchSize
) {
_SELF = address(this);
ESCROW_MASTER_COPY = this;
DUAL_GOVERNANCE = dualGovernance;

ST_ETH = stETH;
Expand All @@ -120,7 +120,7 @@ contract Escrow is IEscrow {
/// @param minAssetsLockDuration The minimum duration that must pass from the last stETH, wstETH, or unstETH lock
/// by the vetoer before they are allowed to unlock assets from the Escrow.
function initialize(Duration minAssetsLockDuration) external {
if (address(this) == _SELF) {
if (this == ESCROW_MASTER_COPY) {
revert NonProxyCallsForbidden();
}
_checkCallerIsDualGovernance();
Expand All @@ -131,6 +131,13 @@ contract Escrow is IEscrow {
ST_ETH.approve(address(WITHDRAWAL_QUEUE), type(uint256).max);
}

// ---
// Base Escrow Getters
// ---
function getEscrowState() external view returns (State) {
return _escrowState.state;
}

// ---
// Lock & Unlock stETH
// ---
Expand Down Expand Up @@ -274,12 +281,47 @@ contract Escrow is IEscrow {
/// have sufficient time to claim it.
/// @param rageQuitEthWithdrawalsDelay The waiting period that vetoers must observe after the Rage Quit process
/// is finalized before they can withdraw ETH from the Escrow.
function startRageQuit(Duration rageQuitExtensionPeriodDuration, Duration rageQuitEthWithdrawalsDelay) external {
function startRageQuit(
Duration rageQuitExtensionPeriodDuration,
Duration rageQuitEthWithdrawalsDelay
) external returns (IRageQuitEscrow rageQuitEscrow) {
_checkCallerIsDualGovernance();
_escrowState.startRageQuit(rageQuitExtensionPeriodDuration, rageQuitEthWithdrawalsDelay);
_batchesQueue.open(WITHDRAWAL_QUEUE.getLastRequestId());
rageQuitEscrow = IRageQuitEscrow(this);
}

// ---
// Signalling Escrow Getters
// ---

/// @notice Returns the current Rage Quit support value as a percentage.
/// @return rageQuitSupport The current Rage Quit support as a `PercentD16` value.
function getRageQuitSupport() external view returns (PercentD16) {
StETHAccounting memory stETHTotals = _accounting.stETHTotals;
UnstETHAccounting memory unstETHTotals = _accounting.unstETHTotals;

uint256 finalizedETH = unstETHTotals.finalizedETH.toUint256();
uint256 unfinalizedShares = (stETHTotals.lockedShares + unstETHTotals.unfinalizedShares).toUint256();

return PercentsD16.fromFraction({
numerator: ST_ETH.getPooledEthByShares(unfinalizedShares) + finalizedETH,
denominator: ST_ETH.totalSupply() + finalizedETH
});
}

/// @notice Returns the minimum duration that must elapse after the last stETH, wstETH, or unstETH lock
/// by a vetoer before they are permitted to unlock their assets from the Escrow.
function getMinAssetsLockDuration() external view returns (Duration minAssetsLockDuration) {
minAssetsLockDuration = _escrowState.minAssetsLockDuration;
}

// TODO: implement
function getLockedUnstETHState(uint256 unstETHId) external view returns (LockedUnstETHState memory) {}

// TODO: implement
function getSignallingEscrowState() external view returns (SignallingEscrowState memory) {}

// ---
// Request Withdrawal Batches
// ---
Expand Down Expand Up @@ -421,12 +463,6 @@ contract Escrow is IEscrow {
// Escrow Management
// ---

/// @notice Returns the minimum duration that must elapse after the last stETH, wstETH, or unstETH lock
/// by a vetoer before they are permitted to unlock their assets from the Escrow.
function getMinAssetsLockDuration() external view returns (Duration) {
return _escrowState.minAssetsLockDuration;
}

/// @notice Sets the minimum duration that must elapse after the last stETH, wstETH, or unstETH lock
/// by a vetoer before they are permitted to unlock their assets from the Escrow.
/// @param newMinAssetsLockDuration The new minimum lock duration to be set.
Expand Down Expand Up @@ -463,9 +499,12 @@ contract Escrow is IEscrow {
}

// ---
// Getters
// Rage Quit Escrow Getters
// ---

// TODO: implement
function getRageQuitEscrowState() external view returns (RageQuitEscrowState memory) {}

/// @notice Returns the total amounts of locked and claimed assets in the Escrow.
/// @return totals A struct containing the total amounts of locked and claimed assets, including:
/// - `stETHClaimedETH`: The total amount of ETH claimed from locked stETH.
Expand Down Expand Up @@ -541,21 +580,6 @@ contract Escrow is IEscrow {
return _escrowState.rageQuitExtensionPeriodStartedAt;
}

/// @notice Returns the current Rage Quit support value as a percentage.
/// @return rageQuitSupport The current Rage Quit support as a `PercentD16` value.
function getRageQuitSupport() external view returns (PercentD16) {
StETHAccounting memory stETHTotals = _accounting.stETHTotals;
UnstETHAccounting memory unstETHTotals = _accounting.unstETHTotals;

uint256 finalizedETH = unstETHTotals.finalizedETH.toUint256();
uint256 unfinalizedShares = (stETHTotals.lockedShares + unstETHTotals.unfinalizedShares).toUint256();

return PercentsD16.fromFraction({
numerator: ST_ETH.getPooledEthByShares(unfinalizedShares) + finalizedETH,
denominator: ST_ETH.totalSupply() + finalizedETH
});
}

/// @notice Returns whether the Rage Quit process has been finalized.
/// @return A boolean value indicating whether the Rage Quit process has been finalized (`true`) or not (`false`).
function isRageQuitFinalized() external view returns (bool) {
Expand Down
2 changes: 0 additions & 2 deletions contracts/interfaces/IDualGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity 0.8.26;

import {IDualGovernanceConfigProvider} from "./IDualGovernanceConfigProvider.sol";
import {IGovernance} from "./IGovernance.sol";
import {IEscrow} from "./IEscrow.sol";
import {IResealManager} from "./IResealManager.sol";
import {ITiebreaker} from "./ITiebreaker.sol";
import {Timestamp} from "../types/Timestamp.sol";
Expand All @@ -26,7 +25,6 @@ interface IDualGovernance is IGovernance, ITiebreaker {
function MIN_TIEBREAKER_ACTIVATION_TIMEOUT() external view returns (Duration);
function MAX_TIEBREAKER_ACTIVATION_TIMEOUT() external view returns (Duration);
function MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT() external view returns (uint256);
function ESCROW_MASTER_COPY() external view returns (IEscrow);

function canSubmitProposal() external view returns (bool);
function canCancelAllPendingProposals() external view returns (bool);
Expand Down
Loading

0 comments on commit 4a9350f

Please sign in to comment.