Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save aviggiano/f5f22404e0bbd314fd217642b1deed2b to your computer and use it in GitHub Desktop.
Save aviggiano/f5f22404e0bbd314fd217642b1deed2b to your computer and use it in GitHub Desktop.
Test that liquidating a position does not leave the protocol with less collateral than minimum debt value
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import { Fixed256x18 } from "@tempusfinance/tempus-utils/contracts/math/Fixed256x18.sol";
import { PositionManager } from "../contracts/PositionManager.sol";
import { IERC20Indexable } from "../contracts/Interfaces/IERC20Indexable.sol";
import { IRToken } from "../contracts/Interfaces/IRToken.sol";
import { PriceFeedTestnet } from "./mocks/PriceFeedTestnet.sol";
import { TestSetup } from "./utils/TestSetup.t.sol";
import { FlashMintLiquidator } from "../contracts/FlashMintLiquidator.sol";
import { MockAMM } from "./mocks/MockAMM.sol";
contract PositionManagerLiquidationTotalCollateralCannotBeLowerThanMinCollateralTest is TestSetup {
using Fixed256x18 for uint256;
uint256 public constant DEFAULT_PRICE = 200 * 1e18;
PriceFeedTestnet public priceFeed;
PriceFeedTestnet public priceFeed2;
IRToken public rToken;
IERC20Indexable public raftDebtToken;
IERC20Indexable public raftDebtToken2;
MockAMM public mockAmm;
FlashMintLiquidator public liquidator;
function setUp() public override {
super.setUp();
priceFeed = new PriceFeedTestnet();
priceFeed2 = new PriceFeedTestnet();
rToken = positionManager.rToken();
positionManager.addCollateralToken(collateralToken, priceFeed, splitLiquidationCollateral);
positionManager.addCollateralToken(collateralToken2, priceFeed2, splitLiquidationCollateral2);
mockAmm = new MockAMM(collateralToken2, rToken, DEFAULT_PRICE);
liquidator = new FlashMintLiquidator(positionManager, mockAmm, collateralToken2);
(,raftDebtToken, ,,,,,,,) = positionManager.collateralInfo(collateralToken);
(,raftDebtToken2, ,,,,,,,) = positionManager.collateralInfo(collateralToken2);
collateralToken.mint(ALICE, 10e36);
collateralToken.mint(BOB, 10e36);
collateralToken.mint(CAROL, 10e36);
collateralToken.mint(DAVE, 10e36);
collateralToken.mint(EVE, 10e36);
collateralToken.mint(FRANK, 10e36);
collateralToken2.mint(ALICE, 10e36);
collateralToken2.mint(BOB, 10e36);
collateralToken2.mint(CAROL, 10e36);
collateralToken2.mint(DAVE, 10e36);
collateralToken2.mint(EVE, 10e36);
collateralToken2.mint(FRANK, 10e36);
priceFeed.setPrice(DEFAULT_PRICE);
}
event Debug(string name, uint256 value);
function testLiquidateCollateralWhenOnlyOnePositionActive() public {
// 1. Deposit high TVL on collateral 1
// 2. Deposit min on collateral 2
// 3. Deposit high on collateral 2
// 4. Liquidate high collateral 2
// 5. Deposit new high collateral 2
// 6. See what happens to you and them
// 1. Deposit high TVL on collateral 1
uint256 initialCR = 1.11e18;
uint256 rToMint = 55.6 * 1e6 * 1e18; // 55.6m TVL according to defillama
uint256 collateralAmount = rToMint.divUp(DEFAULT_PRICE).mulUp(initialCR);
vm.startPrank(ALICE);
collateralToken.approve(address(positionManager), collateralAmount);
positionManager.managePosition(
collateralToken,
ALICE,
collateralAmount,
true, // collateral increase
rToMint,
true, // debt increase
1e17,
emptySignature
);
emit Debug("b4 raftDebtToken.balanceOf(ALICE)", raftDebtToken.balanceOf(ALICE));
emit Debug("b4 rToken.balanceOf(ALICE)", rToken.balanceOf(ALICE));
// 2. Deposit min on collateral 2
vm.stopPrank();
vm.startPrank(BOB);
rToMint = splitLiquidationCollateral2.LOW_TOTAL_DEBT(); // mint minimum debt value (3000 R) using collateral 2
collateralAmount = rToMint.divUp(DEFAULT_PRICE).mulUp(initialCR); // assuming collateral 2 price == collateral 1 price
collateralToken2.approve(address(positionManager), collateralAmount);
positionManager.managePosition(
collateralToken2,
BOB,
collateralAmount,
true, // collateral increase
rToMint,
true, // debt increase
1e17,
emptySignature
);
emit Debug("b4 raftDebtToken2.balanceOf(BOB)", raftDebtToken2.balanceOf(BOB));
emit Debug("b4 rToken.balanceOf(BOB)", rToken.balanceOf(BOB));
vm.stopPrank();
// 3. Deposit high on collateral 2
// last position cannot be liquidated, so we need a new one
vm.startPrank(CAROL);
rToMint = 10_000 * splitLiquidationCollateral2.LOW_TOTAL_DEBT(); // whale
collateralAmount = rToMint.divUp(DEFAULT_PRICE).mulUp(initialCR); // assuming collateral 2 price == collateral 1 price
collateralToken2.approve(address(positionManager), collateralAmount);
positionManager.managePosition(
collateralToken2,
CAROL,
collateralAmount,
true, // collateral increase
rToMint,
true, // debt increase
1e17,
emptySignature
);
vm.stopPrank();
emit Debug("b4 raftDebtToken2.balanceOf(CAROL)", raftDebtToken2.balanceOf(CAROL));
emit Debug("b4 rToken.balanceOf(CAROL)", rToken.balanceOf(CAROL));
// 4. Liquidate high collateral 2
// price drops to 1ETH:198R, reducing CAROL's ICR between 100% and 110%
uint256 price = 198e18;
priceFeed2.setPrice(price);
vm.startPrank(ALICE);
positionManager.liquidate(CAROL); // liquidate whale, greatly reducing collateral2 from protocol
emit Debug("raftDebtToken.balanceOf(ALICE)", raftDebtToken.balanceOf(ALICE));
emit Debug("rToken.balanceOf(ALICE)", rToken.balanceOf(ALICE));
emit Debug("raftDebtToken2.balanceOf(BOB)", raftDebtToken2.balanceOf(BOB));
emit Debug("rToken.balanceOf(BOB)", rToken.balanceOf(BOB));
emit Debug("raftDebtToken2.balanceOf(CAROL)", raftDebtToken2.balanceOf(CAROL));
emit Debug("rToken.balanceOf(CAROL)", rToken.balanceOf(CAROL));
assertEq(raftDebtToken.balanceOf(CAROL), 0);
vm.stopPrank();
// 5. Deposit new high collateral 2
// another user tries to deposit again
rToMint = 400_000e18;
collateralAmount = rToMint.divUp(price).mulUp(initialCR);
vm.startPrank(DAVE);
collateralToken2.approve(address(positionManager), collateralAmount);
positionManager.managePosition(
collateralToken2,
DAVE,
collateralAmount,
true, // collateral increase
rToMint,
true, // debt increase
1e17,
emptySignature
);
emit Debug("raftDebtToken2.balanceOf(DAVE)", raftDebtToken2.balanceOf(DAVE));
emit Debug("rToken.balanceOf(DAVE)", rToken.balanceOf(DAVE));
// 6. See what happens to you and them
assertEq(raftDebtToken2.balanceOf(DAVE), rToken.balanceOf(DAVE));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment