From 611605eebf51a0be5fbd02f8a4fced68f0375072 Mon Sep 17 00:00:00 2001 From: David Racero <canillas.mail@gmail.com> Date: Wed, 13 Jan 2021 15:11:39 +0100 Subject: [PATCH] Fix mockup base tests. Separate uniswap adapter tests into separate files. --- .../mocks/swap/MockUniswapV2Router02.sol | 56 +- test/__setup.spec.ts | 24 +- test/uniswapAdapters.base.spec.ts | 227 +++ ... => uniswapAdapters.liquiditySwap.spec.ts} | 1591 +---------------- test/uniswapAdapters.repay.spec.ts | 1431 +++++++++++++++ 5 files changed, 1747 insertions(+), 1582 deletions(-) create mode 100644 test/uniswapAdapters.base.spec.ts rename test/{uniswapAdapters.spec.ts => uniswapAdapters.liquiditySwap.spec.ts} (53%) create mode 100644 test/uniswapAdapters.repay.spec.ts diff --git a/contracts/mocks/swap/MockUniswapV2Router02.sol b/contracts/mocks/swap/MockUniswapV2Router02.sol index 7f38bf92..b7fd3f80 100644 --- a/contracts/mocks/swap/MockUniswapV2Router02.sol +++ b/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; -import {IUniswapV2Router02} from "../../interfaces/IUniswapV2Router02.sol"; +import {IUniswapV2Router02} from '../../interfaces/IUniswapV2Router02.sol'; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {MintableERC20} from '../tokens/MintableERC20.sol'; @@ -22,7 +22,7 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { function swapExactTokensForTokens( uint256 amountIn, - uint256 /* amountOutMin */, + uint256, /* amountOutMin */ address[] calldata path, address to, uint256 /* deadline */ @@ -32,50 +32,74 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { MintableERC20(path[1]).mint(_amountToReturn[path[0]]); IERC20(path[1]).transfer(to, _amountToReturn[path[0]]); - amounts = new uint[](path.length); + amounts = new uint256[](path.length); amounts[0] = amountIn; amounts[1] = _amountToReturn[path[0]]; } function swapTokensForExactTokens( - uint amountOut, - uint /* amountInMax */, + uint256 amountOut, + uint256, /* amountInMax */ address[] calldata path, address to, - uint /* deadline */ + uint256 /* deadline */ ) external override returns (uint256[] memory amounts) { IERC20(path[0]).transferFrom(msg.sender, address(this), _amountToSwap[path[0]]); MintableERC20(path[1]).mint(amountOut); IERC20(path[1]).transfer(to, amountOut); - amounts = new uint[](path.length); + amounts = new uint256[](path.length); amounts[0] = _amountToSwap[path[0]]; amounts[1] = amountOut; } - function setAmountOut(uint amountIn, address reserveIn, address reserveOut, uint amountOut) public { + function setAmountOut( + uint256 amountIn, + address reserveIn, + address reserveOut, + uint256 amountOut + ) public { _amountsOut[reserveIn][reserveOut][amountIn] = amountOut; } - function setAmountIn(uint amountOut, address reserveIn, address reserveOut, uint amountIn) public { + function setAmountIn( + uint256 amountOut, + address reserveIn, + address reserveOut, + uint256 amountIn + ) public { _amountsIn[reserveIn][reserveOut][amountOut] = amountIn; } - function setDefaultMockValue(uint value) public { + function setDefaultMockValue(uint256 value) public { defaultMockValue = value; } - function getAmountsOut(uint amountIn, address[] calldata path) external view override returns (uint[] memory) { - uint256[] memory amounts = new uint256[](2); + function getAmountsOut(uint256 amountIn, address[] calldata path) + external + view + override + returns (uint256[] memory) + { + uint256[] memory amounts = new uint256[](path.length); amounts[0] = amountIn; - amounts[1] = _amountsOut[path[0]][path[1]][amountIn] > 0 ? _amountsOut[path[0]][path[1]][amountIn] : defaultMockValue; + 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] > 0 ? _amountsIn[path[0]][path[1]][amountOut] : defaultMockValue; + function getAmountsIn(uint256 amountOut, address[] calldata path) + external + view + override + returns (uint256[] memory) + { + uint256[] memory amounts = new uint256[](path.length); + amounts[0] = _amountsIn[path[0]][path[1]][amountOut] > 0 + ? _amountsIn[path[0]][path[1]][amountOut] + : defaultMockValue; amounts[1] = amountOut; return amounts; } diff --git a/test/__setup.spec.ts b/test/__setup.spec.ts index 89f535e4..de3478af 100644 --- a/test/__setup.spec.ts +++ b/test/__setup.spec.ts @@ -29,7 +29,12 @@ import { import { Signer } from 'ethers'; import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../helpers/types'; import { MintableERC20 } from '../types/MintableERC20'; -import { ConfigNames, getReservesConfigByPool, getTreasuryAddress, loadPoolConfig } from '../helpers/configuration'; +import { + ConfigNames, + getReservesConfigByPool, + getTreasuryAddress, + loadPoolConfig, +} from '../helpers/configuration'; import { initializeMakeSuite } from './helpers/make-suite'; import { @@ -38,10 +43,7 @@ import { setInitialMarketRatesInRatesOracleByHelper, } from '../helpers/oracles-helpers'; import { DRE, waitForTx } from '../helpers/misc-utils'; -import { - initReservesByHelper, - configureReservesByHelper, -} from '../helpers/init-helpers'; +import { initReservesByHelper, configureReservesByHelper } from '../helpers/init-helpers'; import AaveConfig from '../markets/aave'; import { ZERO_ADDRESS } from '../helpers/constants'; import { @@ -216,13 +218,15 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const treasuryAddress = await getTreasuryAddress(config); - await initReservesByHelper(reservesParams, allReservesAddresses, admin, treasuryAddress, ZERO_ADDRESS, false); - await configureReservesByHelper( + await initReservesByHelper( reservesParams, allReservesAddresses, - testHelpers, - admin + admin, + treasuryAddress, + ZERO_ADDRESS, + false ); + await configureReservesByHelper(reservesParams, allReservesAddresses, testHelpers, admin); const collateralManager = await deployLendingPoolCollateralManager(); await waitForTx( @@ -238,6 +242,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const UniswapLiquiditySwapAdapter = await deployUniswapLiquiditySwapAdapter([ addressesProvider.address, mockUniswapRouter.address, + mockTokens.WETH.address, ]); await insertContractAddressInDb( eContractid.UniswapLiquiditySwapAdapter, @@ -247,6 +252,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const UniswapRepayAdapter = await deployUniswapRepayAdapter([ addressesProvider.address, mockUniswapRouter.address, + mockTokens.WETH.address, ]); await insertContractAddressInDb(eContractid.UniswapRepayAdapter, UniswapRepayAdapter.address); diff --git a/test/uniswapAdapters.base.spec.ts b/test/uniswapAdapters.base.spec.ts new file mode 100644 index 00000000..80f76725 --- /dev/null +++ b/test/uniswapAdapters.base.spec.ts @@ -0,0 +1,227 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; +import { getMockUniswapRouter } from '../helpers/contracts-getters'; +import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +import BigNumber from 'bignumber.js'; +import { evmRevert, evmSnapshot } from '../helpers/misc-utils'; +import { ethers } from 'ethers'; +import { USD_ADDRESS } from '../helpers/constants'; +const { parseEther } = ethers.utils; + +const { expect } = require('chai'); + +makeSuite('Uniswap adapters', (testEnv: TestEnv) => { + let mockUniswapRouter: MockUniswapV2Router02; + let evmSnapshotId: string; + + before(async () => { + mockUniswapRouter = await getMockUniswapRouter(); + }); + + beforeEach(async () => { + evmSnapshotId = await evmSnapshot(); + }); + + afterEach(async () => { + await evmRevert(evmSnapshotId); + }); + + describe('BaseUniswapAdapter', () => { + describe('getAmountsOut', () => { + it('should return the estimated amountOut and prices for the asset swap', async () => { + const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; + + const amountIn = parseEther('1'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.sub(flashloanPremium); + + const wethPrice = await oracle.getAssetPrice(weth.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountToSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const outPerInPrice = amountToSwap + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(expectedDaiAmount.mul(parseEther('1'))); + const ethUsdValue = amountIn + .mul(wethPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + const daiUsdValue = expectedDaiAmount + .mul(daiPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountOut( + amountToSwap, + weth.address, + dai.address, + expectedDaiAmount + ); + + const result = await uniswapLiquiditySwapAdapter.getAmountsOut( + amountIn, + weth.address, + dai.address + ); + + expect(result['0']).to.be.eq(expectedDaiAmount); + expect(result['1']).to.be.eq(outPerInPrice); + expect(result['2']).to.be.eq(ethUsdValue); + expect(result['3']).to.be.eq(daiUsdValue); + }); + it('should work correctly with different decimals', async () => { + const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; + + const amountIn = parseEther('10'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.sub(flashloanPremium); + + const aavePrice = await oracle.getAssetPrice(aave.address); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const expectedUSDCAmount = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) + ); + + const outPerInPrice = amountToSwap + .mul(parseEther('1')) + .mul('1000000') // usdc 6 decimals + .div(expectedUSDCAmount.mul(parseEther('1'))); + + const aaveUsdValue = amountIn + .mul(aavePrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + const usdcUsdValue = expectedUSDCAmount + .mul(usdcPrice) + .div('1000000') // usdc 6 decimals + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountOut( + amountToSwap, + aave.address, + usdc.address, + expectedUSDCAmount + ); + + const result = await uniswapLiquiditySwapAdapter.getAmountsOut( + amountIn, + aave.address, + usdc.address + ); + + expect(result['0']).to.be.eq(expectedUSDCAmount); + expect(result['1']).to.be.eq(outPerInPrice); + expect(result['2']).to.be.eq(aaveUsdValue); + expect(result['3']).to.be.eq(usdcUsdValue); + }); + }); + + describe('getAmountsIn', () => { + it('should return the estimated required amountIn for the asset swap', async () => { + const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; + + const amountIn = parseEther('1'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.add(flashloanPremium); + + const wethPrice = await oracle.getAssetPrice(weth.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const amountOut = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountIn.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const inPerOutPrice = amountOut + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(amountToSwap.mul(parseEther('1'))); + + const ethUsdValue = amountToSwap + .mul(wethPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + const daiUsdValue = amountOut + .mul(daiPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn); + + const result = await uniswapLiquiditySwapAdapter.getAmountsIn( + amountOut, + weth.address, + dai.address + ); + + expect(result['0']).to.be.eq(amountToSwap); + expect(result['1']).to.be.eq(inPerOutPrice); + expect(result['2']).to.be.eq(ethUsdValue); + expect(result['3']).to.be.eq(daiUsdValue); + }); + it('should work correctly with different decimals', async () => { + const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; + + const amountIn = parseEther('10'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.add(flashloanPremium); + + const aavePrice = await oracle.getAssetPrice(aave.address); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const amountOut = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) + ); + + const inPerOutPrice = amountOut + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(amountToSwap.mul('1000000')); // usdc 6 decimals + + const aaveUsdValue = amountToSwap + .mul(aavePrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + const usdcUsdValue = amountOut + .mul(usdcPrice) + .div('1000000') // usdc 6 decimals + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountIn(amountOut, aave.address, usdc.address, amountIn); + + const result = await uniswapLiquiditySwapAdapter.getAmountsIn( + amountOut, + aave.address, + usdc.address + ); + + expect(result['0']).to.be.eq(amountToSwap); + expect(result['1']).to.be.eq(inPerOutPrice); + expect(result['2']).to.be.eq(aaveUsdValue); + expect(result['3']).to.be.eq(usdcUsdValue); + }); + }); + }); +}); diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.liquiditySwap.spec.ts similarity index 53% rename from test/uniswapAdapters.spec.ts rename to test/uniswapAdapters.liquiditySwap.spec.ts index e5dcb7f5..65882c0e 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.liquiditySwap.spec.ts @@ -5,13 +5,9 @@ import { buildPermitParams, getSignatureFromTypedData, buildLiquiditySwapParams, - buildRepayAdapterParams, } from '../helpers/contracts-helpers'; import { getMockUniswapRouter } from '../helpers/contracts-getters'; -import { - deployUniswapLiquiditySwapAdapter, - deployUniswapRepayAdapter, -} from '../helpers/contracts-deployments'; +import { deployUniswapLiquiditySwapAdapter } from '../helpers/contracts-deployments'; import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; import { Zero } from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; @@ -19,9 +15,8 @@ import { DRE, evmRevert, evmSnapshot } from '../helpers/misc-utils'; import { ethers } from 'ethers'; import { eContractid } from '../helpers/types'; import { AToken } from '../types/AToken'; -import { StableDebtToken } from '../types/StableDebtToken'; import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; -import { MAX_UINT_AMOUNT, USD_ADDRESS } from '../helpers/constants'; +import { MAX_UINT_AMOUNT } from '../helpers/constants'; const { parseEther } = ethers.utils; const { expect } = require('chai'); @@ -42,218 +37,25 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await evmRevert(evmSnapshotId); }); - describe('BaseUniswapAdapter', () => { - describe('getAmountsOut', () => { - it('should return the estimated amountOut and prices for the asset swap', async () => { - const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; - - const amountIn = parseEther('1'); - const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.sub(flashloanPremium); - - const wethPrice = await oracle.getAssetPrice(weth.address); - const daiPrice = await oracle.getAssetPrice(dai.address); - const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountToSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const outPerInPrice = amountToSwap - .mul(parseEther('1')) - .mul(parseEther('1')) - .div(expectedDaiAmount.mul(parseEther('1'))); - const ethUsdValue = amountIn - .mul(wethPrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - const daiUsdValue = expectedDaiAmount - .mul(daiPrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - - await mockUniswapRouter.setAmountOut( - amountToSwap, - weth.address, - dai.address, - expectedDaiAmount - ); - - const result = await uniswapLiquiditySwapAdapter.getAmountsOut( - amountIn, - weth.address, - dai.address - ); - - expect(result['0']).to.be.eq(expectedDaiAmount); - expect(result['1']).to.be.eq(outPerInPrice); - expect(result['2']).to.be.eq(ethUsdValue); - expect(result['3']).to.be.eq(daiUsdValue); - }); - it('should work correctly with different decimals', async () => { - const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; - - const amountIn = parseEther('10'); - const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.sub(flashloanPremium); - - const aavePrice = await oracle.getAssetPrice(aave.address); - const usdcPrice = await oracle.getAssetPrice(usdc.address); - const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - - const expectedUSDCAmount = await convertToCurrencyDecimals( - usdc.address, - new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) - ); - - const outPerInPrice = amountToSwap - .mul(parseEther('1')) - .mul('1000000') // usdc 6 decimals - .div(expectedUSDCAmount.mul(parseEther('1'))); - - const aaveUsdValue = amountIn - .mul(aavePrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - - const usdcUsdValue = expectedUSDCAmount - .mul(usdcPrice) - .div('1000000') // usdc 6 decimals - .mul(usdPrice) - .div(parseEther('1')); - - await mockUniswapRouter.setAmountOut( - amountToSwap, - aave.address, - usdc.address, - expectedUSDCAmount - ); - - const result = await uniswapLiquiditySwapAdapter.getAmountsOut( - amountIn, - aave.address, - usdc.address - ); - - expect(result['0']).to.be.eq(expectedUSDCAmount); - expect(result['1']).to.be.eq(outPerInPrice); - expect(result['2']).to.be.eq(aaveUsdValue); - expect(result['3']).to.be.eq(usdcUsdValue); - }); - }); - - describe('getAmountsIn', () => { - it('should return the estimated required amountIn for the asset swap', async () => { - const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; - - const amountIn = parseEther('1'); - const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.add(flashloanPremium); - - const wethPrice = await oracle.getAssetPrice(weth.address); - const daiPrice = await oracle.getAssetPrice(dai.address); - const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - - const amountOut = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountIn.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const inPerOutPrice = amountOut - .mul(parseEther('1')) - .mul(parseEther('1')) - .div(amountToSwap.mul(parseEther('1'))); - - const ethUsdValue = amountToSwap - .mul(wethPrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - const daiUsdValue = amountOut - .mul(daiPrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - - await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn); - - const result = await uniswapLiquiditySwapAdapter.getAmountsIn( - amountOut, - weth.address, - dai.address - ); - - expect(result['0']).to.be.eq(amountToSwap); - expect(result['1']).to.be.eq(inPerOutPrice); - expect(result['2']).to.be.eq(ethUsdValue); - expect(result['3']).to.be.eq(daiUsdValue); - }); - it('should work correctly with different decimals', async () => { - const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; - - const amountIn = parseEther('10'); - const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.add(flashloanPremium); - - const aavePrice = await oracle.getAssetPrice(aave.address); - const usdcPrice = await oracle.getAssetPrice(usdc.address); - const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - - const amountOut = await convertToCurrencyDecimals( - usdc.address, - new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) - ); - - const inPerOutPrice = amountOut - .mul(parseEther('1')) - .mul(parseEther('1')) - .div(amountToSwap.mul('1000000')); // usdc 6 decimals - - const aaveUsdValue = amountToSwap - .mul(aavePrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - - const usdcUsdValue = amountOut - .mul(usdcPrice) - .div('1000000') // usdc 6 decimals - .mul(usdPrice) - .div(parseEther('1')); - - await mockUniswapRouter.setAmountIn(amountOut, aave.address, usdc.address, amountIn); - - const result = await uniswapLiquiditySwapAdapter.getAmountsIn( - amountOut, - aave.address, - usdc.address - ); - - expect(result['0']).to.be.eq(amountToSwap); - expect(result['1']).to.be.eq(inPerOutPrice); - expect(result['2']).to.be.eq(aaveUsdValue); - expect(result['3']).to.be.eq(usdcUsdValue); - }); - }); - }); - describe('UniswapLiquiditySwapAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { - const { addressesProvider } = testEnv; + const { addressesProvider, weth } = testEnv; await deployUniswapLiquiditySwapAdapter([ addressesProvider.address, mockUniswapRouter.address, + weth.address, ]); }); it('should revert if not valid addresses provider', async () => { + const { weth } = testEnv; expect( - deployUniswapLiquiditySwapAdapter([mockUniswapRouter.address, mockUniswapRouter.address]) + deployUniswapLiquiditySwapAdapter([ + mockUniswapRouter.address, + mockUniswapRouter.address, + weth.address, + ]) ).to.be.reverted; }); }); @@ -1367,7 +1169,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') @@ -1445,7 +1248,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r, s, }, - ] + ], + [false] ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') @@ -1493,7 +1297,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); @@ -1511,7 +1316,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); @@ -1529,7 +1335,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); @@ -1541,7 +1348,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [dai.address], [amountWETHtoSwap], [expectedDaiAmount], - [] + [], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); @@ -1559,7 +1367,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); @@ -1597,7 +1406,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('minAmountOut exceed max slippage'); }); @@ -1679,7 +1489,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ); const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); @@ -1818,7 +1629,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: aUsdcr, s: aUsdcs, }, - ] + ], + [false] ); const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); @@ -1884,7 +1696,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') @@ -1969,7 +1782,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r, s, }, - ] + ], + [false] ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') @@ -1994,1341 +1808,4 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); }); - - describe('UniswapRepayAdapter', () => { - beforeEach(async () => { - const { users, weth, dai, usdc, aave, pool, deployer } = testEnv; - const userAddress = users[0].address; - - // Provide liquidity - await dai.mint(parseEther('20000')); - await dai.approve(pool.address, parseEther('20000')); - await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); - - const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '2000000'); - await usdc.mint(usdcLiquidity); - await usdc.approve(pool.address, usdcLiquidity); - await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); - - await weth.mint(parseEther('100')); - await weth.approve(pool.address, parseEther('100')); - await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); - - await aave.mint(parseEther('1000000')); - await aave.approve(pool.address, parseEther('1000000')); - await pool.deposit(aave.address, parseEther('1000000'), deployer.address, 0); - - // Make a deposit for user - await weth.mint(parseEther('1000')); - await weth.approve(pool.address, parseEther('1000')); - await pool.deposit(weth.address, parseEther('1000'), userAddress, 0); - - await aave.mint(parseEther('1000000')); - await aave.approve(pool.address, parseEther('1000000')); - await pool.deposit(aave.address, parseEther('1000000'), userAddress, 0); - - await usdc.mint(usdcLiquidity); - await usdc.approve(pool.address, usdcLiquidity); - await pool.deposit(usdc.address, usdcLiquidity, userAddress, 0); - }); - - describe('constructor', () => { - it('should deploy with correct parameters', async () => { - const { addressesProvider } = testEnv; - await deployUniswapRepayAdapter([addressesProvider.address, mockUniswapRouter.address]); - }); - - it('should revert if not valid addresses provider', async () => { - expect(deployUniswapRepayAdapter([mockUniswapRouter.address, mockUniswapRouter.address])).to - .be.reverted; - }); - }); - - describe('executeOperation', () => { - it('should correctly swap tokens and repay 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.0009) - .toFixed(0); - - await mockUniswapRouter.setAmountIn( - flashLoanDebt, - weth.address, - dai.address, - liquidityToSwap - ); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ) - .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); - - 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); - - 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.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly swap tokens and repay debt with permit', 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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; - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; - const deadline = MAX_UINT_AMOUNT; - const nonce = (await aWETH._nonces(userAddress)).toNumber(); - const msgParams = buildPermitParams( - chainId, - aWETH.address, - '1', - await aWETH.name(), - userAddress, - uniswapRepayAdapter.address, - nonce, - deadline, - liquidityToSwap.toString() - ); - - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; - if (!ownerPrivateKey) { - throw new Error('INVALID_OWNER_PK'); - } - - const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.0009) - .toFixed(0); - - await mockUniswapRouter.setAmountIn( - flashLoanDebt, - weth.address, - dai.address, - liquidityToSwap - ); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - liquidityToSwap, - deadline, - v, - r, - s - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ) - .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); - - 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); - - 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.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should revert if caller not lending pool', async () => { - const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - uniswapRepayAdapter - .connect(user) - .executeOperation( - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params - ) - ).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL'); - }); - - it('should revert if there is not debt to repay with the specified rate mode', async () => { - const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - await weth.connect(user).mint(amountWETHtoSwap); - await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ).to.be.reverted; - }); - - it('should revert if there is not debt to repay', async () => { - const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - await weth.connect(user).mint(amountWETHtoSwap); - await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ).to.be.reverted; - }); - - it('should revert when max amount allowed to swap is bigger than max slippage', async () => { - const { users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter } = 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); - await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); - - 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, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); - }); - - it('should swap, repay debt and pull the needed ATokens leaving no leftovers', 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); - const userWethBalanceBefore = await weth.balanceOf(userAddress); - - const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) - .multipliedBy(0.995) - .toFixed(0); - - 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, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ) - .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), flashLoanDebt); - - 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); - const userWethBalance = await weth.balanceOf(userAddress); - - 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.lt(userAEthBalanceBefore); - 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', 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); - - // 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); - - // Add a % to repay on top of the debt - const amountToRepay = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); - await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [amountToRepay.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.eq(Zero); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly swap tokens and repay the whole variable 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); - - const daiStableVariableTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).variableDebtTokenAddress; - - const daiVariableDebtContract = await getContract<StableDebtToken>( - eContractid.VariableDebtToken, - daiStableVariableTokenAddress - ); - - const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( - userAddress - ); - - // 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); - - // Add a % to repay on top of the debt - const amountToRepay = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); - await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 2, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [amountToRepay.toString()], - [0], - userAddress, - params, - 0 - ); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiVariableDebtAmount = await daiVariableDebtContract.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(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiVariableDebtAmount).to.be.eq(Zero); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly repay debt using the same asset as collateral', async () => { - const { users, pool, aDai, dai, uniswapRepayAdapter, helpersContract } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - // Add deposit for user - await dai.mint(parseEther('20')); - await dai.approve(pool.address, parseEther('20')); - await pool.deposit(dai.address, parseEther('20'), userAddress, 0); - - const amountCollateralToSwap = parseEther('10'); - const debtAmount = parseEther('10'); - - // Open user Debt - await pool.connect(user).borrow(dai.address, debtAmount, 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 flashLoanDebt = new BigNumber(amountCollateralToSwap.toString()) - .multipliedBy(1.0009) - .toFixed(0); - - await aDai.connect(user).approve(uniswapRepayAdapter.address, flashLoanDebt); - const userADaiBalanceBefore = await aDai.balanceOf(userAddress); - const userDaiBalanceBefore = await dai.balanceOf(userAddress); - - const params = buildRepayAdapterParams( - dai.address, - amountCollateralToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [amountCollateralToSwap.toString()], - [0], - userAddress, - params, - 0 - ); - - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userADaiBalance = await aDai.balanceOf(userAddress); - const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); - const userDaiBalance = await dai.balanceOf(userAddress); - - expect(adapterADaiBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); - expect(userDaiStableDebtAmount).to.be.lt(debtAmount); - expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); - expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(flashLoanDebt)); - expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); - }); - }); - - describe('swapAndRepay', () => { - it('should correctly swap tokens and repay 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); - - await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); - - await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }); - - 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); - - 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.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly swap tokens and repay debt with permit', 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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; - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); - - await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); - - const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; - const deadline = MAX_UINT_AMOUNT; - const nonce = (await aWETH._nonces(userAddress)).toNumber(); - const msgParams = buildPermitParams( - chainId, - aWETH.address, - '1', - await aWETH.name(), - userAddress, - uniswapRepayAdapter.address, - nonce, - deadline, - liquidityToSwap.toString() - ); - - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; - if (!ownerPrivateKey) { - throw new Error('INVALID_OWNER_PK'); - } - - const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { - amount: liquidityToSwap, - deadline, - v, - r, - s, - }); - - 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); - - 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.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should revert if there is not debt to repay', async () => { - const { users, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; - const user = users[0].signer; - - 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 liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); - - await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); - - await expect( - uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }) - ).to.be.reverted; - }); - - it('should revert when max amount allowed to swap is bigger than max slippage', async () => { - const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); - await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); - - await mockUniswapRouter.setDefaultMockValue(bigMaxAmountToSwap); - - await expect( - uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, bigMaxAmountToSwap, expectedDaiAmount, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }) - ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); - }); - - it('should swap, repay debt and pull the needed ATokens leaving no leftovers', 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); - const userWethBalanceBefore = await weth.balanceOf(userAddress); - - const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) - .multipliedBy(0.995) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); - - await mockUniswapRouter.setDefaultMockValue(actualWEthSwapped); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }); - - 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); - const userWethBalance = await weth.balanceOf(userAddress); - - 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.lt(userAEthBalanceBefore); - 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', 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); - - // 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); - - // Add a % to repay on top of the debt - const amountToRepay = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); - await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, amountToRepay, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }); - - 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.eq(Zero); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly swap tokens and repay the whole variable 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) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); - - const daiStableVariableTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).variableDebtTokenAddress; - - const daiVariableDebtContract = await getContract<StableDebtToken>( - eContractid.VariableDebtToken, - daiStableVariableTokenAddress - ); - - const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( - userAddress - ); - - // 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); - - // Add a % to repay on top of the debt - const amountToRepay = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); - await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, amountToRepay, 2, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiVariableDebtAmount = await daiVariableDebtContract.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(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiVariableDebtAmount).to.be.eq(Zero); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly repay debt using the same asset as collateral', async () => { - const { users, pool, dai, uniswapRepayAdapter, helpersContract, aDai } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - // Add deposit for user - await dai.mint(parseEther('20')); - await dai.approve(pool.address, parseEther('20')); - await pool.deposit(dai.address, parseEther('20'), userAddress, 0); - - const amountCollateralToSwap = parseEther('10'); - - const debtAmount = parseEther('10'); - - // Open user Debt - await pool.connect(user).borrow(dai.address, debtAmount, 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); - - await aDai.connect(user).approve(uniswapRepayAdapter.address, amountCollateralToSwap); - const userADaiBalanceBefore = await aDai.balanceOf(userAddress); - const userDaiBalanceBefore = await dai.balanceOf(userAddress); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay( - dai.address, - dai.address, - amountCollateralToSwap, - amountCollateralToSwap, - 1, - { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - } - ); - - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userADaiBalance = await aDai.balanceOf(userAddress); - const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); - const userDaiBalance = await dai.balanceOf(userAddress); - - expect(adapterADaiBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); - expect(userDaiStableDebtAmount).to.be.lt(debtAmount); - expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); - expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(amountCollateralToSwap)); - expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); - }); - }); - }); }); diff --git a/test/uniswapAdapters.repay.spec.ts b/test/uniswapAdapters.repay.spec.ts new file mode 100644 index 00000000..10b12a04 --- /dev/null +++ b/test/uniswapAdapters.repay.spec.ts @@ -0,0 +1,1431 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { + convertToCurrencyDecimals, + getContract, + buildPermitParams, + getSignatureFromTypedData, + buildLiquiditySwapParams, + buildRepayAdapterParams, +} from '../helpers/contracts-helpers'; +import { getMockUniswapRouter } from '../helpers/contracts-getters'; +import { + deployUniswapLiquiditySwapAdapter, + deployUniswapRepayAdapter, +} from '../helpers/contracts-deployments'; +import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +import { Zero } from '@ethersproject/constants'; +import BigNumber from 'bignumber.js'; +import { DRE, evmRevert, evmSnapshot } from '../helpers/misc-utils'; +import { ethers } from 'ethers'; +import { eContractid } from '../helpers/types'; +import { StableDebtToken } from '../types/StableDebtToken'; +import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; +import { MAX_UINT_AMOUNT } from '../helpers/constants'; +const { parseEther } = ethers.utils; + +const { expect } = require('chai'); + +makeSuite('Uniswap adapters', (testEnv: TestEnv) => { + let mockUniswapRouter: MockUniswapV2Router02; + let evmSnapshotId: string; + + before(async () => { + mockUniswapRouter = await getMockUniswapRouter(); + }); + + beforeEach(async () => { + evmSnapshotId = await evmSnapshot(); + }); + + afterEach(async () => { + await evmRevert(evmSnapshotId); + }); + + describe('UniswapRepayAdapter', () => { + beforeEach(async () => { + const { users, weth, dai, usdc, aave, pool, deployer } = testEnv; + const userAddress = users[0].address; + + // Provide liquidity + await dai.mint(parseEther('20000')); + await dai.approve(pool.address, parseEther('20000')); + await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + + const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '2000000'); + await usdc.mint(usdcLiquidity); + await usdc.approve(pool.address, usdcLiquidity); + await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); + + await weth.mint(parseEther('100')); + await weth.approve(pool.address, parseEther('100')); + await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); + + await aave.mint(parseEther('1000000')); + await aave.approve(pool.address, parseEther('1000000')); + await pool.deposit(aave.address, parseEther('1000000'), deployer.address, 0); + + // Make a deposit for user + await weth.mint(parseEther('1000')); + await weth.approve(pool.address, parseEther('1000')); + await pool.deposit(weth.address, parseEther('1000'), userAddress, 0); + + await aave.mint(parseEther('1000000')); + await aave.approve(pool.address, parseEther('1000000')); + await pool.deposit(aave.address, parseEther('1000000'), userAddress, 0); + + await usdc.mint(usdcLiquidity); + await usdc.approve(pool.address, usdcLiquidity); + await pool.deposit(usdc.address, usdcLiquidity, userAddress, 0); + }); + + describe('constructor', () => { + it('should deploy with correct parameters', async () => { + const { addressesProvider, weth } = testEnv; + await deployUniswapRepayAdapter([ + addressesProvider.address, + mockUniswapRouter.address, + weth.address, + ]); + }); + + it('should revert if not valid addresses provider', async () => { + const { weth } = testEnv; + expect( + deployUniswapRepayAdapter([ + mockUniswapRouter.address, + mockUniswapRouter.address, + weth.address, + ]) + ).to.be.reverted; + }); + }); + + describe('executeOperation', () => { + it('should correctly swap tokens and repay 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + liquidityToSwap + ); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); + + 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); + + 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.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay debt with permit', 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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; + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapRepayAdapter.address, + nonce, + deadline, + liquidityToSwap.toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + liquidityToSwap + ); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + liquidityToSwap, + deadline, + v, + r, + s + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); + + 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); + + 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.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should revert if caller not lending pool', async () => { + const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + uniswapRepayAdapter + .connect(user) + .executeOperation( + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params + ) + ).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL'); + }); + + it('should revert if there is not debt to repay with the specified rate mode', async () => { + const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await weth.connect(user).mint(amountWETHtoSwap); + await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ).to.be.reverted; + }); + + it('should revert if there is not debt to repay', async () => { + const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await weth.connect(user).mint(amountWETHtoSwap); + await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ).to.be.reverted; + }); + + it('should revert when max amount allowed to swap is bigger than max slippage', async () => { + const { users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter } = 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); + + 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, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); + }); + + it('should swap, repay debt and pull the needed ATokens leaving no leftovers', 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); + const userWethBalanceBefore = await weth.balanceOf(userAddress); + + const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) + .multipliedBy(0.995) + .toFixed(0); + + 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, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), flashLoanDebt); + + 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); + const userWethBalance = await weth.balanceOf(userAddress); + + 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.lt(userAEthBalanceBefore); + 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', 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); + + // 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); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [amountToRepay.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.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay the whole variable 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const daiStableVariableTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).variableDebtTokenAddress; + + const daiVariableDebtContract = await getContract<StableDebtToken>( + eContractid.VariableDebtToken, + daiStableVariableTokenAddress + ); + + const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( + userAddress + ); + + // 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); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 2, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [amountToRepay.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiVariableDebtAmount = await daiVariableDebtContract.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(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiVariableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly repay debt using the same asset as collateral', async () => { + const { users, pool, aDai, dai, uniswapRepayAdapter, helpersContract } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + // Add deposit for user + await dai.mint(parseEther('20')); + await dai.approve(pool.address, parseEther('20')); + await pool.deposit(dai.address, parseEther('20'), userAddress, 0); + + const amountCollateralToSwap = parseEther('10'); + const debtAmount = parseEther('10'); + + // Open user Debt + await pool.connect(user).borrow(dai.address, debtAmount, 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 flashLoanDebt = new BigNumber(amountCollateralToSwap.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await aDai.connect(user).approve(uniswapRepayAdapter.address, flashLoanDebt); + const userADaiBalanceBefore = await aDai.balanceOf(userAddress); + const userDaiBalanceBefore = await dai.balanceOf(userAddress); + + const params = buildRepayAdapterParams( + dai.address, + amountCollateralToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [amountCollateralToSwap.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userADaiBalance = await aDai.balanceOf(userAddress); + const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); + const userDaiBalance = await dai.balanceOf(userAddress); + + expect(adapterADaiBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); + expect(userDaiStableDebtAmount).to.be.lt(debtAmount); + expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); + expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(flashLoanDebt)); + expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); + }); + }); + + describe('swapAndRepay', () => { + it('should correctly swap tokens and repay 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + expectedDaiAmount, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + 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); + + 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.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay debt with permit', 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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; + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapRepayAdapter.address, + nonce, + deadline, + liquidityToSwap.toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + expectedDaiAmount, + 1, + { + amount: liquidityToSwap, + deadline, + v, + r, + s, + }, + false + ); + + 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); + + 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.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should revert if there is not debt to repay', async () => { + const { users, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; + const user = users[0].signer; + + 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 liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + await expect( + uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + expectedDaiAmount, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ) + ).to.be.reverted; + }); + + it('should revert when max amount allowed to swap is bigger than max slippage', async () => { + const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); + + await mockUniswapRouter.setDefaultMockValue(bigMaxAmountToSwap); + + await expect( + uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + bigMaxAmountToSwap, + expectedDaiAmount, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ) + ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); + }); + + it('should swap, repay debt and pull the needed ATokens leaving no leftovers', 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); + const userWethBalanceBefore = await weth.balanceOf(userAddress); + + const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) + .multipliedBy(0.995) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); + + await mockUniswapRouter.setDefaultMockValue(actualWEthSwapped); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + expectedDaiAmount, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + 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); + const userWethBalance = await weth.balanceOf(userAddress); + + 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.lt(userAEthBalanceBefore); + 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', 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 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); + + // 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); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + amountToRepay, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + 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.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay the whole variable 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) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const daiStableVariableTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).variableDebtTokenAddress; + + const daiVariableDebtContract = await getContract<StableDebtToken>( + eContractid.VariableDebtToken, + daiStableVariableTokenAddress + ); + + const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( + userAddress + ); + + // 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); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + amountToRepay, + 2, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiVariableDebtAmount = await daiVariableDebtContract.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(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiVariableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly repay debt using the same asset as collateral', async () => { + const { users, pool, dai, uniswapRepayAdapter, helpersContract, aDai } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + // Add deposit for user + await dai.mint(parseEther('20')); + await dai.approve(pool.address, parseEther('20')); + await pool.deposit(dai.address, parseEther('20'), userAddress, 0); + + const amountCollateralToSwap = parseEther('10'); + + const debtAmount = parseEther('10'); + + // Open user Debt + await pool.connect(user).borrow(dai.address, debtAmount, 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); + + await aDai.connect(user).approve(uniswapRepayAdapter.address, amountCollateralToSwap); + const userADaiBalanceBefore = await aDai.balanceOf(userAddress); + const userDaiBalanceBefore = await dai.balanceOf(userAddress); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + dai.address, + dai.address, + amountCollateralToSwap, + amountCollateralToSwap, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userADaiBalance = await aDai.balanceOf(userAddress); + const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); + const userDaiBalance = await dai.balanceOf(userAddress); + + expect(adapterADaiBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); + expect(userDaiStableDebtAmount).to.be.lt(debtAmount); + expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); + expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(amountCollateralToSwap)); + expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); + }); + }); + }); +});