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

Feat/deposit-max-balance-savings #26

Merged
merged 11 commits into from
Aug 9, 2024
3 changes: 3 additions & 0 deletions contracts/BaseRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral {
data[i],
(IERC20, IERC4626, uint256, address, uint256)
);
if (amount == type(uint256).max) amount = token.balanceOf(address(this));
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
_changeAllowance(token, address(savingsRate), type(uint256).max);
_deposit4626(savingsRate, amount, to, minSharesOut);
} else if (actions[i] == ActionType.deposit4626Referral) {
Expand All @@ -269,13 +270,15 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral {
uint256 minSharesOut,
address referrer
) = abi.decode(data[i], (IERC20, IERC4626, uint256, address, uint256, address));
if (amount == type(uint256).max) amount = token.balanceOf(address(this));
_changeAllowance(token, address(savingsRate), type(uint256).max);
_deposit4626Referral(savingsRate, amount, to, minSharesOut, referrer);
} else if (actions[i] == ActionType.redeem4626) {
(IERC4626 savingsRate, uint256 shares, address to, uint256 minAmountOut) = abi.decode(
data[i],
(IERC4626, uint256, address, uint256)
);
if (shares == type(uint256).max) shares = savingsRate.balanceOf(msg.sender);
_redeem4626(savingsRate, shares, to, minAmountOut);
} else if (actions[i] == ActionType.withdraw4626) {
(IERC4626 savingsRate, uint256 amount, address to, uint256 maxSharesOut) = abi.decode(
Expand Down
172 changes: 171 additions & 1 deletion test/foundry/AngeRouterMainnet.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ contract AngleRouterMainnetTest is BaseTest {
assertEq(token.balanceOf(address(to)), 0);
}

function testMint4626ForgotFunds(uint256 initShares, uint256 shares, uint256 maxAmount, uint256 gainOrLoss) public {
function testMint4626ForgotFunds(
uint256 initShares,
uint256 shares,
uint256 maxAmount,
uint256 gainOrLoss
) public {
address to = address(router);
uint256 balanceUsers = BASE_TOKENS * 1 ether;
deal(address(token), address(_alice), balanceUsers);
Expand Down Expand Up @@ -189,6 +194,52 @@ contract AngleRouterMainnetTest is BaseTest {
assertEq(token.balanceOf(address(to)), 0);
}

function testDeposit4626MaxBalance(
uint256 initShares,
uint256 amount,
uint256 minSharesOut,
uint256 gainOrLoss
) public {
address to = address(router);

uint256 balanceUsers = BASE_TOKENS * 1 ether;
deal(address(token), address(_alice), balanceUsers);

_randomizeSavingsRate(gainOrLoss, initShares);

amount = bound(amount, 0, balanceUsers);
uint256 previewDeposit = savingsRate.previewDeposit(amount);

PermitType[] memory paramsPermit = new PermitType[](0);
ActionType[] memory actionType = new ActionType[](2);
bytes[] memory data = new bytes[](2);

actionType[0] = ActionType.transfer;
data[0] = abi.encode(token, router, amount);
actionType[1] = ActionType.deposit4626;
data[1] = abi.encode(token, savingsRate, type(uint256).max, to, minSharesOut);

uint256 mintedShares = savingsRate.convertToShares(amount);

vm.startPrank(_alice);
token.approve(address(router), type(uint256).max);
// as this is a mock vault, previewMint is exactly what is needed to mint
if (previewDeposit < minSharesOut) {
vm.expectRevert(BaseRouter.TooSmallAmountOut.selector);
router.mixer(paramsPermit, actionType, data);
return;
} else {
router.mixer(paramsPermit, actionType, data);
}
vm.stopPrank();

assertEq(savingsRate.balanceOf(address(to)), previewDeposit);
assertEq(savingsRate.balanceOf(address(to)), mintedShares);

assertEq(token.balanceOf(address(router)), 0);
assertEq(token.balanceOf(address(_alice)), balanceUsers - amount);
}

0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
function testDeposit4626ForgotFunds(
uint256 initShares,
uint256 amount,
Expand Down Expand Up @@ -232,6 +283,53 @@ contract AngleRouterMainnetTest is BaseTest {
assertEq(token.balanceOf(address(_alice)), balanceUsers - amount);
}

function testDepositReferral4626MaxBalance(
uint256 initShares,
uint256 amount,
uint256 minSharesOut,
uint256 gainOrLoss,
address referrer
) public {
address to = address(router);

uint256 balanceUsers = BASE_TOKENS * 1 ether;
deal(address(token), address(_alice), balanceUsers);

_randomizeSavingsRate(gainOrLoss, initShares);

amount = bound(amount, 0, balanceUsers);
uint256 previewDeposit = savingsRate.previewDeposit(amount);

PermitType[] memory paramsPermit = new PermitType[](0);
ActionType[] memory actionType = new ActionType[](2);
bytes[] memory data = new bytes[](2);

actionType[0] = ActionType.transfer;
data[0] = abi.encode(token, router, amount);
actionType[1] = ActionType.deposit4626Referral;
data[1] = abi.encode(token, savingsRate, type(uint256).max, to, minSharesOut, referrer);

uint256 mintedShares = savingsRate.convertToShares(amount);

vm.startPrank(_alice);
token.approve(address(router), type(uint256).max);
// as this is a mock vault, previewMint is exactly what is needed to mint
if (previewDeposit < minSharesOut) {
vm.expectRevert(BaseRouter.TooSmallAmountOut.selector);
router.mixer(paramsPermit, actionType, data);
return;
} else {
router.mixer(paramsPermit, actionType, data);
}
vm.stopPrank();

assertEq(savingsRate.balanceOf(address(to)), previewDeposit);
assertEq(savingsRate.balanceOf(address(to)), mintedShares);

assertEq(token.balanceOf(address(router)), 0);
assertEq(token.balanceOf(address(_alice)), balanceUsers - amount);
}

function testRedeem4626GoodPractice(
uint256 initShares,
uint256 aliceAmount,
Expand Down Expand Up @@ -387,6 +485,78 @@ contract AngleRouterMainnetTest is BaseTest {
assertEq(token.balanceOf(address(_alice)), balanceUsers - aliceAmount);
}

function testRedeem4626MaxBalance(
uint256 initShares,
uint256 aliceAmount,
uint256 minAmount,
uint256 gainOrLoss,
uint256 gainOrLoss2
) public {
uint256 balanceUsers = BASE_TOKENS * 1 ether;
deal(address(token), address(_alice), balanceUsers);

_randomizeSavingsRate(gainOrLoss, initShares);

aliceAmount = bound(aliceAmount, 0, balanceUsers);
uint256 previewDeposit = savingsRate.previewDeposit(aliceAmount);
// otherwise there could be overflows
vm.assume(previewDeposit < type(uint256).max / BASE_PARAMS);

uint256 previewRedeem;
{
// do a first deposit
PermitType[] memory paramsPermit = new PermitType[](0);
ActionType[] memory actionType = new ActionType[](2);
bytes[] memory data = new bytes[](2);

actionType[0] = ActionType.transfer;
data[0] = abi.encode(token, router, aliceAmount);
actionType[1] = ActionType.deposit4626;
data[1] = abi.encode(token, savingsRate, aliceAmount, _alice, previewDeposit);

vm.startPrank(_alice);
token.approve(address(router), type(uint256).max);
router.mixer(paramsPermit, actionType, data);
vm.stopPrank();

assertEq(savingsRate.balanceOf(address(router)), 0);
assertEq(savingsRate.balanceOf(address(_alice)), previewDeposit);
assertEq(token.balanceOf(address(router)), 0);
assertEq(token.balanceOf(address(_alice)), balanceUsers - aliceAmount);

// make the savings rate have a loss / gain
gainOrLoss2 = bound(gainOrLoss2, 1, 1 ether * 1 ether);
deal(address(token), address(savingsRate), gainOrLoss2);

// then redeem
uint256 sharesToBurn = savingsRate.balanceOf(_alice);

actionType = new ActionType[](1);
data = new bytes[](1);

actionType[0] = ActionType.redeem4626;
data[0] = abi.encode(savingsRate, type(uint256).max, address(router), minAmount);

previewRedeem = savingsRate.previewRedeem(sharesToBurn);
vm.startPrank(_alice);
savingsRate.approve(address(router), type(uint256).max);
// as this is a mock vault, previewRedeem is exactly what should be received
if (previewRedeem < minAmount) {
vm.expectRevert(BaseRouter.TooSmallAmountOut.selector);
router.mixer(paramsPermit, actionType, data);
return;
} else {
router.mixer(paramsPermit, actionType, data);
}
vm.stopPrank();
assertEq(savingsRate.balanceOf(address(_alice)), previewDeposit - sharesToBurn);
}

assertEq(savingsRate.balanceOf(address(router)), 0);
assertEq(token.balanceOf(address(router)), previewRedeem);
assertEq(token.balanceOf(address(_alice)), balanceUsers - aliceAmount);
}

function testWithdraw4626GoodPractice(
uint256 initShares,
uint256 aliceAmount,
Expand Down
Loading