Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d9f4858
feat: add configurable withdrawal fee and integrate into finalization
avsetsin Nov 1, 2025
4c72632
docs: fix WithdrawalQueue doc comments
avsetsin Nov 1, 2025
3c0aabe
test: add WithdrawalQueue initialization unit tests
avsetsin Nov 1, 2025
bb3d972
test: add WithdrawalQueue tests for pause/resume access control and u…
avsetsin Nov 1, 2025
3f00a97
test: add WithdrawalQueue tests for rebalancing-disabled request with…
avsetsin Nov 1, 2025
9774a6a
test: add InitialPause tests asserting implementation is paused but p…
avsetsin Nov 1, 2025
3f55a17
test: setWithdrawalFee reverts in emergency exit
avsetsin Nov 1, 2025
403297e
test: add finalization edge-case tests
avsetsin Nov 1, 2025
d339cb5
test: add SocializedLoss event emission test for rebalance finalizati…
avsetsin Nov 1, 2025
142522e
test: improve withdrawal-queue claiming tests and clean up formatting
avsetsin Nov 1, 2025
3dc38d9
test: reformat findCheckpointHintBatch calls and reorganize tests
avsetsin Nov 1, 2025
d8ba010
chore: reformat withdrawal-queue tests
avsetsin Nov 1, 2025
5b51f20
chore: reformat unit tests
avsetsin Nov 1, 2025
5504e9b
chore: remove lib/aave-v3-origin git submodule
avsetsin Nov 1, 2025
4c459d0
Merge branch 'testnet-2' of https://github.com/lidofinance/vaults-wra…
avsetsin Nov 2, 2025
4029f4c
feat: block operations when vault is in bad debt
avsetsin Nov 3, 2025
d5f61a1
fix(vault-params): enforce strict < relation for forcedRebalanceThres…
avsetsin Nov 3, 2025
d730e77
fix: use explicit newForcedRebalanceThresholdBP and update equality c…
avsetsin Nov 4, 2025
da8e210
style: reformat function signatures in HarnessCore.s.sol and IBoringO…
avsetsin Nov 4, 2025
a56280f
Merge pull request #40 from lidofinance/fix/frt-cap
avsetsin Nov 4, 2025
99c9cf5
Merge pull request #39 from lidofinance/feature/bad-debt-check
arwer13 Nov 4, 2025
5decf31
refactor: replace withdrawal fee with per-request gas cost coverage
avsetsin Nov 5, 2025
5344061
feat: allow specifying gas-cost coverage recipient in finalize
avsetsin Nov 5, 2025
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
13 changes: 5 additions & 8 deletions script/HarnessCore.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,11 @@ contract HarnessCore is Script {
return (true, abi.decode(ret, (address)));
}

function _arr6(
string memory a,
string memory b,
string memory c,
string memory d,
string memory e,
string memory f
) private pure returns (string[] memory r) {
function _arr6(string memory a, string memory b, string memory c, string memory d, string memory e, string memory f)
private
pure
returns (string[] memory r)
{
r = new string[](6);
r[0] = a;
r[1] = b;
Expand Down
14 changes: 14 additions & 0 deletions src/StvPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList {
error InvalidRequestType();
error NotEnoughToRebalance();
error UnassignedLiabilityOnVault();
error VaultInBadDebt();

bytes32 public constant REQUEST_VALIDATOR_EXIT_ROLE = keccak256("REQUEST_VALIDATOR_EXIT_ROLE");
bytes32 public constant TRIGGER_VALIDATOR_WITHDRAWAL_ROLE = keccak256("TRIGGER_VALIDATOR_WITHDRAWAL_ROLE");
Expand Down Expand Up @@ -314,6 +315,14 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList {
if (totalUnassignedLiabilityShares() > 0) revert UnassignedLiabilityOnVault();
}

/**
* @dev Checks if the vault is not in bad debt (value < liability)
*/
function _checkNoBadDebt() internal view {
uint256 totalValueInStethShares = _getSharesByPooledEth(VAULT_HUB.totalValue(address(STAKING_VAULT)));
if (totalValueInStethShares < totalLiabilityShares()) revert VaultInBadDebt();
}

// =================================================================================
// STETH HELPERS
// =================================================================================
Expand Down Expand Up @@ -346,9 +355,13 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList {
* @dev Overridden method from ERC20 to prevent updates if there are unassigned liability
*/
function _update(address _from, address _to, uint256 _value) internal virtual override {
// Ensure vault is not in bad debt (value < liability) before any transfer
_checkNoBadDebt();

// In rare scenarios, the vault could have liability shares that are not assigned to any pool users
// In such cases, it prevents any transfers until the unassigned liability is rebalanced
_checkNoUnassignedLiability();

super._update(_from, _to, _value);
}

Expand All @@ -374,6 +387,7 @@ contract StvPool is Initializable, ERC20Upgradeable, AllowList {
*/
function burnStvForWithdrawalQueue(uint256 _stv) external {
_checkOnlyWithdrawalQueue();
_checkNoBadDebt();
_checkNoUnassignedLiability();
_burnUnsafe(address(WITHDRAWAL_QUEUE), _stv);
}
Expand Down
17 changes: 11 additions & 6 deletions src/StvStETHPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -488,25 +488,29 @@ contract StvStETHPool is StvPool {
IVaultHub.VaultConnection memory connection = DASHBOARD.vaultConnection();

uint256 maxReserveRatioBP = TOTAL_BASIS_POINTS - 1;
uint256 maxForcedRebalanceThresholdBP = maxReserveRatioBP - 1;

/// Invariants from the OperatorGrid
assert(connection.reserveRatioBP > 0);
assert(connection.reserveRatioBP <= maxReserveRatioBP);
assert(connection.forcedRebalanceThresholdBP > 0);
assert(connection.forcedRebalanceThresholdBP <= connection.reserveRatioBP);
assert(connection.forcedRebalanceThresholdBP < connection.reserveRatioBP);

uint16 newReserveRatioBP = uint16(Math.min(connection.reserveRatioBP + RESERVE_RATIO_GAP_BP, maxReserveRatioBP));
uint16 newThresholdBP =
uint16(Math.min(connection.forcedRebalanceThresholdBP + RESERVE_RATIO_GAP_BP, maxReserveRatioBP));
uint16 newForcedRebalanceThresholdBP = uint16(
Math.min(connection.forcedRebalanceThresholdBP + RESERVE_RATIO_GAP_BP, maxForcedRebalanceThresholdBP)
);

StvStETHPoolStorage storage $ = _getStvStETHPoolStorage();

if (newReserveRatioBP == $.reserveRatioBP && newThresholdBP == $.forcedRebalanceThresholdBP) return;
if (newReserveRatioBP == $.reserveRatioBP && newForcedRebalanceThresholdBP == $.forcedRebalanceThresholdBP) {
return;
}

$.reserveRatioBP = newReserveRatioBP;
$.forcedRebalanceThresholdBP = newThresholdBP;
$.forcedRebalanceThresholdBP = newForcedRebalanceThresholdBP;

emit VaultParametersUpdated(newReserveRatioBP, newThresholdBP);
emit VaultParametersUpdated(newReserveRatioBP, newForcedRebalanceThresholdBP);
}

// =================================================================================
Expand Down Expand Up @@ -671,6 +675,7 @@ contract StvStETHPool is StvPool {
returns (uint256 stvToBurn)
{
_checkNoUnassignedLiability();
_checkNoBadDebt();

if (_stethShares == 0) revert ZeroArgument();
if (_stethShares > mintedStethSharesOf(_account)) revert InsufficientMintedShares();
Expand Down
Loading