Skip to content

Commit

Permalink
test: fix liquidation test
Browse files Browse the repository at this point in the history
  • Loading branch information
MerlinEgalite committed Oct 15, 2021
1 parent b060563 commit e428e50
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 12 deletions.
17 changes: 16 additions & 1 deletion contracts/interfaces/compound/ICompound.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ interface ICErc20 {

function repayBorrow(uint256) external returns (uint256);

function underlying() external returns (address);
function underlying() external view returns (address);
}

interface ICEth {
Expand Down Expand Up @@ -224,6 +224,21 @@ interface IComptroller {
uint256,
uint256
);

function getHypotheticalAccountLiquidity(
address,
address,
uint256,
uint256
)
external
returns (
uint256,
uint256,
uint256
);

function checkMembership(address, address) external view returns (bool);
}

interface IInterestRateModel {
Expand Down
22 changes: 22 additions & 0 deletions contracts/test/SimplePriceOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.7;

import {ICErc20, ICToken} from "../interfaces/compound/ICompound.sol";

/// Price Oracle for liquidation tests
contract SimplePriceOracle {
mapping(address => uint256) public prices;

function getUnderlyingPrice(ICToken _cToken) public view returns (uint256) {
return prices[address(ICErc20(address(_cToken)).underlying())];
}

function setUnderlyingPrice(ICToken _cToken, uint256 _underlyingPriceMantissa) public {
address asset = address(ICErc20(address(_cToken)).underlying());
prices[asset] = _underlyingPriceMantissa;
}

function setDirectPrice(address _asset, uint256 _price) public {
prices[_asset] = _price;
}
}
71 changes: 60 additions & 11 deletions test/cream-polygon-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -785,15 +785,27 @@ describe('MorphoPositionsManagerForCream Contract', () => {
});
});

