Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: match vault abi #100

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions src/TokenizedStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,17 @@ contract TokenizedStrategy {
return _maxWithdraw(_strategyStorage(), owner);
}

/**
* @notice Accepts a `maxLoss` variable in order to match the multi
* strategy vaults ABI.
*/
function maxWithdraw(
address owner,
fp-crypto marked this conversation as resolved.
Show resolved Hide resolved
uint256 /*maxLoss*/
) external view returns (uint256) {
return _maxWithdraw(_strategyStorage(), owner);
}

/**
* @notice Total number of strategy shares that can be
* redeemed from the strategy by `owner`, where `owner`
Expand All @@ -792,6 +803,17 @@ contract TokenizedStrategy {
return _maxRedeem(_strategyStorage(), owner);
}

/**
* @notice Accepts a `maxLoss` variable in order to match the multi
* strategy vaults ABI.
*/
function maxRedeem(
address owner,
uint256 /*maxLoss*/
) external view returns (uint256) {
return _maxRedeem(_strategyStorage(), owner);
}

/*//////////////////////////////////////////////////////////////
INTERNAL 4626 VIEW METHODS
//////////////////////////////////////////////////////////////*/
Expand Down
10 changes: 10 additions & 0 deletions src/interfaces/ITokenizedStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ interface ITokenizedStrategy is IERC4626, IERC20Permit {
uint256 maxLoss
) external returns (uint256);

function maxWithdraw(
address owner,
uint256 /*maxLoss*/
) external view returns (uint256);

function maxRedeem(
address owner,
uint256 /*maxLoss*/
) external view returns (uint256);

/*//////////////////////////////////////////////////////////////
MODIFIER HELPERS
//////////////////////////////////////////////////////////////*/
Expand Down
97 changes: 97 additions & 0 deletions src/test/CustomImplementation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,103 @@ contract CustomImplementationsTest is Setup {
);
}

function test_customWithdrawLimit_maxLossVariable(
address _address,
uint256 _amount,
uint16 _profitFactor
) public {
_amount = bound(_amount, minFuzzAmount, maxFuzzAmount);
_profitFactor = uint16(bound(uint256(_profitFactor), 10, MAX_BPS));

uint256 profit = (_amount * _profitFactor) / MAX_BPS;

strategy = IMockStrategy(setUpIlliquidStrategy());

vm.assume(
_address != address(0) &&
_address != address(strategy) &&
_address != address(yieldSource)
);

setFees(0, 0);

mintAndDepositIntoStrategy(strategy, _address, _amount);

uint256 idle = asset.balanceOf(address(strategy));
assertGt(idle, 0);

// Assure we have a withdraw limit
assertEq(strategy.availableWithdrawLimit(_address), idle);
assertGt(strategy.totalAssets(), idle);

// Make sure max withdraw and redeem return the correct amounts
assertEq(strategy.maxWithdraw(_address, 9), idle);
assertEq(
strategy.maxRedeem(_address, 0),
strategy.convertToShares(idle)
);
assertLe(
strategy.convertToAssets(strategy.maxRedeem(_address)),
strategy.availableWithdrawLimit(_address)
);

vm.expectRevert("ERC4626: redeem more than max");
vm.prank(_address);
strategy.redeem(_amount, _address, _address);

vm.expectRevert("ERC4626: withdraw more than max");
vm.prank(_address);
strategy.withdraw(_amount, _address, _address);

createAndCheckProfit(strategy, profit, 0, 0);

increaseTimeAndCheckBuffer(strategy, 5 days, profit / 2);

idle = asset.balanceOf(address(strategy));
assertGt(idle, 0);

// Assure we have a withdraw limit
assertEq(strategy.availableWithdrawLimit(_address), idle);
assertGt(strategy.totalAssets(), idle);

// Make sure max withdraw and redeem return the correct amounts
assertEq(strategy.maxWithdraw(_address, 69), idle);
assertEq(
strategy.maxRedeem(_address, 2 ** 256 - 1),
strategy.convertToShares(idle)
);
assertLe(
strategy.convertToAssets(strategy.maxRedeem(_address)),
strategy.availableWithdrawLimit(_address)
);

vm.expectRevert("ERC4626: redeem more than max");
vm.prank(_address);
strategy.redeem(_amount, _address, _address);

vm.expectRevert("ERC4626: withdraw more than max");
vm.prank(_address);
strategy.withdraw(_amount, _address, _address);

uint256 before = asset.balanceOf(_address);
uint256 redeem = strategy.maxRedeem(_address, _amount);
uint256 conversion = strategy.convertToAssets(_amount);

vm.prank(_address);
strategy.redeem(redeem, _address, _address, 0);

// We need to give 2 wei rounding buffer
assertApproxEq(strategy.convertToAssets(_amount), conversion, 2);
assertApproxEq(asset.balanceOf(_address) - before, idle, 2);
assertApproxEq(strategy.availableWithdrawLimit(_address), 0, 2);
assertApproxEq(strategy.maxWithdraw(_address, 99), 0, 2);
assertApproxEq(strategy.maxRedeem(_address, 0), 0, 2);
assertLe(
strategy.convertToAssets(strategy.maxRedeem(_address, _amount)),
strategy.availableWithdrawLimit(_address)
);
}

function test_customDepositLimit(
address _allowed,
address _notAllowed,
Expand Down
Loading