Skip to content
This repository was archived by the owner on Aug 26, 2024. It is now read-only.

Commit dc8ad4c

Browse files
safeLiquidate now redeems collateral
1 parent 544bf46 commit dc8ad4c

File tree

4 files changed

+182
-6
lines changed

4 files changed

+182
-6
lines changed

contracts/ILiquidator.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ interface ILiquidator {
3535
uint256 repayAmount,
3636
ICErc20 cErc20,
3737
ICErc20 cTokenCollateral,
38-
uint256 minOutputAmount
38+
uint256 minOutputAmount,
39+
bool redeemCollateral
3940
) external returns (uint256);
4041

4142
function safeLiquidateToTokensWithFlashLoan(LiquidateToTokensWithFlashSwapVars calldata vars)

contracts/IonicLiquidator.sol

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,27 @@ contract IonicLiquidator is OwnableUpgradeable, ILiquidator, IUniswapV2Callee, I
119119
uint256 repayAmount,
120120
ICErc20 cErc20,
121121
ICErc20 cTokenCollateral,
122-
uint256 minOutputAmount
122+
uint256 minOutputAmount,
123+
bool redeemCollateral
123124
) external onlyPERPermissioned(borrower) returns (uint256) {
124125
// Transfer tokens in, approve to cErc20, and liquidate borrow
125126
require(repayAmount > 0, "Repay amount (transaction value) must be greater than 0.");
126127
IERC20Upgradeable underlying = IERC20Upgradeable(cErc20.underlying());
127128
underlying.safeTransferFrom(msg.sender, address(this), repayAmount);
128129
justApprove(underlying, address(cErc20), repayAmount);
129130
require(cErc20.liquidateBorrow(borrower, repayAmount, address(cTokenCollateral)) == 0, "Liquidation failed.");
130-
// Transfer seized amount to sender
131-
return transferSeizedFunds(address(cTokenCollateral), minOutputAmount);
131+
132+
if (redeemCollateral) {
133+
// Redeem seized cTokens for underlying asset
134+
uint256 seizedCTokenAmount = cTokenCollateral.balanceOf(address(this));
135+
require(seizedCTokenAmount > 0, "No cTokens seized.");
136+
uint256 redeemResult = cTokenCollateral.redeem(seizedCTokenAmount);
137+
require(redeemResult == 0, "Error calling redeeming seized cToken: error code not equal to 0");
138+
139+
return transferSeizedFunds(address(cTokenCollateral.underlying()), minOutputAmount);
140+
} else {
141+
return transferSeizedFunds(address(cTokenCollateral), minOutputAmount);
142+
}
132143
}
133144

