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

Commit 1b5701f

Browse files
authored
Merge pull request #41 from ionicprotocol/test/kim-flash-swap-liquidations
Kim flash swap liquidations
2 parents d0cd0c6 + 719d735 commit 1b5701f

13 files changed

+397
-196
lines changed

contracts/IonicLiquidator.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ contract IonicLiquidator is OwnableUpgradeable, ILiquidator, IUniswapV2Callee {
7070
uint8 _flashSwapFee
7171
) external initializer {
7272
__Ownable_init();
73-
require(_uniswapV2router != address(0), "UniswapV2Factory not defined.");
73+
require(_uniswapV2router != address(0), "_uniswapV2router not defined.");
7474
W_NATIVE_ADDRESS = _wtoken;
7575
UNISWAP_V2_ROUTER_02 = IUniswapV2Router02(_uniswapV2router);
7676
flashSwapFee = _flashSwapFee;
@@ -188,7 +188,7 @@ contract IonicLiquidator is OwnableUpgradeable, ILiquidator, IUniswapV2Callee {
188188
msg.data
189189
);
190190

191-
return transferSeizedFunds(address(vars.cTokenCollateral), vars.minProfitAmount);
191+
return transferSeizedFunds(_liquidatorProfitExchangeSource, vars.minProfitAmount);
192192
}
193193

194194
/**

contracts/compound/CTokenFirstExtension.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,6 @@ contract CTokenFirstExtension is
346346
function borrowRatePerBlockAfterBorrow(uint256 borrowAmount) public view returns (uint256) {
347347
uint256 cash = asCToken().getCash();
348348
require(cash >= borrowAmount, "market cash not enough");
349-
//if (cash < borrowAmount) revert ;
350349

351350
return
352351
interestRateModel.getBorrowRate(
@@ -708,7 +707,7 @@ contract CTokenFirstExtension is
708707
}
709708

710709
function registerInSFS() external returns (uint256) {
711-
require(hasAdminRights(), "!admin");
710+
require(hasAdminRights() || msg.sender == address(comptroller), "!admin");
712711
SFSRegister sfsContract = SFSRegister(0x8680CEaBcb9b56913c519c069Add6Bc3494B7020);
713712
return sfsContract.register(0x8Fba84867Ba458E7c6E2c024D2DE3d0b5C3ea1C2);
714713
}

contracts/compound/ComptrollerFirstExtension.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,11 @@ contract ComptrollerFirstExtension is
508508
function registerInSFS() external returns (uint256) {
509509
require(hasAdminRights(), "!admin");
510510
SFSRegister sfsContract = SFSRegister(0x8680CEaBcb9b56913c519c069Add6Bc3494B7020);
511+
512+
for (uint256 i = 0; i < allMarkets.length; i++) {
513+
allMarkets[i].registerInSFS();
514+
}
515+
511516
return sfsContract.register(0x8Fba84867Ba458E7c6E2c024D2DE3d0b5C3ea1C2);
512517
}
513518
}

contracts/external/uniswap/IUniswapV2Router02.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ interface IUniswapV2Router02 is IUniswapV2Router01 {
3434
uint256 deadline
3535
) external;
3636

37+
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
38+
uint256 amountIn,
39+
uint256 amountOutMin,
40+
address[] calldata path,
41+
address to,
42+
address referrer,
43+
uint256 deadline
44+
) external;
45+
3746
function swapExactETHForTokensSupportingFeeOnTransferTokens(
3847
uint256 amountOutMin,
3948
address[] calldata path,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.0;
3+
4+
import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol";
5+
import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol";
6+
7+
import "../external/uniswap/IUniswapV2Router02.sol";
8+
9+
import "./IRedemptionStrategy.sol";
10+
11+
abstract contract BaseUniswapV2Liquidator is IRedemptionStrategy {
12+
using SafeERC20Upgradeable for IERC20Upgradeable;
13+
14+
/**
15+
* @notice Redeems custom collateral `token` for an underlying token.
16+
* @param inputToken The input wrapped token to be redeemed for an underlying token.
17+
* @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token.
18+
* @param strategyData The ABI-encoded data to be used in the redemption strategy logic.
19+
* @return outputToken The underlying ERC20 token outputted.
20+
* @return outputAmount The quantity of underlying tokens outputted.
21+
*/
22+
function redeem(
23+
IERC20Upgradeable inputToken,
24+
uint256 inputAmount,
25+
bytes memory strategyData
26+
) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) {
27+
return _convert(inputToken, inputAmount, strategyData);
28+
}
29+
30+
function _convert(
31+
IERC20Upgradeable inputToken,
32+
uint256 inputAmount,
33+
bytes memory strategyData
34+
) internal returns (IERC20Upgradeable outputToken, uint256 outputAmount) {
35+
// Get Uniswap router and path
36+
(IUniswapV2Router02 uniswapV2Router, address[] memory swapPath) = abi.decode(
37+
strategyData,
38+
(IUniswapV2Router02, address[])
39+
);
40+
require(swapPath.length >= 2 && swapPath[0] == address(inputToken), "Invalid UniswapLiquidator swap path.");
41+
42+
// Swap underlying tokens
43+
inputToken.approve(address(uniswapV2Router), inputAmount);
44+
45+
// call the relevant fn depending on the uni v2 fork specifics
46+
_swap(uniswapV2Router, inputAmount, swapPath);
47+
48+
// Get new collateral
49+
outputToken = IERC20Upgradeable(swapPath[swapPath.length - 1]);
50+
outputAmount = outputToken.balanceOf(address(this));
51+
}
52+
53+
function _swap(
54+
IUniswapV2Router02 uniswapV2Router,
55+
uint256 inputAmount,
56+
address[] memory swapPath
57+
) internal virtual;
58+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.0;
3+
4+
import "./BaseUniswapV2Liquidator.sol";
5+
6+
contract KimUniV2Liquidator is BaseUniswapV2Liquidator {
7+
function _swap(
8+
IUniswapV2Router02 uniswapV2Router,
9+
uint256 inputAmount,
10+
address[] memory swapPath
11+
) internal override {
12+
uniswapV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
13+
inputAmount,
14+
0,
15+
swapPath,
16+
address(this),
17+
address(0), // referrer
18+
block.timestamp
19+
);
20+
}
21+
22+
function name() public pure virtual returns (string memory) {
23+
return "KimUniV2Liquidator";
24+
}
25+
}

