Skip to content

Commit fe72b45

Browse files
authored
Merge pull request #58 from lidofinance/feature/review
Review
2 parents 13c73d8 + b34b931 commit fe72b45

File tree

8 files changed

+317
-47
lines changed

8 files changed

+317
-47
lines changed

src/StvPool.sol

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList, FeaturePausable
2626
error NotEnoughToRebalance();
2727
error UnassignedLiabilityOnVault();
2828
error VaultInBadDebt();
29+
error VaultReportStale();
2930

3031
bytes32 public constant DEPOSITS_FEATURE = keccak256("DEPOSITS_FEATURE");
3132
bytes32 public constant DEPOSITS_PAUSE_ROLE = keccak256("DEPOSITS_PAUSE_ROLE");
@@ -208,6 +209,7 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList, FeaturePausable
208209
* @param _recipient Address to receive the minted shares
209210
* @param _referral Address of the referral (if any)
210211
* @return stv Amount of stv minted
212+
* @dev Requires fresh oracle report to price stv accurately
211213
*/
212214
function depositETH(address _recipient, address _referral) public payable returns (uint256 stv) {
213215
stv = _deposit(_recipient, _referral);
@@ -218,6 +220,7 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList, FeaturePausable
218220
if (_recipient == address(0)) revert InvalidRecipient();
219221
_checkFeatureNotPaused(DEPOSITS_FEATURE);
220222
_checkAllowList();
223+
_checkFreshReport();
221224

222225
stv = previewDeposit(msg.value);
223226
_mint(_recipient, stv);
@@ -260,7 +263,7 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList, FeaturePausable
260263
* @param _stethShares Amount of stETH shares to rebalance (18 decimals)
261264
* @dev Only unassigned liability can be rebalanced with this method, not individual liability
262265
* @dev Can be called by anyone if there is any unassigned liability
263-
* @dev Required fresh oracle report before calling
266+
* @dev Requires fresh oracle report before calling (check is performed in VaultHub)
264267
*/
265268
function rebalanceUnassignedLiability(uint256 _stethShares) external {
266269
_checkOnlyUnassignedLiabilityRebalance(_stethShares);
@@ -274,7 +277,7 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList, FeaturePausable
274277
* @dev Only unassigned liability can be rebalanced with this method, not individual liability
275278
* @dev Can be called by anyone if there is any unassigned liability
276279
* @dev This function accepts ETH and uses it to rebalance unassigned liability
277-
* @dev Required fresh oracle report before calling
280+
* @dev Requires fresh oracle report before calling (check is performed in VaultHub)
278281
*/
279282
function rebalanceUnassignedLiabilityWithEther() external payable {
280283
uint256 stethShares = _getSharesByPooledEth(msg.value);
@@ -406,4 +409,12 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList, FeaturePausable
406409
_checkRole(DEPOSITS_RESUME_ROLE, msg.sender);
407410
_resumeFeature(DEPOSITS_FEATURE);
408411
}
412+
413+
// =================================================================================
414+
// ORACLE FRESHNESS CHECK
415+
// =================================================================================
416+
417+
function _checkFreshReport() internal view {
418+
if (!VAULT_HUB.isReportFresh(address(VAULT))) revert VaultReportStale();
419+
}
409420
}

src/StvStETHPool.sol

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ contract StvStETHPool is StvPool {
2929
error InsufficientStv();
3030
error ZeroArgument();
3131
error ArraysLengthMismatch(uint256 firstArrayLength, uint256 secondArrayLength);
32-
error VaultReportStale();
3332
error UndercollateralizedAccount();
3433
error CollateralizedAccount();
3534
error ExcessiveLossSocialization();
@@ -543,7 +542,7 @@ contract StvStETHPool is StvPool {
543542
* @dev Second, if there are remaining liability shares, rebalances Staking Vault
544543
* @dev Requires fresh oracle report, which is checked in the Withdrawal Queue
545544
*/
546-
function rebalanceMintedStethShares(uint256 _stethShares, uint256 _maxStvToBurn)
545+
function rebalanceMintedStethSharesForWithdrawalQueue(uint256 _stethShares, uint256 _maxStvToBurn)
547546
public
548547
returns (uint256 stvBurned)
549548
{
@@ -559,6 +558,8 @@ contract StvStETHPool is StvPool {
559558
* @dev Requires fresh oracle report to price stv accurately
560559
*/
561560
function forceRebalance(address _account) public returns (uint256 stvBurned) {
561+
_checkFreshReport();
562+
562563
(uint256 stethShares, uint256 stv, bool isUndercollateralized) = previewForceRebalance(_account);
563564
if (isUndercollateralized) revert UndercollateralizedAccount();
564565

@@ -573,6 +574,7 @@ contract StvStETHPool is StvPool {
573574
*/
574575
function forceRebalanceAndSocializeLoss(address _account) public returns (uint256 stvBurned) {
575576
_checkRole(LOSS_SOCIALIZER_ROLE, msg.sender);
577+
_checkFreshReport();
576578

577579
(uint256 stethShares, uint256 stv, bool isUndercollateralized) = previewForceRebalance(_account);
578580
if (!isUndercollateralized) revert CollateralizedAccount();
@@ -586,15 +588,13 @@ contract StvStETHPool is StvPool {
586588
* @return stethShares The amount of stETH shares to rebalance, limited by available assets
587589
* @return stv The amount of stv needed to burn in exchange for the stETH shares, limited by user's stv balance
588590
* @return isUndercollateralized True if the user's assets are insufficient to cover the liability
589-
* @dev Requires fresh oracle report to price stv accurately
591+
* @dev Requires fresh oracle report to price stv accurately (not enforced in this method, so caller must ensure it)
590592
*/
591593
function previewForceRebalance(address _account)
592594
public
593595
view
594596
returns (uint256 stethShares, uint256 stv, bool isUndercollateralized)
595597
{
596-
_checkFreshReport();
597-
598598
uint256 stethSharesLiability = mintedStethSharesOf(_account);
599599
uint256 stvBalance = balanceOf(_account);
600600
uint256 assets = assetsOf(_account);
@@ -706,10 +706,6 @@ contract StvStETHPool is StvPool {
706706
}
707707
}
708708

709-
function _checkFreshReport() internal view {
710-
if (!VAULT_HUB.isReportFresh(address(VAULT))) revert VaultReportStale();
711-
}
712-
713709
// =================================================================================
714710
// LOSS SOCIALIZATION LIMITER
715711
// =================================================================================

src/WithdrawalQueue.sol

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable, FeaturePausable
303303
* @param _stvToWithdraw Array of amounts of stv to withdraw
304304
* @param _stethSharesToRebalance Array of amounts of stETH shares to rebalance if supported by the pool, array of 0 otherwise
305305
* @return requestIds the created withdrawal request ids
306+
* @dev Transfers stv and liability shares from the requester to the withdrawal queue
307+
* @dev Requires fresh oracle report to price stv accurately
306308
*/
307309
function requestWithdrawalBatch(
308310
address _owner,
@@ -311,6 +313,7 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable, FeaturePausable
311313
) external returns (uint256[] memory requestIds) {
312314
_checkFeatureNotPaused(WITHDRAWALS_FEATURE);
313315
_checkArrayLength(_stvToWithdraw.length, _stethSharesToRebalance.length);
316+
_checkFreshReport();
314317

315318
requestIds = new uint256[](_stvToWithdraw.length);
316319
for (uint256 i = 0; i < _stvToWithdraw.length; ++i) {
@@ -325,12 +328,15 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable, FeaturePausable
325328
* @param _stethSharesToRebalance Amount of steth shares to rebalance if supported by the pool, 0 otherwise
326329
* @return requestId The created withdrawal request id
327330
* @dev Transfers stv and liability shares from the requester to the withdrawal queue
331+
* @dev Requires fresh oracle report to price stv accurately
328332
*/
329333
function requestWithdrawal(address _owner, uint256 _stvToWithdraw, uint256 _stethSharesToRebalance)
330334
external
331335
returns (uint256 requestId)
332336
{
333337
_checkFeatureNotPaused(WITHDRAWALS_FEATURE);
338+
_checkFreshReport();
339+
334340
requestId = _requestWithdrawal(_owner, _stvToWithdraw, _stethSharesToRebalance);
335341
}
336342

@@ -562,15 +568,15 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable, FeaturePausable
562568

563569
// Stv burning is limited at this point by maxStvToRebalance calculated above
564570
// to make sure that only stv of finalized requests is used for rebalancing
565-
totalStvRebalanced = POOL.rebalanceMintedStethShares(totalStethShares, maxStvToRebalance);
571+
totalStvRebalanced = POOL.rebalanceMintedStethSharesForWithdrawalQueue(totalStethShares, maxStvToRebalance);
566572
}
567573

568574
// 3. Burn any remaining stv that was not used for rebalancing
569575
// The rebalancing may burn less stv than maxStvToRebalance because of:
570576
// - the changed stv rate after the first step
571577
// - accumulated rounding errors in maxStvToRebalance
572578
//
573-
// It's guaranteed by POOL.rebalanceMintedStethShares() that maxStvToRebalance >= totalStvRebalanced
579+
// It's guaranteed by POOL.rebalanceMintedStethSharesForWithdrawalQueue() that maxStvToRebalance >= totalStvRebalanced
574580
uint256 remainingStvForRebalance = maxStvToRebalance - totalStvRebalanced;
575581
if (remainingStvForRebalance > 0) {
576582
POOL.burnStvForWithdrawalQueue(remainingStvForRebalance);
@@ -959,7 +965,7 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable, FeaturePausable
959965
function _calcRequestAmounts(
960966
WithdrawalRequest memory _prevRequest,
961967
WithdrawalRequest memory _request,
962-
Checkpoint memory checkpoint
968+
Checkpoint memory _checkpoint
963969
)
964970
internal
965971
pure
@@ -979,21 +985,21 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable, FeaturePausable
979985
uint256 requestStvRate = (assetsToClaim * E36_PRECISION_BASE) / stv;
980986

981987
// Apply discount if the request stv rate is above the finalization stv rate
982-
if (requestStvRate > checkpoint.stvRate) {
983-
assetsToClaim = Math.mulDiv(stv, checkpoint.stvRate, E36_PRECISION_BASE, Math.Rounding.Floor);
988+
if (requestStvRate > _checkpoint.stvRate) {
989+
assetsToClaim = Math.mulDiv(stv, _checkpoint.stvRate, E36_PRECISION_BASE, Math.Rounding.Floor);
984990
}
985991

986992
if (stethSharesToRebalance > 0) {
987993
assetsToRebalance =
988-
Math.mulDiv(stethSharesToRebalance, checkpoint.stethShareRate, E27_PRECISION_BASE, Math.Rounding.Ceil);
994+
Math.mulDiv(stethSharesToRebalance, _checkpoint.stethShareRate, E27_PRECISION_BASE, Math.Rounding.Ceil);
989995

990996
// Decrease assets to claim by the amount of assets to rebalance
991997
assetsToClaim = Math.saturatingSub(assetsToClaim, assetsToRebalance);
992998
}
993999

9941000
// Apply request finalization gas cost coverage
995-
if (checkpoint.gasCostCoverage > 0) {
996-
gasCostCoverage = Math.min(assetsToClaim, checkpoint.gasCostCoverage);
1001+
if (_checkpoint.gasCostCoverage > 0) {
1002+
gasCostCoverage = Math.min(assetsToClaim, _checkpoint.gasCostCoverage);
9971003
assetsToClaim -= gasCostCoverage;
9981004
}
9991005
}
@@ -1064,8 +1070,10 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable, FeaturePausable
10641070
// CHECKS
10651071
// =================================================================================
10661072

1067-
function _checkArrayLength(uint256 firstArrayLength, uint256 secondArrayLength) internal pure {
1068-
if (firstArrayLength != secondArrayLength) revert ArraysLengthMismatch(firstArrayLength, secondArrayLength);
1073+
function _checkArrayLength(uint256 _firstArrayLength, uint256 _secondArrayLength) internal pure {
1074+
if (_firstArrayLength != _secondArrayLength) {
1075+
revert ArraysLengthMismatch(_firstArrayLength, _secondArrayLength);
1076+
}
10691077
}
10701078

10711079
function _checkFreshReport() internal view {

src/interfaces/IStvStETHPool.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {IVaultHub} from "./core/IVaultHub.sol";
77

88
interface IStvStETHPool is IBasePool {
99
function totalExceedingMintedSteth() external view returns (uint256);
10-
function rebalanceMintedStethShares(uint256 _stethShares, uint256 _maxStvToBurn)
10+
function rebalanceMintedStethSharesForWithdrawalQueue(uint256 _stethShares, uint256 _maxStvToBurn)
1111
external
1212
returns (uint256 stvBurned);
1313
function transferFromWithLiabilityForWithdrawalQueue(address _from, uint256 _stv, uint256 _stethShares) external;

0 commit comments

Comments
 (0)