xdescribe('Test liquidation', () => {
it('Borrower should be liquidated while supply (collateral) is only on Compound', async () => {
describe('Test liquidation', () => {
it('Borrower should be liquidated while supply (collateral) is only on Cream', async () => {
// Deploy custom price oracle
const PriceOracle = await ethers.getContractFactory('SimplePriceOracle');
priceOracle = await PriceOracle.deploy();
await priceOracle.deployed();

// Install admin user
const adminAddress = await comptroller.admin();
await hre.network.provider.send('hardhat_impersonateAccount', [adminAddress]);
await hre.network.provider.send('hardhat_setBalance', [adminAddress, ethers.utils.parseEther('10').toHexString()]);
const admin = await ethers.getSigner(adminAddress);

// Deposit
const amount = to6Decimals(utils.parseUnits('100'));
await usdcToken.connect(borrower1).approve(morphoPositionsManagerForCream.address, amount);
await morphoPositionsManagerForCream.connect(borrower1).supply(config.tokens.cUsdc.address, amount);
const collateralBalanceInCToken = (await morphoPositionsManagerForCream.supplyBalanceInOf(config.tokens.cUsdc.address, borrower1.getAddress())).onCream;
const cExchangeRate = await cUsdcToken.callStatic.exchangeRateCurrent();
const collateralBalanceInUnderlying = cTokenToUnderlying(collateralBalanceInCToken, cExchangeRate);
const { collateralFactorMantissa } = await comptroller.markets(config.tokens.cDai.address);
const { collateralFactorMantissa } = await comptroller.markets(config.tokens.cUsdc.address);
const usdcPriceMantissa = await compoundOracle.getUnderlyingPrice(config.tokens.cUsdc.address);
const daiPriceMantissa = await compoundOracle.getUnderlyingPrice(config.tokens.cDai.address);
const maxToBorrow = collateralBalanceInUnderlying.mul(usdcPriceMantissa).div(daiPriceMantissa).mul(collateralFactorMantissa).div(SCALE);
Expand All @@ -803,6 +815,19 @@ describe('MorphoPositionsManagerForCream Contract', () => {
const collateralBalanceBefore = (await morphoPositionsManagerForCream.supplyBalanceInOf(config.tokens.cUsdc.address, borrower1.getAddress())).onCream;
const borrowBalanceBefore = (await morphoPositionsManagerForCream.borrowBalanceInOf(config.tokens.cDai.address, borrower1.getAddress())).onCream;

// Set price oracle
await comptroller.connect(admin)._setPriceOracle(priceOracle.address);
priceOracle.setUnderlyingPrice(config.tokens.cDai.address, BigNumber.from('1010182920000000000'));
priceOracle.setUnderlyingPrice(config.tokens.cUsdc.address, BigNumber.from('1000000000000000000000000000000'));
priceOracle.setUnderlyingPrice(config.tokens.cUni.address, BigNumber.from('1000000000000000000000000000000'));
priceOracle.setUnderlyingPrice(config.tokens.cUsdt.address, BigNumber.from('1000000000000000000000000000000'));

// Force creamOracle update by setting comptroller again (but with the custom price oracle)
await hre.network.provider.send('hardhat_impersonateAccount', [morphoMarketsManagerForCompLike.address]);
await hre.network.provider.send('hardhat_setBalance', [morphoMarketsManagerForCompLike.address, ethers.utils.parseEther('10').toHexString()]);
const morphoMarketsManagerUser = await ethers.getSigner(morphoMarketsManagerForCompLike.address);
await morphoPositionsManagerForCream.connect(morphoMarketsManagerUser).setComptroller(comptroller.address);

// Mine block
await hre.network.provider.send('evm_mine', []);

Expand All @@ -819,8 +844,8 @@ describe('MorphoPositionsManagerForCream Contract', () => {
const borrowIndex = await cDaiToken.borrowIndex();
const cUsdcExchangeRate = await cUsdcToken.callStatic.exchangeRateCurrent();
const liquidationIncentive = await comptroller.liquidationIncentiveMantissa();
const collateralAssetPrice = await compoundOracle.getUnderlyingPrice(config.tokens.cUsdc.address);
const borrowedAssetPrice = await compoundOracle.getUnderlyingPrice(config.tokens.cDai.address);
const collateralAssetPrice = await priceOracle.getUnderlyingPrice(config.tokens.cUsdc.address);
const borrowedAssetPrice = await priceOracle.getUnderlyingPrice(config.tokens.cDai.address);
const amountToSeize = toRepay.mul(borrowedAssetPrice).div(collateralAssetPrice).mul(liquidationIncentive).div(SCALE);
const expectedCollateralBalanceAfter = collateralBalanceBefore.sub(underlyingToCToken(amountToSeize, cUsdcExchangeRate));
const expectedBorrowBalanceAfter = borrowBalanceBefore.sub(underlyingToCdUnit(toRepay, borrowIndex));
Expand All @@ -837,6 +862,17 @@ describe('MorphoPositionsManagerForCream Contract', () => {
});

it('Borrower should be liquidated while supply (collateral) is on Compound and in peer-to-peer', async () => {
// Deploy custom price oracle
const PriceOracle = await ethers.getContractFactory('SimplePriceOracle');
priceOracle = await PriceOracle.deploy();
await priceOracle.deployed();

// Install admin user
const adminAddress = await comptroller.admin();
await hre.network.provider.send('hardhat_impersonateAccount', [adminAddress]);
await hre.network.provider.send('hardhat_setBalance', [adminAddress, ethers.utils.parseEther('10').toHexString()]);
const admin = await ethers.getSigner(adminAddress);

await daiToken.connect(supplier1).approve(morphoPositionsManagerForCream.address, utils.parseUnits('1000'));
await morphoPositionsManagerForCream.connect(supplier1).supply(config.tokens.cDai.address, utils.parseUnits('1000'));

Expand All @@ -845,10 +881,10 @@ describe('MorphoPositionsManagerForCream Contract', () => {
await usdcToken.connect(borrower1).approve(morphoPositionsManagerForCream.address, amount);
await morphoPositionsManagerForCream.connect(borrower1).supply(config.tokens.cUsdc.address, amount);

// borrower2 borrows part of supply of borrower1 -> borrower1 has supply in peer-to-peer and on Compound
// borrower2 borrows part of supply of borrower1 -> borrower1 has supply in peer-to-peer and on Cream
const toBorrow = amount;
await uniToken.connect(borrower2).approve(morphoPositionsManagerForCream.address, utils.parseUnits('200'));
await morphoPositionsManagerForCream.connect(borrower2).supply(config.tokens.cUni.address, utils.parseUnits('200'));
await uniToken.connect(borrower2).approve(morphoPositionsManagerForCream.address, utils.parseUnits('50'));
await morphoPositionsManagerForCream.connect(borrower2).supply(config.tokens.cUni.address, utils.parseUnits('50'));
await morphoPositionsManagerForCream.connect(borrower2).borrow(config.tokens.cUsdc.address, toBorrow);

// borrower1 borrows DAI
Expand All @@ -859,7 +895,7 @@ describe('MorphoPositionsManagerForCream Contract', () => {
const supplyBalanceOnCompInUnderlying = cTokenToUnderlying(supplyBalanceOnComp1, cUsdcExchangeRate1);
const supplyBalanceMorphoInUnderlying = mUnitToUnderlying(supplyBalanceInP2P1, mUsdcExchangeRate1);
const supplyBalanceInUnderlying = supplyBalanceOnCompInUnderlying.add(supplyBalanceMorphoInUnderlying);
const { collateralFactorMantissa } = await comptroller.markets(config.tokens.cDai.address);
const { collateralFactorMantissa } = await comptroller.markets(config.tokens.cUsdc.address);
const usdcPriceMantissa = await compoundOracle.getUnderlyingPrice(config.tokens.cUsdc.address);
const daiPriceMantissa = await compoundOracle.getUnderlyingPrice(config.tokens.cDai.address);
const maxToBorrow = supplyBalanceInUnderlying.mul(usdcPriceMantissa).div(daiPriceMantissa).mul(collateralFactorMantissa).div(SCALE);
Expand All @@ -868,6 +904,19 @@ describe('MorphoPositionsManagerForCream Contract', () => {
const collateralBalanceInP2PBefore = (await morphoPositionsManagerForCream.supplyBalanceInOf(config.tokens.cUsdc.address, borrower1.getAddress())).inP2P;
const borrowBalanceInP2PBefore = (await morphoPositionsManagerForCream.borrowBalanceInOf(config.tokens.cDai.address, borrower1.getAddress())).inP2P;

// Set price oracle
await comptroller.connect(admin)._setPriceOracle(priceOracle.address);
priceOracle.setUnderlyingPrice(config.tokens.cDai.address, BigNumber.from('1020182920000000000'));
priceOracle.setUnderlyingPrice(config.tokens.cUsdc.address, BigNumber.from('1000000000000000000000000000000'));
priceOracle.setUnderlyingPrice(config.tokens.cUni.address, BigNumber.from('1000000000000000000000000000000'));
priceOracle.setUnderlyingPrice(config.tokens.cUsdt.address, BigNumber.from('1000000000000000000000000000000'));

// Force creamOracle update by setting comptroller again (but with the custom price oracle)
await hre.network.provider.send('hardhat_impersonateAccount', [morphoMarketsManagerForCompLike.address]);
await hre.network.provider.send('hardhat_setBalance', [morphoMarketsManagerForCompLike.address, ethers.utils.parseEther('10').toHexString()]);
const morphoMarketsManagerUser = await ethers.getSigner(morphoMarketsManagerForCompLike.address);
await morphoPositionsManagerForCream.connect(morphoMarketsManagerUser).setComptroller(comptroller.address);

// Mine block
await hre.network.provider.send('evm_mine', []);

Expand All @@ -885,8 +934,8 @@ describe('MorphoPositionsManagerForCream Contract', () => {
const mDaiExchangeRate = await morphoMarketsManagerForCompLike.mUnitExchangeRate(config.tokens.cDai.address);
const cUsdcExchangeRate = await cUsdcToken.callStatic.exchangeRateCurrent();
const liquidationIncentive = await comptroller.liquidationIncentiveMantissa();
const collateralAssetPrice = await compoundOracle.getUnderlyingPrice(config.tokens.cUsdc.address);
const borrowedAssetPrice = await compoundOracle.getUnderlyingPrice(config.tokens.cDai.address);
const collateralAssetPrice = await priceOracle.getUnderlyingPrice(config.tokens.cUsdc.address);
const borrowedAssetPrice = await priceOracle.getUnderlyingPrice(config.tokens.cDai.address);
const amountToSeize = toRepay.mul(borrowedAssetPrice).div(collateralAssetPrice).mul(liquidationIncentive).div(SCALE);
const expectedCollateralBalanceInP2PAfter = collateralBalanceInP2PBefore.sub(amountToSeize.sub(cTokenToUnderlying(collateralBalanceOnCompBefore, cUsdcExchangeRate)));
const expectedBorrowBalanceInP2PAfter = borrowBalanceInP2PBefore.sub(toRepay.mul(SCALE).div(mDaiExchangeRate));
Expand Down

0 comments on commit e428e50

Please sign in to comment.