mirror of
https://github.com/Instadapp/fluid-contracts-public.git
synced 2024-07-29 21:57:37 +00:00
174 lines
6.8 KiB
Solidity
174 lines
6.8 KiB
Solidity
// SPDX-License-Identifier: AGPL-3.0
|
|
pragma solidity >=0.8.0 <0.9.0;
|
|
|
|
import "forge-std/Test.sol";
|
|
import { FixedPointMathLib } from "solmate/src/utils/FixedPointMathLib.sol";
|
|
import { MockERC20 } from "solmate/src/test/utils/mocks/MockERC20.sol";
|
|
|
|
import { LiquidityCalcs } from "../../../../contracts/libraries/liquidityCalcs.sol";
|
|
import { IFluidLendingRewardsRateModel } from "../../../../contracts/protocols/lending/interfaces/iLendingRewardsRateModel.sol";
|
|
import { IFToken } from "../../../../contracts/protocols/lending/interfaces/iFToken.sol";
|
|
import { Events } from "../../../../contracts/protocols/lending/fToken/events.sol";
|
|
|
|
contract Handler is Test, Events {
|
|
using FixedPointMathLib for uint256;
|
|
|
|
IFToken token;
|
|
MockERC20 underlying;
|
|
|
|
address[] public actors;
|
|
address public admin;
|
|
address public rateModel;
|
|
|
|
address internal currentActor;
|
|
|
|
modifier useActor(uint256 actorIndexSeed) {
|
|
currentActor = actors[bound(actorIndexSeed, 0, actors.length - 1)];
|
|
vm.startPrank(currentActor);
|
|
_;
|
|
vm.stopPrank();
|
|
}
|
|
|
|
modifier useAdminActor() {
|
|
vm.startPrank(admin);
|
|
_;
|
|
vm.stopPrank();
|
|
}
|
|
|
|
constructor(IFToken token_, address underlying_, address[] memory actors_, address admin_) {
|
|
token = token_;
|
|
underlying = MockERC20(underlying_);
|
|
actors = actors_;
|
|
admin = admin_;
|
|
}
|
|
|
|
function deposit(
|
|
uint256 assets,
|
|
address receiver,
|
|
uint256 actorIndexSeed
|
|
) external useActor(actorIndexSeed) returns (uint256 shares) {
|
|
assets = bound(assets, 1e4, 1e18);
|
|
|
|
underlying.mint(currentActor, assets);
|
|
underlying.approve(address(token), type(uint256).max);
|
|
|
|
(, , , , , , , , uint256 currentTokenExchangePrice) = IFToken(address(token)).getData();
|
|
// solidity rounds down by default
|
|
uint256 expectedShares = (assets * LiquidityCalcs.EXCHANGE_PRICES_PRECISION) / currentTokenExchangePrice;
|
|
|
|
uint256 assetsBefore = underlying.balanceOf(currentActor);
|
|
uint256 sharesBefore = token.balanceOf(currentActor);
|
|
token.deposit(assets, currentActor);
|
|
uint256 assetsAfter = underlying.balanceOf(currentActor);
|
|
uint256 sharesAfter = token.balanceOf(currentActor);
|
|
|
|
// equal because expectedAssetsReceived is already rounded down as default in Solidity
|
|
assertEq(sharesAfter - sharesBefore, expectedShares);
|
|
assertEq(assetsBefore - assetsAfter, assets);
|
|
}
|
|
|
|
function mint(uint256 shares, uint256 actorIndexSeed) external useActor(actorIndexSeed) {
|
|
shares = bound(shares, 1e4, 1e18);
|
|
|
|
uint256 consumeAssets = token.previewMint(shares);
|
|
|
|
underlying.mint(currentActor, consumeAssets);
|
|
underlying.approve(address(token), type(uint256).max);
|
|
|
|
(, , , , , , , , uint256 currentTokenExchangePrice) = IFToken(address(token)).getData();
|
|
|
|
uint256 expectedAssets = (shares * currentTokenExchangePrice) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION;
|
|
|
|
uint256 assetsBefore = underlying.balanceOf(currentActor);
|
|
uint256 sharesBefore = token.balanceOf(currentActor);
|
|
token.mint(shares, currentActor);
|
|
uint256 assetsAfter = underlying.balanceOf(currentActor);
|
|
uint256 sharesAfter = token.balanceOf(currentActor);
|
|
|
|
assertEq(sharesAfter - sharesBefore, shares);
|
|
assertTrue(assetsBefore - assetsAfter >= expectedAssets);
|
|
}
|
|
|
|
function withdraw(
|
|
uint256 assets,
|
|
address receiver,
|
|
address owner,
|
|
uint256 actorIndexSeed
|
|
) external useActor(actorIndexSeed) {
|
|
assets = bound(assets, 1e4, 1e18);
|
|
underlying.mint(currentActor, assets);
|
|
underlying.approve(address(token), type(uint256).max);
|
|
|
|
(, , , , , , , , uint256 currentTokenExchangePrice) = IFToken(address(token)).getData();
|
|
|
|
uint256 sharesToBurn_ = assets.mulDivUp(LiquidityCalcs.EXCHANGE_PRICES_PRECISION, currentTokenExchangePrice);
|
|
|
|
if (token.balanceOf(currentActor) < sharesToBurn_) return;
|
|
|
|
uint256 expectedBurnedShares = (assets * LiquidityCalcs.EXCHANGE_PRICES_PRECISION) / currentTokenExchangePrice;
|
|
|
|
uint256 assetsBefore = underlying.balanceOf(currentActor);
|
|
uint256 sharesBefore = token.balanceOf(currentActor);
|
|
token.withdraw(assets, currentActor, currentActor);
|
|
uint256 assetsAfter = underlying.balanceOf(currentActor);
|
|
uint256 sharesAfter = token.balanceOf(currentActor);
|
|
|
|
// assert that the actual burned shares are greater than or equal to the expected amount, confirming rounding up
|
|
assertTrue(sharesBefore - sharesAfter >= expectedBurnedShares);
|
|
assertEq(assetsAfter - assetsBefore, assets);
|
|
}
|
|
|
|
function redeem(
|
|
uint256 shares,
|
|
address receiver,
|
|
address owner,
|
|
uint256 actorIndexSeed
|
|
) external useActor(actorIndexSeed) {
|
|
shares = bound(shares, 1e4, 1e18);
|
|
uint256 sharesBalance = token.balanceOf(currentActor);
|
|
|
|
if (shares > sharesBalance) {
|
|
shares = sharesBalance;
|
|
}
|
|
|
|
if (token.previewRedeem(sharesBalance) == 0) return;
|
|
|
|
(, , , , , , , , uint256 currentTokenExchangePrice) = IFToken(address(token)).getData();
|
|
|
|
uint256 assetsBefore = underlying.balanceOf(currentActor);
|
|
uint256 sharesBefore = token.balanceOf(currentActor);
|
|
token.redeem(shares, currentActor, currentActor);
|
|
uint256 assetsAfter = underlying.balanceOf(currentActor);
|
|
uint256 sharesAfter = token.balanceOf(currentActor);
|
|
|
|
uint256 expectedAssetsReceived = (shares * currentTokenExchangePrice) /
|
|
LiquidityCalcs.EXCHANGE_PRICES_PRECISION;
|
|
|
|
// equal because expectedAssetsReceived is already rounded down as default in Solidity
|
|
assertEq(assetsAfter - assetsBefore, expectedAssetsReceived);
|
|
// the combination of rounding down in previewRedeem and rounding up in executeWithdraw does not lead to a situation where the amount of burned shares is higher than the input shares.
|
|
assertEq(sharesBefore - sharesAfter, shares);
|
|
}
|
|
|
|
function updateRewards() external useAdminActor {
|
|
//NOTE: function changes rateModel address to zero address in order to get the lowest rewards in this case 0 .
|
|
vm.expectEmit(true, true, true, true);
|
|
emit LogUpdateRewards(IFluidLendingRewardsRateModel(address(0)));
|
|
token.updateRewards(IFluidLendingRewardsRateModel(address(0)));
|
|
}
|
|
|
|
function initialDeposit(uint256 assets_) external useAdminActor {
|
|
assets_ = bound(assets_, 1e4, 2e18);
|
|
underlying.mint(admin, assets_);
|
|
underlying.approve(address(token), type(uint256).max);
|
|
|
|
token.deposit(assets_, admin);
|
|
}
|
|
|
|
function updateRates() public {
|
|
vm.expectEmit(true, false, false, false);
|
|
emit LogUpdateRates(0, 0);
|
|
token.updateRates();
|
|
}
|
|
}
|