contracts/liquidators/UniswapV2Liquidator.sol

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,20 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity >=0.8.0;
33

4-
import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol";
5-
import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol";
6-
7-
import "../external/uniswap/IUniswapV2Router02.sol";
8-
9-
import "./IRedemptionStrategy.sol";
4+
import "./BaseUniswapV2Liquidator.sol";
105

116
/**
127
* @title UniswapV2Liquidator
138
* @notice Exchanges seized token collateral for underlying tokens via a Uniswap V2 router for use as a step in a liquidation.
149
* @author David Lucid <[email protected]> (https://github.com/davidlucid)
1510
*/
16-
contract UniswapV2Liquidator is IRedemptionStrategy {
17-
using SafeERC20Upgradeable for IERC20Upgradeable;
18-
19-
/**
20-
* @notice Redeems custom collateral `token` for an underlying token.
21-
* @param inputToken The input wrapped token to be redeemed for an underlying token.
22-
* @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token.
23-
* @param strategyData The ABI-encoded data to be used in the redemption strategy logic.
24-
* @return outputToken The underlying ERC20 token outputted.
25-
* @return outputAmount The quantity of underlying tokens outputted.
26-
*/
27-
function redeem(
28-
IERC20Upgradeable inputToken,
29-
uint256 inputAmount,
30-
bytes memory strategyData
31-
) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) {
32-
return _convert(inputToken, inputAmount, strategyData);
33-
}
34-
35-
function _convert(
36-
IERC20Upgradeable inputToken,
11+
contract UniswapV2Liquidator is BaseUniswapV2Liquidator {
12+
function _swap(
13+
IUniswapV2Router02 uniswapV2Router,
3714
uint256 inputAmount,
38-
bytes memory strategyData
39-
) internal returns (IERC20Upgradeable outputToken, uint256 outputAmount) {
40-
// Get Uniswap router and path
41-
(IUniswapV2Router02 uniswapV2Router, address[] memory swapPath) = abi.decode(
42-
strategyData,
43-
(IUniswapV2Router02, address[])
44-
);
45-
require(swapPath.length >= 2 && swapPath[0] == address(inputToken), "Invalid UniswapLiquidator swap path.");
46-
47-
// Swap underlying tokens
48-
inputToken.approve(address(uniswapV2Router), inputAmount);
15+
address[] memory swapPath
16+
) internal override {
4917
uniswapV2Router.swapExactTokensForTokens(inputAmount, 0, swapPath, address(this), block.timestamp);
50-
51-
// Get new collateral
52-
outputToken = IERC20Upgradeable(swapPath[swapPath.length - 1]);
53-
outputAmount = outputToken.balanceOf(address(this));
5418
}
5519

5620
function name() public pure virtual returns (string memory) {

contracts/liquidators/registry/LiquidatorsRegistryExtension.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ contract LiquidatorsRegistryExtension is LiquidatorsRegistryStorage, DiamondExte
253253
strategyData = solidlySwapLiquidatorData(inputToken, outputToken);
254254
} else if (isStrategy(strategy, "SolidlyLpTokenLiquidator")) {
255255
strategyData = solidlyLpTokenLiquidatorData(inputToken, outputToken);
256-
} else if (isStrategy(strategy, "UniswapV2LiquidatorFunder")) {
256+
} else if (isStrategy(strategy, "UniswapV2LiquidatorFunder") || isStrategy(strategy, "KimUniV2Liquidator")) {
257257
strategyData = uniswapV2LiquidatorData(inputToken, outputToken);
258258
} else if (isStrategy(strategy, "UniswapV3LiquidatorFunder")) {
259259
strategyData = uniswapV3LiquidatorFunderData(inputToken, outputToken);

contracts/test/ComptrollerTest.t.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ contract ComptrollerTest is BaseTest {
6161
_testInflationProtection();
6262
}
6363

64+
function testModeInflationProtection() public debuggingOnly fork(MODE_MAINNET) {
65+
_testInflationProtection();
66+
}
67+
6468
function _testInflationProtection() internal {
6569
PoolDirectory fpd = PoolDirectory(ap.getAddress("PoolDirectory"));
6670
PoolDirectory.Pool[] memory pools = fpd.getAllPools();

contracts/test/DevTesting.t.sol

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,36 @@ contract DevTesting is BaseTest {
136136
}
137137
}
138138

139+
function testDisableCollateralUsdc() public debuggingOnly fork(MODE_MAINNET) {
140+
address user = 0xF70CBE91fB1b1AfdeB3C45Fb8CDD2E1249b5b75E;
141+
address usdcMarketAddr = 0x2BE717340023C9e14C1Bb12cb3ecBcfd3c3fB038;
142+
143+
vm.startPrank(user);
144+
145+
uint256 borrowed = ICErc20(usdcMarketAddr).borrowBalanceCurrent(user);
146+
147+
emit log_named_uint("borrowed", borrowed);
148+
149+
pool.exitMarket(usdcMarketAddr);
150+
}
151+
139152
function testAssetAsCollateralCap() public debuggingOnly fork(MODE_MAINNET) {
140153
pool.getAssetAsCollateralValueCap(wethMarket, usdcMarket, false, deployer);
141154
}
142155

156+
function testRegisterSFS() public debuggingOnly fork(MODE_MAINNET) {
157+
emit log_named_address("pool admin", pool.admin());
158+
159+
vm.startPrank(0x8Fba84867Ba458E7c6E2c024D2DE3d0b5C3ea1C2);
160+
pool.registerInSFS();
161+
162+
ICErc20[] memory markets = pool.getAllMarkets();
163+
164+
for (uint8 i = 0; i < markets.length; i++) {
165+
markets[i].registerInSFS();
166+
}
167+
}
168+
143169
function testModeUsdcBorrow() public debuggingOnly fork(MODE_MAINNET) {
144170
vm.prank(deployer);
145171
require(usdcMarket.borrow(5e6) == 0, "can't borrow");

0 commit comments

Comments
 (0)