Refactor repayAdapter to flash loan the debt asset instead of the collateral

This commit is contained in:
Gerardo Nardelli 2020-11-20 15:53:50 -03:00
parent b48b50208a
commit 43d05c2bdf
3 changed files with 114 additions and 357 deletions

View File

@ -16,18 +16,10 @@ import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
**/ **/
contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
/*
* STANDARD: Use the provided amounts parameters
* ALL_DEBT: Repay the whole debt balance
* ALL_COLLATERAL: Use all the collateral balance to repay the max amount of debt
*/
enum RepayMode {STANDARD, ALL_DEBT, ALL_COLLATERAL}
struct RepayParams { struct RepayParams {
address assetToSwapTo; address collateralAsset;
uint256 repayAmount; uint256 collateralAmount;
uint256 rateMode; uint256 rateMode;
RepayMode repayMode;
PermitSignature permitSignature; PermitSignature permitSignature;
} }
@ -40,17 +32,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
{} {}
/** /**
* @dev Swaps the received reserve amount into the asset specified in the params. The received funds from the swap are * @dev Uses the received funds from the flash loan to repay a debt on the protocol on behalf of the user. Then pulls
* then used to repay a debt on the protocol on behalf of the user. * the collateral from the user and swaps it to repay the flash loan.
* The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset, swap it
* repay the flash loan. * and repay the flash loan.
* @param assets Address to be swapped * @param assets Address to be swapped
* @param amounts Amount of the reserve to be swapped * @param amounts Amount of the reserve to be swapped
* @param premiums Fee of the flash loan * @param premiums Fee of the flash loan
* @param initiator Address of the user * @param initiator Address of the user
* @param params Additional variadic field to include extra params. Expected parameters: * @param params Additional variadic field to include extra params. Expected parameters:
* address Address of the reserve to be swapped to and repay * address collateralAsset Address of the reserve to be swapped
* uint256 repayAmount Amount of debt to be repaid * uint256 collateralAmount Amount of reserve to be swapped
* uint256 rateMode Rate modes of the debt to be repaid * uint256 rateMode Rate modes of the debt to be repaid
* RepayMode repayMode Enum indicating the repaid mode * RepayMode repayMode Enum indicating the repaid mode
* uint256 permitAmount Amount for the permit signature * uint256 permitAmount Amount for the permit signature
@ -71,13 +63,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
RepayParams memory decodedParams = _decodeParams(params); RepayParams memory decodedParams = _decodeParams(params);
_swapAndRepay( _swapAndRepay(
decodedParams.collateralAsset,
assets[0], assets[0],
decodedParams.assetToSwapTo,
amounts[0], amounts[0],
decodedParams.repayAmount, decodedParams.collateralAmount,
decodedParams.rateMode, decodedParams.rateMode,
initiator, initiator,
decodedParams.repayMode,
premiums[0], premiums[0],
decodedParams.permitSignature decodedParams.permitSignature
); );
@ -86,12 +77,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
} }
/** /**
* @dev Perform the swap and the repay of the debt * @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 assetFrom Address of token to be swapped
* @param assetTo Address of token to be received * @param assetTo Address of debt token to be received from the swap
* @param amount Amount of the reserve to be swapped * @param amount Amount of the debt to be repaid
* @param repayAmount 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 * @param rateMode Rate mode of the debt to be repaid
* @param initiator Address of the user * @param initiator Address of the user
* @param premium Fee of the flash loan * @param premium Fee of the flash loan
@ -101,61 +92,39 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
address assetFrom, address assetFrom,
address assetTo, address assetTo,
uint256 amount, uint256 amount,
uint256 repayAmount, uint256 collateralAmount,
uint256 rateMode, uint256 rateMode,
address initiator, address initiator,
RepayMode repayMode,
uint256 premium, uint256 premium,
PermitSignature memory permitSignature PermitSignature memory permitSignature
) internal { ) internal {
uint256 debtRepayAmount;
uint256 amountSwapped;
ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom);
if (repayMode == RepayMode.ALL_COLLATERAL) {
uint256 aTokenInitiatorBalance = IERC20(reserveData.aTokenAddress).balanceOf(initiator);
amountSwapped = aTokenInitiatorBalance.sub(premium);
debtRepayAmount = _swapExactTokensForTokens(assetFrom, assetTo, amountSwapped, repayAmount);
} else {
if (repayMode == RepayMode.ALL_DEBT) {
ReserveLogic.ReserveData memory reserveDebtData = _getReserveData(assetTo);
address debtToken = ReserveLogic.InterestRateMode(rateMode) == ReserveLogic.InterestRateMode.STABLE
? reserveDebtData.stableDebtTokenAddress
: reserveDebtData.variableDebtTokenAddress;
debtRepayAmount = IERC20(debtToken).balanceOf(initiator);
} else {
debtRepayAmount = repayAmount;
}
amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, amount, debtRepayAmount);
}
// Repay debt // Repay debt
IERC20(assetTo).approve(address(POOL), debtRepayAmount); IERC20(assetTo).approve(address(POOL), amount);
POOL.repay(assetTo, debtRepayAmount, rateMode, initiator); POOL.repay(assetTo, amount, rateMode, initiator);
// In the case the repay amount provided exceeded the actual debt, send the leftovers to the user uint256 debtRepayLeftovers = IERC20(assetTo).balanceOf(address(this));
_sendRepayLeftovers(assetTo, initiator);
uint256 flashLoanDebt = amount.add(premium); uint256 flashLoanDebt = amount.add(premium);
uint256 amountToPull = amountSwapped.add(premium); uint256 neededForFlashLoanDebt = flashLoanDebt.sub(debtRepayLeftovers);
_pullAToken(assetFrom, reserveData.aTokenAddress, initiator, amountToPull, permitSignature); // Pull aTokens from user
ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom);
_pullAToken(assetFrom, reserveData.aTokenAddress, initiator, collateralAmount, permitSignature);
uint256 amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, collateralAmount, neededForFlashLoanDebt);
// Send collateral leftovers from swap to the user
_sendLeftovers(assetFrom, initiator);
// Repay flashloan // Repay flashloan
IERC20(assetFrom).approve(address(POOL), flashLoanDebt); IERC20(assetTo).approve(address(POOL), flashLoanDebt);
} }
/** /**
* @dev Decodes debt information encoded in flashloan params * @dev Decodes debt information encoded in flashloan params
* @param params Additional variadic field to include extra params. Expected parameters: * @param params Additional variadic field to include extra params. Expected parameters:
* address Address of the reserve to be swapped to and repay * address collateralAsset Address of the reserve to be swapped
* uint256 repayAmount Amount of debt to be repaid * uint256 collateralAmount Amount of reserve to be swapped
* uint256 rateMode Rate modes of the debt to be repaid * uint256 rateMode Rate modes of the debt to be repaid
* RepayMode repayMode Enum indicating the repaid mode
* uint256 permitAmount Amount for the permit signature * uint256 permitAmount Amount for the permit signature
* uint256 deadline Deadline for the permit signature * uint256 deadline Deadline for the permit signature
* uint8 v V param for the permit signature * uint8 v V param for the permit signature
@ -165,22 +134,20 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
*/ */
function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) { function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) {
( (
address assetToSwapTo, address collateralAsset,
uint256 repayAmount, uint256 collateralAmount,
uint256 rateMode, uint256 rateMode,
RepayMode repayMode,
uint256 permitAmount, uint256 permitAmount,
uint256 deadline, uint256 deadline,
uint8 v, uint8 v,
bytes32 r, bytes32 r,
bytes32 s bytes32 s
) = abi.decode(params, (address, uint256, uint256, RepayMode, uint256, uint256, uint8, bytes32, bytes32)); ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32));
return RepayParams( return RepayParams(
assetToSwapTo, collateralAsset,
repayAmount, collateralAmount,
rateMode, rateMode,
repayMode,
PermitSignature( PermitSignature(
permitAmount, permitAmount,
deadline, deadline,
@ -196,7 +163,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
* @param asset address of the asset * @param asset address of the asset
* @param user address * @param user address
*/ */
function _sendRepayLeftovers(address asset, address user) internal { function _sendLeftovers(address asset, address user) internal {
uint256 assetLeftover = IERC20(asset).balanceOf(address(this)); uint256 assetLeftover = IERC20(asset).balanceOf(address(this));
if (assetLeftover > 0) { if (assetLeftover > 0) {

View File

@ -259,10 +259,9 @@ export const buildLiquiditySwapParams = (
}; };
export const buildRepayAdapterParams = ( export const buildRepayAdapterParams = (
assetToSwapTo: tEthereumAddress, collateralAsset: tEthereumAddress,
repayAmount: BigNumberish, collateralAmount: BigNumberish,
rateMode: BigNumberish, rateMode: BigNumberish,
repayMode: BigNumberish,
permitAmount: BigNumberish, permitAmount: BigNumberish,
deadline: BigNumberish, deadline: BigNumberish,
v: BigNumberish, v: BigNumberish,
@ -276,11 +275,10 @@ export const buildRepayAdapterParams = (
'uint256', 'uint256',
'uint256', 'uint256',
'uint256', 'uint256',
'uint256',
'uint8', 'uint8',
'bytes32', 'bytes32',
'bytes32', 'bytes32',
], ],
[assetToSwapTo, repayAmount, rateMode, repayMode, permitAmount, deadline, v, r, s] [collateralAsset, collateralAmount, rateMode, permitAmount, deadline, v, r, s]
); );
}; };

View File

@ -2085,19 +2085,19 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
// Subtract the FL fee from the amount to be swapped 0,09% await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const flashLoanDebt = new BigNumber(expectedDaiAmount.toString())
.multipliedBy(1.0009)
.toFixed(0);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
expectedDaiAmount, liquidityToSwap,
1, 1,
0, 0,
0, 0,
0, 0,
0,
'0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000000000000000000000000000000' '0x0000000000000000000000000000000000000000000000000000000000000000'
); );
@ -2107,8 +2107,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
.connect(user) .connect(user)
.flashLoan( .flashLoan(
uniswapRepayAdapter.address, uniswapRepayAdapter.address,
[weth.address], [dai.address],
[flashloanAmount.toString()], [expectedDaiAmount.toString()],
[0], [0],
userAddress, userAddress,
params, params,
@ -2116,7 +2116,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
) )
) )
.to.emit(uniswapRepayAdapter, 'Swapped') .to.emit(uniswapRepayAdapter, 'Swapped')
.withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt);
const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address);
const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address);
@ -2170,9 +2170,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
const liquidityToSwap = amountWETHtoSwap; const liquidityToSwap = amountWETHtoSwap;
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
// Subtract the FL fee from the amount to be swapped 0,09%
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID;
const deadline = MAX_UINT_AMOUNT; const deadline = MAX_UINT_AMOUNT;
const nonce = (await aWETH._nonces(userAddress)).toNumber(); const nonce = (await aWETH._nonces(userAddress)).toNumber();
@ -2195,13 +2192,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
const flashLoanDebt = new BigNumber(expectedDaiAmount.toString())
.multipliedBy(1.0009)
.toFixed(0);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
expectedDaiAmount, liquidityToSwap,
1, 1,
0,
liquidityToSwap, liquidityToSwap,
deadline, deadline,
v, v,
@ -2214,8 +2214,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
.connect(user) .connect(user)
.flashLoan( .flashLoan(
uniswapRepayAdapter.address, uniswapRepayAdapter.address,
[weth.address], [dai.address],
[flashloanAmount.toString()], [expectedDaiAmount.toString()],
[0], [0],
userAddress, userAddress,
params, params,
@ -2223,7 +2223,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
) )
) )
.to.emit(uniswapRepayAdapter, 'Swapped') .to.emit(uniswapRepayAdapter, 'Swapped')
.withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt);
const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address);
const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address);
@ -2257,19 +2257,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
const liquidityToSwap = amountWETHtoSwap; const liquidityToSwap = amountWETHtoSwap;
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
// Subtract the FL fee from the amount to be swapped 0,09% await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
expectedDaiAmount, liquidityToSwap,
1, 1,
0, 0,
0, 0,
0, 0,
0,
'0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000000000000000000000000000000' '0x0000000000000000000000000000000000000000000000000000000000000000'
); );
@ -2278,8 +2274,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
uniswapRepayAdapter uniswapRepayAdapter
.connect(user) .connect(user)
.executeOperation( .executeOperation(
[weth.address], [dai.address],
[flashloanAmount.toString()], [expectedDaiAmount.toString()],
[0], [0],
userAddress, userAddress,
params params
@ -2309,19 +2305,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
const liquidityToSwap = amountWETHtoSwap; const liquidityToSwap = amountWETHtoSwap;
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
// Subtract the FL fee from the amount to be swapped 0,09% await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
expectedDaiAmount, liquidityToSwap,
1, 1,
0, 0,
0, 0,
0, 0,
0,
'0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000000000000000000000000000000' '0x0000000000000000000000000000000000000000000000000000000000000000'
); );
@ -2331,8 +2323,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
.connect(user) .connect(user)
.flashLoan( .flashLoan(
uniswapRepayAdapter.address, uniswapRepayAdapter.address,
[weth.address], [dai.address],
[flashloanAmount.toString()], [expectedDaiAmount.toString()],
[0], [0],
userAddress, userAddress,
params, params,
@ -2360,19 +2352,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
const liquidityToSwap = amountWETHtoSwap; const liquidityToSwap = amountWETHtoSwap;
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
// Subtract the FL fee from the amount to be swapped 0,09% await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
expectedDaiAmount, liquidityToSwap,
1, 1,
0, 0,
0, 0,
0, 0,
0,
'0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000000000000000000000000000000' '0x0000000000000000000000000000000000000000000000000000000000000000'
); );
@ -2382,8 +2370,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
.connect(user) .connect(user)
.flashLoan( .flashLoan(
uniswapRepayAdapter.address, uniswapRepayAdapter.address,
[weth.address], [dai.address],
[flashloanAmount.toString()], [expectedDaiAmount.toString()],
[0], [0],
userAddress, userAddress,
params, params,
@ -2408,22 +2396,18 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
// Open user Debt // Open user Debt
await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress);
await aWETH.connect(user).approve(uniswapRepayAdapter.address, amountWETHtoSwap);
// Subtract the FL fee from the amount to be swapped 0,09%
const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); const bigMaxAmountToSwap = amountWETHtoSwap.mul(2);
const flashloanAmount = new BigNumber(bigMaxAmountToSwap.toString()).div(1.0009).toFixed(0); await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
expectedDaiAmount, bigMaxAmountToSwap,
1, 1,
0, 0,
0, 0,
0, 0,
0,
'0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000000000000000000000000000000' '0x0000000000000000000000000000000000000000000000000000000000000000'
); );
@ -2433,8 +2417,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
.connect(user) .connect(user)
.flashLoan( .flashLoan(
uniswapRepayAdapter.address, uniswapRepayAdapter.address,
[weth.address], [dai.address],
[flashloanAmount.toString()], [expectedDaiAmount.toString()],
[0], [0],
userAddress, userAddress,
params, params,
@ -2482,26 +2466,27 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
const liquidityToSwap = amountWETHtoSwap; const liquidityToSwap = amountWETHtoSwap;
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
const userWethBalanceBefore = await weth.balanceOf(userAddress);
// Subtract the FL fee from the amount to be swapped 0,09% const actualWEthSwapped = new BigNumber(liquidityToSwap.toString())
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
const actualWEthSwapped = new BigNumber(flashloanAmount.toString())
.multipliedBy(0.995) .multipliedBy(0.995)
.toFixed(0); .toFixed(0);
const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped); const leftOverWeth = new BigNumber(liquidityToSwap.toString()).minus(actualWEthSwapped);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped);
const flashLoanDebt = new BigNumber(expectedDaiAmount.toString())
.multipliedBy(1.0009)
.toFixed(0);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
expectedDaiAmount, liquidityToSwap,
1, 1,
0, 0,
0, 0,
0, 0,
0,
'0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000000000000000000000000000000' '0x0000000000000000000000000000000000000000000000000000000000000000'
); );
@ -2511,8 +2496,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
.connect(user) .connect(user)
.flashLoan( .flashLoan(
uniswapRepayAdapter.address, uniswapRepayAdapter.address,
[weth.address], [dai.address],
[flashloanAmount.toString()], [expectedDaiAmount.toString()],
[0], [0],
userAddress, userAddress,
params, params,
@ -2520,13 +2505,14 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
) )
) )
.to.emit(uniswapRepayAdapter, 'Swapped') .to.emit(uniswapRepayAdapter, 'Swapped')
.withArgs(weth.address, dai.address, actualWEthSwapped.toString(), expectedDaiAmount); .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), flashLoanDebt);
const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address);
const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address);
const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress);
const userAEthBalance = await aWETH.balanceOf(userAddress); const userAEthBalance = await aWETH.balanceOf(userAddress);
const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address);
const userWethBalance = await weth.balanceOf(userAddress);
expect(adapterAEthBalance).to.be.eq(Zero); expect(adapterAEthBalance).to.be.eq(Zero);
expect(adapterWethBalance).to.be.eq(Zero); expect(adapterWethBalance).to.be.eq(Zero);
@ -2534,13 +2520,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount);
expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount);
expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.lt(userAEthBalanceBefore);
expect(userAEthBalance).to.be.gt(userAEthBalanceBefore.sub(liquidityToSwap)); expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(liquidityToSwap));
expect(userAEthBalance).to.be.gte( expect(userWethBalance).to.be.gte(userWethBalanceBefore.add(leftOverWeth.toString()));
userAEthBalanceBefore.sub(liquidityToSwap).add(leftOverWeth.toString())
);
}); });
it('should correctly swap tokens and repay the whole stable debt', async () => { it('should correctly swap tokens and repay the whole stable debt with no leftovers', async () => {
const { const {
users, users,
pool, pool,
@ -2580,19 +2564,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
// Subtract the FL fee from the amount to be swapped 0,09% // Add a % to repay on top of the debt
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); const amountToRepay = new BigNumber(expectedDaiAmount.toString())
.multipliedBy(1.1)
.toFixed(0);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
// Passed amount to repay is smaller than debt,
// but repayAllDebt flag is enabled so the whole debt should be paid
const amountToRepay = expectedDaiAmount.div(2);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
amountToRepay, liquidityToSwap,
1,
1, 1,
0, 0,
0, 0,
@ -2605,8 +2586,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
.connect(user) .connect(user)
.flashLoan( .flashLoan(
uniswapRepayAdapter.address, uniswapRepayAdapter.address,
[weth.address], [dai.address],
[flashloanAmount.toString()], [amountToRepay.toString()],
[0], [0],
userAddress, userAddress,
params, params,
@ -2628,7 +2609,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
}); });
it('should correctly swap tokens and repay the whole variable debt', async () => { it('should correctly swap tokens and repay the whole variable debt with no leftovers', async () => {
const { const {
users, users,
pool, pool,
@ -2670,20 +2651,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
// Subtract the FL fee from the amount to be swapped 0,09% // Add a % to repay on top of the debt
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); const amountToRepay = new BigNumber(expectedDaiAmount.toString())
.multipliedBy(1.1)
.toFixed(0);
await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap);
// Passed amount to repay is smaller than debt,
// but repayAllDebt flag is enabled so the whole debt should be paid
const amountToRepay = expectedDaiAmount.div(2);
const params = buildRepayAdapterParams( const params = buildRepayAdapterParams(
dai.address, weth.address,
amountToRepay, liquidityToSwap,
2, 2,
1,
0, 0,
0, 0,
0, 0,
@ -2695,8 +2673,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
.connect(user) .connect(user)
.flashLoan( .flashLoan(
uniswapRepayAdapter.address, uniswapRepayAdapter.address,
[weth.address], [dai.address],
[flashloanAmount.toString()], [amountToRepay.toString()],
[0], [0],
userAddress, userAddress,
params, params,
@ -2717,192 +2695,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.lt(userAEthBalanceBefore);
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
}); });
it('should swap and repay debt using all the collateral for a bigger debt', async () => {
const {
users,
pool,
weth,
aWETH,
oracle,
dai,
uniswapRepayAdapter,
helpersContract,
} = testEnv;
const user = users[0].signer;
const userAddress = users[0].address;
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
const daiPrice = await oracle.getAssetPrice(dai.address);
const expectedDaiAmount = await convertToCurrencyDecimals(
dai.address,
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
);
const userDebt = new BigNumber(expectedDaiAmount.toString()).multipliedBy(1.1).toFixed(0);
// Open user Debt
await pool.connect(user).borrow(dai.address, userDebt, 1, 0, userAddress);
const daiStableDebtTokenAddress = (
await helpersContract.getReserveTokensAddresses(dai.address)
).stableDebtTokenAddress;
const daiStableDebtContract = await getContract<StableDebtToken>(
eContractid.StableDebtToken,
daiStableDebtTokenAddress
);
const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress);
const liquidityToSwap = amountWETHtoSwap;
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
// Subtract the FL fee from the amount to be swapped 0,09%
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
const actualWEthSwapped = new BigNumber(flashloanAmount.toString())
.multipliedBy(0.995)
.toFixed(0);
// Remove other balance
await aWETH
.connect(user)
.transfer(users[1].address, userAEthBalanceBefore.sub(actualWEthSwapped));
await mockUniswapRouter.connect(user).setAmountToReturn(weth.address, expectedDaiAmount);
const params = buildRepayAdapterParams(
dai.address,
expectedDaiAmount,
1,
2,
0,
0,
0,
'0x0000000000000000000000000000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000000000000000000000000000000'
);
await pool
.connect(user)
.flashLoan(
uniswapRepayAdapter.address,
[weth.address],
[flashloanAmount.toString()],
[0],
userAddress,
params,
0
);
const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address);
const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address);
const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress);
const userAEthBalance = await aWETH.balanceOf(userAddress);
const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address);
expect(adapterAEthBalance).to.be.eq(Zero);
expect(adapterWethBalance).to.be.eq(Zero);
expect(adapterDaiBalance).to.be.eq(Zero);
expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount);
expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount);
expect(userAEthBalance).to.be.eq(Zero);
});
it('should swap and repay debt using all the collateral for a smaller debt', async () => {
const {
users,
pool,
weth,
aWETH,
oracle,
dai,
uniswapRepayAdapter,
helpersContract,
} = testEnv;
const user = users[0].signer;
const userAddress = users[0].address;
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
const daiPrice = await oracle.getAssetPrice(dai.address);
const expectedDaiAmount = await convertToCurrencyDecimals(
dai.address,
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
);
const userDebt = new BigNumber(expectedDaiAmount.toString()).multipliedBy(0.9).toFixed(0);
// Open user Debt
await pool.connect(user).borrow(dai.address, userDebt, 1, 0, userAddress);
const daiStableDebtTokenAddress = (
await helpersContract.getReserveTokensAddresses(dai.address)
).stableDebtTokenAddress;
const daiStableDebtContract = await getContract<StableDebtToken>(
eContractid.StableDebtToken,
daiStableDebtTokenAddress
);
const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress);
const liquidityToSwap = amountWETHtoSwap;
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
// Subtract the FL fee from the amount to be swapped 0,09%
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
const actualWEthSwapped = new BigNumber(flashloanAmount.toString())
.multipliedBy(0.995)
.toFixed(0);
// Remove other balance
await aWETH
.connect(user)
.transfer(users[1].address, userAEthBalanceBefore.sub(actualWEthSwapped));
await mockUniswapRouter.connect(user).setAmountToReturn(weth.address, expectedDaiAmount);
const params = buildRepayAdapterParams(
dai.address,
expectedDaiAmount,
1,
2,
0,
0,
0,
'0x0000000000000000000000000000000000000000000000000000000000000000',
'0x0000000000000000000000000000000000000000000000000000000000000000'
);
await pool
.connect(user)
.flashLoan(
uniswapRepayAdapter.address,
[weth.address],
[flashloanAmount.toString()],
[0],
userAddress,
params,
0
);
const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address);
const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address);
const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress);
const userAEthBalance = await aWETH.balanceOf(userAddress);
const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address);
expect(adapterAEthBalance).to.be.eq(Zero);
expect(adapterWethBalance).to.be.eq(Zero);
expect(adapterDaiBalance).to.be.eq(Zero); // Validate there are no leftovers
expect(userDaiStableDebtAmountBefore).to.be.gte(userDebt);
expect(userDaiStableDebtAmount).to.be.eq(Zero);
expect(userAEthBalance).to.be.eq(Zero);
});
}); });
}); });
}); });