Skip to content

Commit fc40f2a

Browse files
committed
fix: function overload in MSV
1 parent 1129987 commit fc40f2a

File tree

5 files changed

+526
-61
lines changed

5 files changed

+526
-61
lines changed

src/core/MultistrategyLockedVault.sol

Lines changed: 121 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -360,14 +360,62 @@ 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) returns (uint256) {
377+
return withdraw(assets, receiver, owner, 0, new address[](0));
378+
}
379+
380+
/**
381+
* @notice Withdraws assets from the vault with custody enforcement
382+
* @dev ERC4626-extended withdraw function with custody checks
383+
*
384+
* CUSTODY REQUIREMENTS:
385+
* - Owner must have initiated rage quit (active custody)
386+
* - Cooldown period must have passed
387+
* - Withdrawal amount cannot exceed custodied shares
388+
* - Multiple partial withdrawals allowed from same custody
389+
*
390+
* CONVERSION:
391+
* - Uses ROUND_UP when calculating shares (favors vault)
392+
* - shares = (assets * totalSupply + totalAssets - 1) / totalAssets
393+
*
394+
* BEHAVIOR:
395+
* - Validates custody status before processing withdrawal
396+
* - Updates custody by reducing locked shares
397+
* - Clears custody when all locked shares withdrawn
398+
* - Follows standard withdrawal flow after custody checks
399+
*
400+
* LOSS HANDLING:
401+
* - maxLoss_ = 0: Revert if any loss
402+
* - maxLoss_ = 10000: Accept any loss (100%)
403+
* - Users receive proportional share of unrealized losses
404+
*
405+
* @param assets Amount of assets to withdraw
406+
* @param receiver Address to receive the withdrawn assets
407+
* @param owner Address whose shares will be burned
408+
* @param maxLoss Maximum acceptable loss in basis points (0-10000)
409+
* @param strategiesArray Optional custom withdrawal queue (empty = use default)
410+
* @return shares Amount of shares actually burned from owner
411+
* @custom:security Reentrancy protected, requires active custody
364412
*/
365413
function withdraw(
366414
uint256 assets,
367415
address receiver,
368416
address owner,
369417
uint256 maxLoss,
370-
address[] calldata strategiesArray
418+
address[] memory strategiesArray
371419
) public override(MultistrategyVault, IMultistrategyVault) nonReentrant returns (uint256) {
372420
uint256 shares = _convertToShares(assets, Rounding.ROUND_UP);
373421
_processCustodyWithdrawal(owner, shares);
@@ -376,14 +424,63 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
376424
}
377425

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

471568
if (custody.lockedShares > 0) {
472569
uint256 senderBalance = balanceOf(sender_);
@@ -495,9 +592,9 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
495592
function maxWithdraw(
496593
address owner_,
497594
uint256 maxLoss_,
498-
address[] calldata strategiesArray_
499-
) external view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
500-
CustodyInfo memory custody = custodyInfo[owner_];
595+
address[] memory strategiesArray_
596+
) public view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
597+
CustodyInfo storage custody = custodyInfo[owner_];
501598
if (block.timestamp < custody.unlockTime) {
502599
return 0;
503600
}
@@ -512,6 +609,12 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
512609
return Math.min(parentMax, custodyAssets);
513610
}
514611

612+
function maxWithdraw(
613+
address owner_
614+
) external view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
615+
return maxWithdraw(owner_, MAX_BPS, new address[](0));
616+
}
617+
515618
/**
516619
* @notice Get the maximum amount of shares that can be redeemed by an owner
517620
* @param owner_ Address owning shares to check redemption limits for
@@ -525,9 +628,9 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
525628
function maxRedeem(
526629
address owner_,
527630
uint256 maxLoss_,
528-
address[] calldata strategiesArray_
529-
) external view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
530-
CustodyInfo memory custody = custodyInfo[owner_];
631+
address[] memory strategiesArray_
632+
) public view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
633+
CustodyInfo storage custody = custodyInfo[owner_];
531634

532635
if (block.timestamp < custody.unlockTime) {
533636
return 0;
@@ -547,29 +650,13 @@ contract MultistrategyLockedVault is MultistrategyVault, IMultistrategyLockedVau
547650
}
548651

549652
/**
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
653+
* @notice Returns maximum shares that owner can redeem with default parameters
654+
* @dev Overload to match Vyper's default parameters behavior (maxLoss = MAX_BPS, default queue)
655+
* Enforces custody constraints - returns 0 if cooldown period not passed
656+
* @param owner_ Address that owns the shares
657+
* @return max Maximum redeemable shares (constrained by custody)
554658
*/
555-
function getTransferableShares(address user) external view returns (uint256) {
556-
uint256 totalShares = balanceOf(user);
557-
uint256 lockedShares = custodyInfo[user].lockedShares;
558-
return totalShares - lockedShares;
559-
}
560-
561-
/**
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
566-
*/
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);
659+
function maxRedeem(address owner_) public view override(MultistrategyVault, IMultistrategyVault) returns (uint256) {
660+
return maxRedeem(owner_, MAX_BPS, new address[](0));
574661
}
575662
}

0 commit comments

Comments
 (0)