134145
/**

contracts/IonicUniV3Liquidator.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ contract IonicUniV3Liquidator is OwnableUpgradeable, ILiquidator, IUniswapV3Flas
6767
uint256 repayAmount,
6868
ICErc20 cErc20,
6969
ICErc20 cTokenCollateral,
70-
uint256 minOutputAmount
70+
uint256 minOutputAmount,
71+
bool redeemCollateral
7172
) external returns (uint256) {
7273
// Transfer tokens in, approve to cErc20, and liquidate borrow
7374
require(repayAmount > 0, "Repay amount (transaction value) must be greater than 0.");

contracts/test/DevTesting.t.sol

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,30 @@ import { PoolLensSecondary } from "../PoolLensSecondary.sol";
2020
import { JumpRateModel } from "../compound/JumpRateModel.sol";
2121
import { LeveredPositionsLens } from "../ionic/levered/LeveredPositionsLens.sol";
2222

23+
struct HealthFactorVars {
24+
uint256 usdcSupplied;
25+
uint256 wethSupplied;
26+
uint256 ezEthSuppled;
27+
uint256 stoneSupplied;
28+
uint256 wbtcSupplied;
29+
uint256 weEthSupplied;
30+
uint256 merlinBTCSupplied;
31+
uint256 usdcBorrowed;
32+
uint256 wethBorrowed;
33+
uint256 ezEthBorrowed;
34+
uint256 stoneBorrowed;
35+
uint256 wbtcBorrowed;
36+
uint256 weEthBorrowed;
37+
uint256 merlinBTCBorrowed;
38+
ICErc20 testCToken;
39+
address testUnderlying;
40+
uint256 amountBorrow;
41+
}
42+
2343
contract DevTesting is BaseTest {
2444
IonicComptroller pool = IonicComptroller(0xFB3323E24743Caf4ADD0fDCCFB268565c0685556);
2545
PoolLensSecondary lens2 = PoolLensSecondary(0x7Ea7BB80F3bBEE9b52e6Ed3775bA06C9C80D4154);
26-
PoolLens lens = PoolLens(0x431C87E08e2636733a945D742d25Ba77577ED480);
46+
PoolLens lens = PoolLens(0x70BB19a56BfAEc65aE861E6275A90163AbDF36a6);
2747
LeveredPositionsLens levPosLens;
2848

2949
address deployer = 0x1155b614971f16758C92c4890eD338C9e3ede6b7;
@@ -34,6 +54,9 @@ contract DevTesting is BaseTest {
3454
ICErc20 usdtMarket;
3555
ICErc20 wbtcMarket;
3656
ICErc20 ezEthMarket;
57+
ICErc20 stoneMarket;
58+
ICErc20 weEthMarket;
59+
ICErc20 merlinBTCMarket;
3760

3861
// mode mainnet assets
3962
address WETH = 0x4200000000000000000000000000000000000006;
@@ -46,6 +69,8 @@ contract DevTesting is BaseTest {
4669
address DAI = 0xE7798f023fC62146e8Aa1b36Da45fb70855a77Ea;
4770
address BAL = 0xD08a2917653d4E460893203471f0000826fb4034;
4871
address AAVE = 0x7c6b91D9Be155A6Db01f749217d76fF02A7227F2;
72+
address weETH = 0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A;
73+
address merlinBTC = 0x59889b7021243dB5B1e065385F918316cD90D46c;
4974

5075
function afterForkSetUp() internal override {
5176
super.afterForkSetUp();
@@ -56,6 +81,9 @@ contract DevTesting is BaseTest {
5681
usdtMarket = ICErc20(0x94812F2eEa03A49869f95e1b5868C6f3206ee3D3);
5782
wbtcMarket = ICErc20(0xd70254C3baD29504789714A7c69d60Ec1127375C);
5883
ezEthMarket = ICErc20(0x59e710215d45F584f44c0FEe83DA6d43D762D857);
84+
stoneMarket = ICErc20(0x959FA710CCBb22c7Ce1e59Da82A247e686629310);
85+
weEthMarket = ICErc20(0xA0D844742B4abbbc43d8931a6Edb00C56325aA18);
86+
merlinBTCMarket = ICErc20(0x19F245782b1258cf3e11Eda25784A378cC18c108);
5987
} else {
6088
ICErc20[] memory markets = pool.getAllMarkets();
6189
wethMarket = markets[0];
@@ -109,6 +137,141 @@ contract DevTesting is BaseTest {
109137
_testModeBorrowCaps(usdcMarket);
110138
}
111139

140+
function testHypotheticalPosition() public debuggingOnly forkAtBlock(MODE_MAINNET, 8028296) {
141+
HealthFactorVars memory vars;
142+
143+
address wolfy = 0x7d922bf0975424b3371074f54cC784AF738Dac0D;
144+
address usdcWhale = 0x70FF197c32E922700d3ff2483D250c645979855d;
145+
address wbtcWhale = 0xBD8CCf3ebE4CC2D57962cdC2756B143ce0135a6B;
146+
address wethWhale = 0xD746A2a6048C5D3AFF5766a8c4A0C8cFD2311745;
147+
148+
address whale = wbtcWhale;
149+
vars.testCToken = wethMarket;
150+
vars.testUnderlying = WETH;
151+
vars.amountBorrow = 1e18 / 2;
152+
153+
address[] memory cTokens = new address[](1);
154+
155+
vm.startPrank(usdcWhale);
156+
ERC20(USDC).transfer(wolfy, ERC20(USDC).balanceOf(usdcWhale));
157+
vm.stopPrank();
158+
159+
vm.startPrank(wbtcWhale);
160+
ERC20(WBTC).transfer(wolfy, ERC20(WBTC).balanceOf(wbtcWhale));
161+
vm.stopPrank();
162+
163+
vm.startPrank(wethWhale);
164+
ERC20(WETH).transfer(wolfy, ERC20(WETH).balanceOf(wethWhale));
165+
vm.stopPrank();
166+
167+
// emit log_named_uint("USDC balance", ERC20(USDC).balanceOf(wolfy));
168+
// emit log_named_uint("WBTC balance", ERC20(WBTC).balanceOf(wolfy));
169+
// emit log_named_uint("WETH balance", ERC20(WETH).balanceOf(wolfy));
170+
171+
vm.startPrank(wolfy);
172+
173+
ERC20(USDC).approve(address(usdcMarket), ERC20(USDC).balanceOf(wolfy));
174+
usdcMarket.mint(ERC20(USDC).balanceOf(wolfy));
175+
cTokens[0] = address(usdcMarket);
176+
pool.enterMarkets(cTokens);
177+
178+
ERC20(WBTC).approve(address(wbtcMarket), ERC20(WBTC).balanceOf(wolfy));
179+
wbtcMarket.mint(ERC20(WBTC).balanceOf(wolfy));
180+
cTokens[0] = address(wbtcMarket);
181+
pool.enterMarkets(cTokens);
182+
183+
ERC20(WETH).approve(address(wethMarket), ERC20(WETH).balanceOf(wolfy));
184+
wethMarket.mint(ERC20(WETH).balanceOf(wolfy));
185+
cTokens[0] = address(wethMarket);
186+
pool.enterMarkets(cTokens);
187+
188+
wethMarket.borrow(1e18);
189+
190+
vm.stopPrank();
191+
192+
vars.usdcSupplied = usdcMarket.balanceOfUnderlying(wolfy);
193+
vars.wethSupplied = wethMarket.balanceOfUnderlying(wolfy);
194+
vars.ezEthSuppled = ezEthMarket.balanceOfUnderlying(wolfy);
195+
vars.stoneSupplied = stoneMarket.balanceOfUnderlying(wolfy);
196+
vars.wbtcSupplied = wbtcMarket.balanceOfUnderlying(wolfy);
197+
vars.weEthSupplied = weEthMarket.balanceOfUnderlying(wolfy);
198+
vars.merlinBTCSupplied = merlinBTCMarket.balanceOfUnderlying(wolfy);
199+
200+
vars.usdcBorrowed = usdcMarket.borrowBalanceCurrent(wolfy);
201+
vars.wethBorrowed = wethMarket.borrowBalanceCurrent(wolfy);
202+
vars.ezEthBorrowed = ezEthMarket.borrowBalanceCurrent(wolfy);
203+
vars.stoneBorrowed = stoneMarket.borrowBalanceCurrent(wolfy);
204+
vars.wbtcBorrowed = wbtcMarket.borrowBalanceCurrent(wolfy);
205+
vars.weEthBorrowed = weEthMarket.borrowBalanceCurrent(wolfy);
206+
vars.merlinBTCBorrowed = merlinBTCMarket.borrowBalanceCurrent(wolfy);
207+
208+
emit log_named_uint("usdcSupplied", vars.usdcSupplied);
209+
emit log_named_uint("wethSupplied", vars.wethSupplied);
210+
emit log_named_uint("ezEthSupplied", vars.ezEthSuppled);
211+
emit log_named_uint("stoneSupplied", vars.stoneSupplied);
212+
emit log_named_uint("wbtcSupplied", vars.wbtcSupplied);
213+
emit log_named_uint("weEthSupplied", vars.weEthSupplied);
214+
emit log_named_uint("merlinBTCSupplied", vars.merlinBTCSupplied);
215+
216+
emit log_named_uint("-------------------------------------------------", 0);
217+
emit log_named_uint("usdcBorrowed", vars.usdcBorrowed);
218+
emit log_named_uint("wethBorrowed", vars.wethBorrowed);
219+
emit log_named_uint("ezEthBorrowed", vars.ezEthBorrowed);
220+
emit log_named_uint("stoneBorrowed", vars.stoneBorrowed);
221+
emit log_named_uint("wbtcBorrowed", vars.wbtcBorrowed);
222+
emit log_named_uint("weEthBorrowed", vars.weEthBorrowed);
223+
emit log_named_uint("merlinBTCBorrowed", vars.merlinBTCBorrowed);
224+
225+
// emit log_named_uint("value of usdcSupplied", vars.usdcSupplied * pool.oracle().getUnderlyingPrice(usdcMarket));
226+
// emit log_named_uint("value of wethSupplied", vars.wethSupplied * pool.oracle().getUnderlyingPrice(wethMarket));
227+
// emit log_named_uint("value of ezEthSupplied", vars.ezEthSuppled * pool.oracle().getUnderlyingPrice(ezEthMarket));
228+
// emit log_named_uint("value of stoneSupplied", vars.stoneSupplied * pool.oracle().getUnderlyingPrice(stoneMarket));
229+
// emit log_named_uint("value of wbtcSupplied", vars.wbtcSupplied * pool.oracle().getUnderlyingPrice(wbtcMarket));
230+
231+
// emit log_named_uint("value of usdcBorrowed", vars.usdcBorrowed * pool.oracle().getUnderlyingPrice(usdcMarket));
232+
// emit log_named_uint("value of wethBorrowed", vars.wethBorrowed * pool.oracle().getUnderlyingPrice(wethMarket));
233+
// emit log_named_uint("value of ezEthBorrowed", vars.ezEthBorrowed * pool.oracle().getUnderlyingPrice(ezEthMarket));
234+
// emit log_named_uint("value of stoneBorrowed", vars.stoneBorrowed * pool.oracle().getUnderlyingPrice(stoneMarket));
235+
// emit log_named_uint("value of wbtcBorrowed", vars.wbtcBorrowed * pool.oracle().getUnderlyingPrice(wbtcMarket));
236+
237+
vm.startPrank(whale);
238+
ERC20(vars.testUnderlying).transfer(wolfy, ERC20(vars.testUnderlying).balanceOf(whale));
239+
vm.stopPrank();
240+
241+
uint256 hf = lens.getHealthFactor(wolfy, pool);
242+
uint256 hypothetical = lens.getHealthFactorHypothetical(
243+
pool,
244+
wolfy,
245+
address(vars.testCToken),
246+
0,
247+
0,
248+
vars.amountBorrow
249+
);
250+
251+
(uint256 err, uint256 collateralValue, uint256 liquidity, uint256 shortfall) = pool.getAccountLiquidity(wolfy);
252+
253+
emit log_named_uint("-------------------------------------------------", 0);
254+
emit log_named_uint("Collateral Value Before", collateralValue);
255+
emit log_named_uint("Liquidity Before", liquidity);
256+
emit log_named_uint("hf before", hf);
257+
emit log_named_uint("hypothetical hf", hypothetical);
258+
259+
vm.startPrank(wolfy);
260+
ERC20(vars.testUnderlying).approve(address(vars.testCToken), vars.amountBorrow);
261+
vars.testCToken.repayBorrow(vars.amountBorrow);
262+
vm.stopPrank();
263+
264+
uint256 hfAfter = lens.getHealthFactor(wolfy, pool);
265+
(err, collateralValue, liquidity, shortfall) = pool.getAccountLiquidity(wolfy);
266+
267+
emit log_named_uint("-------------------------------------------------", 0);
268+
emit log_named_uint("Collateral Value After", collateralValue);
269+
emit log_named_uint("Liquidity After", liquidity);
270+
emit log_named_uint("hf after", hfAfter);
271+
emit log_named_uint("user balance after", ERC20(vars.testUnderlying).balanceOf(wolfy));
272+
emit log_named_uint("new borrow balance after repay", vars.testCToken.borrowBalanceCurrent(wolfy));
273+
}
274+
112275
function testModeUsdtBorrowCaps() public debuggingOnly fork(MODE_MAINNET) {
113276
_testModeBorrowCaps(usdtMarket);
114277
}

0 commit comments

Comments
 (0)