Skip to content

Commit 421b258

Browse files
committed
refactor: added missing 2 parameters maxWithdraw/maxRedeem
1 parent fe22a59 commit 421b258

File tree

5 files changed

+344
-98
lines changed

5 files changed

+344
-98
lines changed

src/core/MultistrategyLockedVault.sol

Lines changed: 159 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,58 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
360360
}
361361

362362
/**
363-
* @notice Override withdrawal functions to handle custodied shares
363+
* @notice Withdraws assets with default parameters (maxLoss = 10000, default queue)
364+
* @dev Overload to match Vyper's default parameters behavior
365+
* Enforces custody withdrawal rules - requires active rage quit with cooldown passed
366+
* @param assets Amount of assets to withdraw
367+
* @param receiver Address to receive the withdrawn assets
368+
* @param owner Address whose shares will be burned
369+
* @return shares Amount of shares actually burned from owner
370+
* @custom:security Requires active custody and cooldown period passed
371+
*/
372+
function withdraw(
373+
uint256 assets,
374+
address receiver,
375+
address owner
376+
) external override(MultistrategyVault, IMultistrategyVault) nonReentrant returns (uint256) {
377+
uint256 shares = _convertToShares(assets, Rounding.ROUND_UP);
378+
_processCustodyWithdrawal(owner, shares);
379+
_redeem(msg.sender, receiver, owner, assets, shares, 0, new address[](0));
380+
return shares;
381+
}
382+
383+
/**
384+
* @notice Withdraws assets from the vault with custody enforcement
385+
* @dev ERC4626-extended withdraw function with custody checks
386+
*
387+
* CUSTODY REQUIREMENTS:
388+
* - Owner must have initiated rage quit (active custody)
389+
* - Cooldown period must have passed
390+
* - Withdrawal amount cannot exceed custodied shares
391+
* - Multiple partial withdrawals allowed from same custody
392+
*
393+
* CONVERSION:
394+
* - Uses ROUND_UP when calculating shares (favors vault)
395+
* - shares = (assets * totalSupply + totalAssets - 1) / totalAssets
396+
*
397+
* BEHAVIOR:
398+
* - Validates custody status before processing withdrawal
399+
* - Updates custody by reducing locked shares
400+
* - Clears custody when all locked shares withdrawn
401+
* - Follows standard withdrawal flow after custody checks
402+
*
403+
* LOSS HANDLING:
404+
* - maxLoss_ = 0: Revert if any loss
405+
* - maxLoss_ = 10000: Accept any loss (100%)
406+
* - Users receive proportional share of unrealized losses
407+
*
408+
* @param assets Amount of assets to withdraw
409+
* @param receiver Address to receive the withdrawn assets
410+
* @param owner Address whose shares will be burned
411+
* @param maxLoss Maximum acceptable loss in basis points (0-10000)
412+
* @param strategiesArray Optional custom withdrawal queue (empty = use default)
413+
* @return shares Amount of shares actually burned from owner
414+
* @custom:security Reentrancy protected, requires active custody
364415
*/
365416
function withdraw(
366417
uint256 assets,
@@ -376,7 +427,58 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
376427
}
377428

378429
/**
379-
* @notice Override redeem function to handle custodied shares
430+
* @notice Redeems shares with default parameters (maxLoss = 10000, default queue)
431+
* @dev Overload to match Vyper's default parameters behavior
432+
* Enforces custody withdrawal rules - requires active rage quit with cooldown passed
433+
* @param shares Exact amount of shares to burn
434+
* @param receiver Address to receive the withdrawn assets
435+
* @param owner Address whose shares will be burned
436+
* @return assets Amount of assets actually withdrawn and sent to receiver
437+
* @custom:security Requires active custody and cooldown period passed
438+
*/
439+
function redeem(
440+
uint256 shares,
441+
address receiver,
442+
address owner
443+
) external override(MultistrategyVault, IMultistrategyVault) nonReentrant returns (uint256) {
444+
_processCustodyWithdrawal(owner, shares);
445+
uint256 assets = _convertToAssets(shares, Rounding.ROUND_DOWN);
446+
return _redeem(msg.sender, receiver, owner, assets, shares, 10_000, new address[](0));
447+
}
448+
449+
/**
450+
* @notice Redeems exact amount of shares for assets with custody enforcement
451+
* @dev ERC4626-extended redeem function with custody checks
452+
*
453+
* CUSTODY REQUIREMENTS:
454+
* - Owner must have initiated rage quit (active custody)
455+
* - Cooldown period must have passed
456+
* - Redemption amount cannot exceed custodied shares
457+
* - Multiple partial redemptions allowed from same custody
458+
*
459+
* CONVERSION:
460+
* - Uses ROUND_DOWN when calculating assets (favors vault)
461+
* - assets = (shares * totalAssets) / totalSupply
462+
*
463+
* BEHAVIOR:
464+
* - Validates custody status before processing redemption
465+
* - Burns exact shares amount from owner
466+
* - Updates custody by reducing locked shares
467+
* - Clears custody when all locked shares redeemed
468+
* - May return less assets than expected if losses occur
469+
*
470+
* DIFFERENCE FROM WITHDRAW:
471+
* - withdraw(): User specifies assets, function calculates shares
472+
* - redeem(): User specifies shares, function calculates assets
473+
* - redeem() may return less assets than preview if losses occur
474+
*
475+
* @param shares Exact amount of shares to burn
476+
* @param receiver Address to receive the withdrawn assets
477+
* @param owner Address whose shares will be burned
478+
* @param maxLoss Maximum acceptable loss in basis points (0-10000)
479+
* @param strategiesArray Optional custom withdrawal queue (empty = use default)
480+
* @return assets Amount of assets actually withdrawn and sent to receiver
481+
* @custom:security Reentrancy protected, requires active custody
380482
*/
381483
function redeem(
382484
uint256 shares,
@@ -466,7 +568,7 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
466568
*/
467569
function _transfer(address sender_, address receiver_, uint256 amount_) internal override {
468570
// Check if sender has locked shares that would prevent this transfer
469-
CustodyInfo memory custody = custodyInfo[sender_];
571+
CustodyInfo storage custody = custodyInfo[sender_];
470572

471573
if (custody.lockedShares > 0) {
472574
uint256 senderBalance = balanceOf(sender_);
@@ -495,9 +597,9 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
495597
function maxWithdraw(
496598
address owner_,
497599
uint256 maxLoss_,
498-
address[] calldata strategiesArray_
499-
) external view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
500-
CustodyInfo memory custody = custodyInfo[owner_];
600+
address[] memory strategiesArray_
601+
) public view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
602+
CustodyInfo storage custody = custodyInfo[owner_];
501603
if (block.timestamp < custody.unlockTime) {
502604
return 0;
503605
}
@@ -512,6 +614,35 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
512614
return Math.min(parentMax, custodyAssets);
513615
}
514616

617+
/**
618+
* @notice Returns maximum assets that owner can withdraw with custom loss tolerance
619+
* @dev Uses default queue for withdrawal strategies
620+
* @param owner_ Address that owns the shares
621+
* @param maxLoss_ Maximum acceptable loss in basis points (0-10000)
622+
* @return max Maximum withdrawable assets (constrained by custody)
623+
*/
624+
625+
function maxWithdraw(
626+
address owner_,
627+
uint256 maxLoss_
628+
) public view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
629+
return maxWithdraw(owner_, maxLoss_, new address[](0));
630+
}
631+
632+
/**
633+
* @notice Returns maximum assets that owner can withdraw with default parameters
634+
* @dev Overload to match Vyper's default parameters behavior (maxLoss = 0, default queue)
635+
* Enforces custody constraints - returns 0 if cooldown period not passed
636+
* @param owner_ Address that owns the shares
637+
* @return max Maximum withdrawable assets (constrained by custody)
638+
*/
639+
640+
function maxWithdraw(
641+
address owner_
642+
) external view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
643+
return maxWithdraw(owner_, 0, new address[](0));
644+
}
645+
515646
/**
516647
* @notice Get the maximum amount of shares that can be redeemed by an owner
517648
* @param owner_ Address owning shares to check redemption limits for
@@ -525,9 +656,9 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
525656
function maxRedeem(
526657
address owner_,
527658
uint256 maxLoss_,
528-
address[] calldata strategiesArray_
529-
) external view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
530-
CustodyInfo memory custody = custodyInfo[owner_];
659+
address[] memory strategiesArray_
660+
) public view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
661+
CustodyInfo storage custody = custodyInfo[owner_];
531662

532663
if (block.timestamp < custody.unlockTime) {
533664
return 0;
@@ -547,29 +678,29 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
547678
}
548679

549680
/**
550-
* @notice Get the amount of shares that can be transferred by a user
551-
* @param user Address to check transferable shares for
552-
* @return Amount of shares available for transfer (not locked in custody)
553-
* @dev Returns total balance minus shares currently locked in custody
681+
* @notice Returns maximum shares that owner can redeem with default parameters
682+
* @dev Overload to match Vyper's default parameters behavior (maxLoss = MAX_BPS, default queue)
683+
* Enforces custody constraints - returns 0 if cooldown period not passed
684+
* @param owner_ Address that owns the shares
685+
* @param maxLoss_ Maximum acceptable loss in basis points (0-10000)
686+
* @return max Maximum redeemable shares (constrained by custody)
554687
*/
555-
function getTransferableShares(address user) external view returns (uint256) {
556-
uint256 totalShares = balanceOf(user);
557-
uint256 lockedShares = custodyInfo[user].lockedShares;
558-
return totalShares - lockedShares;
688+
689+
function maxRedeem(
690+
address owner_,
691+
uint256 maxLoss_
692+
) public view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
693+
return maxRedeem(owner_, maxLoss_, new address[](0));
559694
}
560695

561696
/**
562-
* @notice Get the amount of shares available for rage quit initiation
563-
* @param user Address to check rage quitable shares for
564-
* @return Amount of shares available for initiating rage quit
565-
* @dev Returns 0 if user already has active custody, otherwise returns full balance
697+
* @notice Returns maximum shares that owner can redeem with default parameters
698+
* @dev Overload to match Vyper's default parameters behavior (maxLoss = MAX_BPS, default queue)
699+
* Enforces custody constraints - returns 0 if cooldown period not passed
700+
* @param owner_ Address that owns the shares
701+
* @return max Maximum redeemable shares (constrained by custody)
566702
*/
567-
function getRageQuitableShares(address user) external view returns (uint256) {
568-
// If user already has active custody, they cannot initiate new rage quit
569-
if (custodyInfo[user].lockedShares > 0) {
570-
return 0;
571-
}
572-
// Otherwise, they can rage quit all their shares
573-
return balanceOf(user);
703+
function maxRedeem(address owner_) public view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
704+
return maxRedeem(owner_, MAX_BPS, new address[](0));
574705
}
575706
}

0 commit comments

Comments
 (0)