@@ -14,9 +14,8 @@ import "../libraries/OperatorSetLib.sol";
1414contract DurationVaultStrategy is DurationVaultStrategyStorage , StrategyBase {
1515 using OperatorSetLib for OperatorSet;
1616
17- /// @dev Thrown when `_queuedUnderlying` exceeds the vault's underlying balance.
18- /// This indicates inconsistent queued-withdrawal accounting and is treated as an invariant violation.
19- error QueuedUnderlyingExceedsBalance ();
17+ /// @dev Thrown when attempting to allocate while a pending allocation modification already exists.
18+ error PendingAllocation ();
2019
2120 /// @notice Emitted when `maxPerDeposit` value is updated from `previousValue` to `newValue`.
2221 event MaxPerDepositUpdated (uint256 previousValue , uint256 newValue );
@@ -141,16 +140,14 @@ contract DurationVaultStrategy is DurationVaultStrategyStorage, StrategyBase {
141140 }
142141
143142 /// @inheritdoc StrategyBase
143+ /// @dev State and TVL limit checks are performed in `beforeAddShares` which is called
144+ /// after deposit by the StrategyManager. This hook only validates the token type.
144145 function _beforeDeposit (
145146 IERC20 token ,
146- uint256 amount
147+ uint256 /* amount*/
147148 ) internal virtual override {
148149 require (_state == VaultState.DEPOSITS, DepositsLocked ());
149- require (amount <= maxPerDeposit, MaxPerDepositExceedsMax ());
150- uint256 balance = _tokenBalance ();
151- require (balance >= _queuedUnderlying, QueuedUnderlyingExceedsBalance ());
152- require (balance - _queuedUnderlying <= maxTotalDeposits, BalanceExceedsMaxTotalDeposits ());
153- super ._beforeDeposit (token, amount);
150+ super ._beforeDeposit (token, 0 );
154151 }
155152
156153 /// @inheritdoc IDurationVaultStrategy
@@ -196,24 +193,33 @@ contract DurationVaultStrategy is DurationVaultStrategyStorage, StrategyBase {
196193 require (_state == VaultState.DEPOSITS, DepositsLocked ());
197194 require (delegationManager.delegatedTo (staker) == address (this ), MustBeDelegatedToVaultOperator ());
198195
199- // Enforce per-deposit and total caps using the minted shares as proxy for underlying.
196+ // Enforce per-deposit cap using the minted shares as proxy for underlying.
200197 uint256 amountUnderlying = sharesToUnderlyingView (shares);
201198 require (amountUnderlying <= maxPerDeposit, MaxPerDepositExceedsMax ());
202199
203- uint256 balance = _tokenBalance ();
204- require (balance >= _queuedUnderlying, QueuedUnderlyingExceedsBalance ());
205- require (balance - _queuedUnderlying <= maxTotalDeposits, BalanceExceedsMaxTotalDeposits ());
200+ // Enforce total cap using operatorShares (active, non-queued shares).
201+ // At this point, operatorShares hasn't been updated yet, so we add the new shares.
202+ uint256 currentOperatorShares = _getOperatorShares ();
203+ uint256 postDepositUnderlying = sharesToUnderlyingView (currentOperatorShares + shares);
204+ require (postDepositUnderlying <= maxTotalDeposits, BalanceExceedsMaxTotalDeposits ());
205+ }
206+
207+ /// @notice Returns the current operator shares for this vault/strategy.
208+ function _getOperatorShares () internal view returns (uint256 ) {
209+ IStrategy[] memory strategies = new IStrategy [](1 );
210+ strategies[0 ] = IStrategy (address (this ));
211+ uint256 [] memory sharesArray = delegationManager.getOperatorShares (address (this ), strategies);
212+ return sharesArray[0 ];
206213 }
207214
208215 /// @inheritdoc IStrategy
209216 function beforeRemoveShares (
210217 address ,
211- uint256 shares
212- ) external override (IStrategy, StrategyBase) onlyStrategyManager {
218+ uint256
219+ ) external view override (IStrategy, StrategyBase) onlyStrategyManager {
220+ // Queuing withdrawals is blocked during ALLOCATIONS. Withdrawals queued during
221+ // DEPOSITS can complete during ALLOCATIONS since they were queued before lock.
213222 require (_state != VaultState.ALLOCATIONS, WithdrawalsLockedDuringAllocations ());
214- // Track queued underlying when withdrawals are queued (only possible during DEPOSITS).
215- uint256 amountUnderlying = sharesToUnderlyingView (shares);
216- _queuedUnderlying += amountUnderlying;
217223 }
218224
219225 /// @notice Sets the maximum deposits (in underlyingToken) that this strategy will hold and accept per deposit.
@@ -238,11 +244,6 @@ contract DurationVaultStrategy is DurationVaultStrategyStorage, StrategyBase {
238244 return (_operatorSet.avs, _operatorSet.id);
239245 }
240246
241- /// @inheritdoc IDurationVaultStrategy
242- function queuedUnderlying () external view override returns (uint256 ) {
243- return _queuedUnderlying;
244- }
245-
246247 function operatorSetRegistered () public view override returns (bool ) {
247248 return _state == VaultState.DEPOSITS || _state == VaultState.ALLOCATIONS;
248249 }
@@ -252,51 +253,28 @@ contract DurationVaultStrategy is DurationVaultStrategyStorage, StrategyBase {
252253 }
253254
254255 function _beforeWithdrawal (
255- address recipient ,
256+ address , /* recipient*/
256257 IERC20 token ,
257- uint256 amountShares
258- ) internal virtual override {
259- // Withdrawals can be completed during ALLOCATIONS only if they were queued before lock.
260- // Queuing is blocked by `beforeRemoveShares` once ALLOCATIONS starts, so any withdrawal
261- // reaching this point in ALLOCATIONS must have been queued earlier. Redistribution
262- // during slashing is also allowed while locked.
263- super ._beforeWithdrawal (recipient, token, amountShares);
264-
265- // Mirror `StrategyBase.withdraw`'s totalShares guard to avoid overflow in
266- // `sharesToUnderlyingView(amountShares)` when `amountShares` is extremely large.
267- require (amountShares <= totalShares, WithdrawalAmountExceedsTotalDeposits ());
268- uint256 amountUnderlying = sharesToUnderlyingView (amountShares);
269- // Adjust queued-underlying accounting when withdrawals complete as tokens.
270- if (_state == VaultState.ALLOCATIONS) {
271- // During ALLOCATIONS, only withdrawals that were queued pre-lock should be allowed.
272- // Slashing redistribution is also allowed and should not consume queuedUnderlying.
273- if (recipient != allocationManager.getRedistributionRecipient (_operatorSet)) {
274- require (_queuedUnderlying >= amountUnderlying, WithdrawalsLocked ());
275- _queuedUnderlying -= amountUnderlying;
276- }
277- } else {
278- if (_queuedUnderlying >= amountUnderlying) {
279- _queuedUnderlying -= amountUnderlying;
280- } else {
281- _queuedUnderlying = 0 ;
282- }
283- }
258+ uint256 /*amountShares*/
259+ ) internal view virtual override {
260+ // Withdrawals are only blocked at the queue stage (beforeRemoveShares).
261+ // Once queued, withdrawals can complete in any state. The DelegationManager
262+ // validates that only properly-queued withdrawals reach this point.
263+ require (token == underlyingToken, OnlyUnderlyingToken ());
284264 }
285265
286266 function _configureOperatorIntegration (
287267 VaultConfig memory config
288268 ) internal {
289- require (config.operatorSet.avs != address (0 ) && config.operatorSet.id != 0 , OperatorIntegrationInvalid ());
269+ require (config.operatorSet.avs != address (0 ), OperatorIntegrationInvalid ());
290270 _operatorSet = config.operatorSet;
291271
292272 // Set allocation delay strictly greater than withdrawal delay to protect pre-lock queued withdrawals.
293273 uint32 minWithdrawal = delegationManager.minWithdrawalDelayBlocks ();
294- allocationDelayBlocks = minWithdrawal + 1 ;
274+ uint32 allocationDelay = minWithdrawal + 1 ;
295275
296276 // apply allocation delay at registration
297- delegationManager.registerAsOperator (
298- config.delegationApprover, allocationDelayBlocks, config.operatorMetadataURI
299- );
277+ delegationManager.registerAsOperator (config.delegationApprover, allocationDelay, config.operatorMetadataURI);
300278
301279 IAllocationManager.RegisterParams memory params;
302280 params.avs = config.operatorSet.avs;
@@ -311,7 +289,7 @@ contract DurationVaultStrategy is DurationVaultStrategyStorage, StrategyBase {
311289 // Pending modifications would cause ModificationAlreadyPending() in AllocationManager.modifyAllocations.
312290 IAllocationManager.Allocation memory alloc =
313291 allocationManager.getAllocation (address (this ), _operatorSet, IStrategy (address (this )));
314- require (alloc.effectBlock == 0 , " PendingAllocation " );
292+ require (alloc.effectBlock == 0 , PendingAllocation () );
315293
316294 IAllocationManager.AllocateParams[] memory params = new IAllocationManager.AllocateParams [](1 );
317295 params[0 ].operatorSet = _operatorSet;
0 commit comments