mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Refactor to avoid leftovers on _swapAndRepay with flash loan
This commit is contained in:
parent
4d2d9e8459
commit
a496be8833
|
@ -123,8 +123,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
|||
/**
|
||||
* @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan
|
||||
*
|
||||
* @param assetFrom Address of token to be swapped
|
||||
* @param assetTo Address of debt token to be received from the swap
|
||||
* @param collateralAsset Address of token to be swapped
|
||||
* @param debtAsset Address of debt token to be received from the swap
|
||||
* @param amount Amount of the debt to be repaid
|
||||
* @param collateralAmount Amount of the reserve to be swapped
|
||||
* @param rateMode Rate mode of the debt to be repaid
|
||||
|
@ -133,8 +133,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
|||
* @param permitSignature struct containing the permit signature
|
||||
*/
|
||||
function _swapAndRepay(
|
||||
address assetFrom,
|
||||
address assetTo,
|
||||
address collateralAsset,
|
||||
address debtAsset,
|
||||
uint256 amount,
|
||||
uint256 collateralAmount,
|
||||
uint256 rateMode,
|
||||
|
@ -142,25 +142,48 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
|||
uint256 premium,
|
||||
PermitSignature memory permitSignature
|
||||
) internal {
|
||||
ReserveLogic.ReserveData memory collateralReserveData = _getReserveData(collateralAsset);
|
||||
|
||||
// Repay debt
|
||||
IERC20(assetTo).approve(address(POOL), amount);
|
||||
POOL.repay(assetTo, amount, rateMode, initiator);
|
||||
uint256 debtRepayLeftovers = IERC20(assetTo).balanceOf(address(this));
|
||||
IERC20(debtAsset).approve(address(POOL), amount);
|
||||
uint256 repaidAmount = IERC20(debtAsset).balanceOf(address(this));
|
||||
POOL.repay(debtAsset, amount, rateMode, initiator);
|
||||
repaidAmount = repaidAmount.sub(IERC20(debtAsset).balanceOf(address(this)));
|
||||
|
||||
uint256 flashLoanDebt = amount.add(premium);
|
||||
uint256 neededForFlashLoanDebt = flashLoanDebt.sub(debtRepayLeftovers);
|
||||
if (collateralAsset != debtAsset) {
|
||||
uint256 maxCollateralToSwap = collateralAmount;
|
||||
if (repaidAmount < amount) {
|
||||
maxCollateralToSwap = maxCollateralToSwap.mul(repaidAmount).div(amount);
|
||||
}
|
||||
|
||||
// Pull aTokens from user
|
||||
ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom);
|
||||
_pullAToken(assetFrom, reserveData.aTokenAddress, initiator, collateralAmount, permitSignature);
|
||||
uint256 neededForFlashLoanDebt = repaidAmount.add(premium);
|
||||
uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt);
|
||||
require(amounts[0] <= maxCollateralToSwap, 'slippage too high');
|
||||
|
||||
uint256 amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, collateralAmount, neededForFlashLoanDebt);
|
||||
// Pull aTokens from user
|
||||
_pullAToken(
|
||||
collateralAsset,
|
||||
collateralReserveData.aTokenAddress,
|
||||
initiator,
|
||||
amounts[0],
|
||||
permitSignature
|
||||
);
|
||||
|
||||
// Send collateral leftovers from swap to the user
|
||||
_sendLeftovers(assetFrom, initiator);
|
||||
// Swap collateral asset to the debt asset
|
||||
_swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], neededForFlashLoanDebt);
|
||||
} else {
|
||||
// Pull aTokens from user
|
||||
_pullAToken(
|
||||
collateralAsset,
|
||||
collateralReserveData.aTokenAddress,
|
||||
initiator,
|
||||
repaidAmount.add(premium),
|
||||
permitSignature
|
||||
);
|
||||
}
|
||||
|
||||
// Repay flashloan
|
||||
IERC20(assetTo).approve(address(POOL), flashLoanDebt);
|
||||
IERC20(debtAsset).approve(address(POOL), amount.add(premium));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -201,17 +224,4 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers the balance of the adapter to the user, as there shouldn't be any leftover in the adapter
|
||||
* @param asset address of the asset
|
||||
* @param user address
|
||||
*/
|
||||
function _sendLeftovers(address asset, address user) internal {
|
||||
uint256 assetLeftover = IERC20(asset).balanceOf(address(this));
|
||||
|
||||
if (assetLeftover > 0) {
|
||||
IERC20(asset).transfer(user, assetLeftover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 {
|
|||
mapping(address => uint256) internal _amountToSwap;
|
||||
mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsIn;
|
||||
mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsOut;
|
||||
uint256 internal defaultMockValue;
|
||||
|
||||
function setAmountToReturn(address reserve, uint256 amount) public {
|
||||
_amountToReturn[reserve] = amount;
|
||||
|
@ -61,16 +62,20 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 {
|
|||
_amountsIn[reserveIn][reserveOut][amountOut] = amountIn;
|
||||
}
|
||||
|
||||
function setDefaultMockValue(uint value) public {
|
||||
defaultMockValue = value;
|
||||
}
|
||||
|
||||
function getAmountsOut(uint amountIn, address[] calldata path) external view override returns (uint[] memory) {
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
amounts[0] = amountIn;
|
||||
amounts[1] = _amountsOut[path[0]][path[1]][amountIn];
|
||||
amounts[1] = _amountsOut[path[0]][path[1]][amountIn] > 0 ? _amountsOut[path[0]][path[1]][amountIn] : defaultMockValue;
|
||||
return amounts;
|
||||
}
|
||||
|
||||
function getAmountsIn(uint amountOut, address[] calldata path) external view override returns (uint[] memory) {
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
amounts[0] = _amountsIn[path[0]][path[1]][amountOut];
|
||||
amounts[0] = _amountsIn[path[0]][path[1]][amountOut] > 0 ? _amountsIn[path[0]][path[1]][amountOut] : defaultMockValue;
|
||||
amounts[1] = amountOut;
|
||||
return amounts;
|
||||
}
|
||||
|
|
|
@ -2091,6 +2091,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.multipliedBy(1.0009)
|
||||
.toFixed(0);
|
||||
|
||||
await mockUniswapRouter.setAmountIn(
|
||||
flashLoanDebt,
|
||||
weth.address,
|
||||
dai.address,
|
||||
liquidityToSwap
|
||||
);
|
||||
|
||||
const params = buildRepayAdapterParams(
|
||||
weth.address,
|
||||
liquidityToSwap,
|
||||
|
@ -2198,6 +2205,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.multipliedBy(1.0009)
|
||||
.toFixed(0);
|
||||
|
||||
await mockUniswapRouter.setAmountIn(
|
||||
flashLoanDebt,
|
||||
weth.address,
|
||||
dai.address,
|
||||
liquidityToSwap
|
||||
);
|
||||
|
||||
const params = buildRepayAdapterParams(
|
||||
weth.address,
|
||||
liquidityToSwap,
|
||||
|
@ -2401,6 +2415,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
|
||||
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap);
|
||||
|
||||
const flashLoanDebt = new BigNumber(expectedDaiAmount.toString())
|
||||
.multipliedBy(1.0009)
|
||||
.toFixed(0);
|
||||
|
||||
await mockUniswapRouter.setAmountIn(
|
||||
flashLoanDebt,
|
||||
weth.address,
|
||||
dai.address,
|
||||
bigMaxAmountToSwap
|
||||
);
|
||||
|
||||
const params = buildRepayAdapterParams(
|
||||
weth.address,
|
||||
bigMaxAmountToSwap,
|
||||
|
@ -2472,14 +2497,19 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.multipliedBy(0.995)
|
||||
.toFixed(0);
|
||||
|
||||
const leftOverWeth = new BigNumber(liquidityToSwap.toString()).minus(actualWEthSwapped);
|
||||
|
||||
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped);
|
||||
|
||||
const flashLoanDebt = new BigNumber(expectedDaiAmount.toString())
|
||||
.multipliedBy(1.0009)
|
||||
.toFixed(0);
|
||||
|
||||
await mockUniswapRouter.setAmountIn(
|
||||
flashLoanDebt,
|
||||
weth.address,
|
||||
dai.address,
|
||||
actualWEthSwapped
|
||||
);
|
||||
|
||||
const params = buildRepayAdapterParams(
|
||||
weth.address,
|
||||
liquidityToSwap,
|
||||
|
@ -2520,8 +2550,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount);
|
||||
expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount);
|
||||
expect(userAEthBalance).to.be.lt(userAEthBalanceBefore);
|
||||
expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(liquidityToSwap));
|
||||
expect(userWethBalance).to.be.gte(userWethBalanceBefore.add(leftOverWeth.toString()));
|
||||
expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped));
|
||||
expect(userWethBalance).to.be.eq(userWethBalanceBefore);
|
||||
});
|
||||
|
||||
it('should correctly swap tokens and repay the whole stable debt with no leftovers', async () => {
|
||||
|
@ -2560,7 +2590,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
|
||||
const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress);
|
||||
|
||||
const liquidityToSwap = amountWETHtoSwap;
|
||||
// Add a % to repay on top of the debt
|
||||
const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString())
|
||||
.multipliedBy(1.1)
|
||||
.toFixed(0);
|
||||
|
||||
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
|
||||
|
||||
|
@ -2569,7 +2603,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.multipliedBy(1.1)
|
||||
.toFixed(0);
|
||||
|
||||
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
|
||||
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap);
|
||||
await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap);
|
||||
|
||||
const params = buildRepayAdapterParams(
|
||||
weth.address,
|
||||
|
@ -2647,7 +2682,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
userAddress
|
||||
);
|
||||
|
||||
const liquidityToSwap = amountWETHtoSwap;
|
||||
// Add a % to repay on top of the debt
|
||||
const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString())
|
||||
.multipliedBy(1.1)
|
||||
.toFixed(0);
|
||||
|
||||
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
|
||||
|
||||
|
@ -2656,7 +2695,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.multipliedBy(1.1)
|
||||
.toFixed(0);
|
||||
|
||||
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
|
||||
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap);
|
||||
await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap);
|
||||
|
||||
const params = buildRepayAdapterParams(
|
||||
weth.address,
|
||||
|
|
Loading…
Reference in New Issue
Block a user