mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Add uniswap adapters unit tests
This commit is contained in:
parent
7c51597282
commit
a05b75b467
20
contracts/interfaces/IUniswapV2Router02.sol
Normal file
20
contracts/interfaces/IUniswapV2Router02.sol
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// SPDX-License-Identifier: agpl-3.0
|
||||||
|
pragma solidity ^0.6.8;
|
||||||
|
|
||||||
|
interface IUniswapV2Router02 {
|
||||||
|
function swapExactTokensForTokens(
|
||||||
|
uint256 amountIn,
|
||||||
|
uint256 amountOutMin,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint256 deadline
|
||||||
|
) external returns (uint256[] memory amounts);
|
||||||
|
|
||||||
|
function swapTokensForExactTokens(
|
||||||
|
uint amountOut,
|
||||||
|
uint amountInMax,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external returns (uint256[] memory amounts);
|
||||||
|
}
|
53
contracts/mocks/swap/MockUniswapV2Router02.sol
Normal file
53
contracts/mocks/swap/MockUniswapV2Router02.sol
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-License-Identifier: agpl-3.0
|
||||||
|
pragma solidity ^0.6.8;
|
||||||
|
|
||||||
|
import {IUniswapV2Router02} from "../../interfaces/IUniswapV2Router02.sol";
|
||||||
|
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||||
|
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||||
|
|
||||||
|
contract MockUniswapV2Router02 is IUniswapV2Router02 {
|
||||||
|
uint256 internal _amountToReturn;
|
||||||
|
uint256 internal _amountToSwap;
|
||||||
|
|
||||||
|
function setAmountToReturn(uint256 amount) public {
|
||||||
|
_amountToReturn = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAmountToSwap(uint256 amount) public {
|
||||||
|
_amountToSwap = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapExactTokensForTokens(
|
||||||
|
uint256 amountIn,
|
||||||
|
uint256 /* amountOutMin */,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint256 /* deadline */
|
||||||
|
) external override returns (uint256[] memory amounts) {
|
||||||
|
IERC20(path[0]).transferFrom(msg.sender, address(this), amountIn);
|
||||||
|
|
||||||
|
MintableERC20(path[1]).mint(_amountToReturn);
|
||||||
|
IERC20(path[1]).transfer(to, _amountToReturn);
|
||||||
|
|
||||||
|
amounts = new uint[](path.length);
|
||||||
|
amounts[0] = amountIn;
|
||||||
|
amounts[1] = _amountToReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapTokensForExactTokens(
|
||||||
|
uint amountOut,
|
||||||
|
uint amountInMax,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint /* deadline */
|
||||||
|
) external override returns (uint256[] memory amounts) {
|
||||||
|
IERC20(path[0]).transferFrom(msg.sender, address(this), _amountToSwap);
|
||||||
|
|
||||||
|
MintableERC20(path[1]).mint(_amountToReturn);
|
||||||
|
IERC20(path[1]).transfer(to, _amountToReturn);
|
||||||
|
|
||||||
|
amounts = new uint[](path.length);
|
||||||
|
amounts[0] = _amountToSwap;
|
||||||
|
amounts[1] = _amountToReturn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,9 @@ import BigNumber from 'bignumber.js';
|
||||||
import {Ierc20Detailed} from '../types/Ierc20Detailed';
|
import {Ierc20Detailed} from '../types/Ierc20Detailed';
|
||||||
import {StableDebtToken} from '../types/StableDebtToken';
|
import {StableDebtToken} from '../types/StableDebtToken';
|
||||||
import {VariableDebtToken} from '../types/VariableDebtToken';
|
import {VariableDebtToken} from '../types/VariableDebtToken';
|
||||||
|
import {MockUniswapV2Router02} from '../types/MockUniswapV2Router02';
|
||||||
|
import {UniswapLiquiditySwapAdapter} from '../types/UniswapLiquiditySwapAdapter';
|
||||||
|
import {UniswapRepayAdapter} from '../types/UniswapRepayAdapter';
|
||||||
import {MockContract} from 'ethereum-waffle';
|
import {MockContract} from 'ethereum-waffle';
|
||||||
import {getReservesConfigByPool} from './configuration';
|
import {getReservesConfigByPool} from './configuration';
|
||||||
import {verifyContract} from './etherscan-verification';
|
import {verifyContract} from './etherscan-verification';
|
||||||
|
@ -47,7 +50,6 @@ const {
|
||||||
|
|
||||||
export type MockTokenMap = {[symbol: string]: MintableERC20};
|
export type MockTokenMap = {[symbol: string]: MintableERC20};
|
||||||
import {ZERO_ADDRESS} from './constants';
|
import {ZERO_ADDRESS} from './constants';
|
||||||
import {MockSwapAdapter} from '../types/MockSwapAdapter';
|
|
||||||
import {signTypedData_v4, TypedData} from 'eth-sig-util';
|
import {signTypedData_v4, TypedData} from 'eth-sig-util';
|
||||||
import {fromRpcSig, ECDSASignature} from 'ethereumjs-util';
|
import {fromRpcSig, ECDSASignature} from 'ethereumjs-util';
|
||||||
import {SignerWithAddress} from '../test/helpers/make-suite';
|
import {SignerWithAddress} from '../test/helpers/make-suite';
|
||||||
|
@ -256,6 +258,27 @@ export const deployChainlinkProxyPriceProvider = async (
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const deployMockUniswapRouter = async () =>
|
||||||
|
await deployContract<MockUniswapV2Router02>(eContractid.MockUniswapV2Router02, []);
|
||||||
|
|
||||||
|
export const deployUniswapLiquiditySwapAdapter = async (
|
||||||
|
addressesProvider: tEthereumAddress,
|
||||||
|
uniswapRouter: tEthereumAddress
|
||||||
|
) =>
|
||||||
|
await deployContract<UniswapLiquiditySwapAdapter>(eContractid.UniswapLiquiditySwapAdapter, [
|
||||||
|
addressesProvider,
|
||||||
|
uniswapRouter,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const deployUniswapRepayAdapter = async (
|
||||||
|
addressesProvider: tEthereumAddress,
|
||||||
|
uniswapRouter: tEthereumAddress
|
||||||
|
) =>
|
||||||
|
await deployContract<UniswapRepayAdapter>(eContractid.UniswapRepayAdapter, [
|
||||||
|
addressesProvider,
|
||||||
|
uniswapRouter,
|
||||||
|
]);
|
||||||
|
|
||||||
export const getChainlingProxyPriceProvider = async (address?: tEthereumAddress) =>
|
export const getChainlingProxyPriceProvider = async (address?: tEthereumAddress) =>
|
||||||
await getContract<MockAggregator>(
|
await getContract<MockAggregator>(
|
||||||
eContractid.ChainlinkProxyPriceProvider,
|
eContractid.ChainlinkProxyPriceProvider,
|
||||||
|
@ -321,8 +344,6 @@ export const deployWalletBalancerProvider = async (
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
export const deployMockSwapAdapter = async (addressesProvider: tEthereumAddress) =>
|
|
||||||
await deployContract<MockSwapAdapter>(eContractid.MockSwapAdapter, [addressesProvider]);
|
|
||||||
|
|
||||||
export const deployAaveProtocolTestHelpers = async (
|
export const deployAaveProtocolTestHelpers = async (
|
||||||
addressesProvider: tEthereumAddress,
|
addressesProvider: tEthereumAddress,
|
||||||
|
@ -548,11 +569,29 @@ export const getMockFlashLoanReceiver = async (address?: tEthereumAddress) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMockSwapAdapter = async (address?: tEthereumAddress) => {
|
export const getMockUniswapRouter = async (address?: tEthereumAddress) => {
|
||||||
return await getContract<MockSwapAdapter>(
|
return await getContract<MockUniswapV2Router02>(
|
||||||
eContractid.MockSwapAdapter,
|
eContractid.MockUniswapV2Router02,
|
||||||
address ||
|
address ||
|
||||||
(await getDb().get(`${eContractid.MockSwapAdapter}.${BRE.network.name}`).value()).address
|
(await getDb().get(`${eContractid.MockUniswapV2Router02}.${BRE.network.name}`).value())
|
||||||
|
.address
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUniswapLiquiditySwapAdapter = async (address?: tEthereumAddress) => {
|
||||||
|
return await getContract<UniswapLiquiditySwapAdapter>(
|
||||||
|
eContractid.UniswapLiquiditySwapAdapter,
|
||||||
|
address ||
|
||||||
|
(await getDb().get(`${eContractid.UniswapLiquiditySwapAdapter}.${BRE.network.name}`).value())
|
||||||
|
.address
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUniswapRepayAdapter = async (address?: tEthereumAddress) => {
|
||||||
|
return await getContract<UniswapLiquiditySwapAdapter>(
|
||||||
|
eContractid.UniswapRepayAdapter,
|
||||||
|
address ||
|
||||||
|
(await getDb().get(`${eContractid.UniswapRepayAdapter}.${BRE.network.name}`).value()).address
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ export enum eContractid {
|
||||||
LendingPoolCollateralManager = 'LendingPoolCollateralManager',
|
LendingPoolCollateralManager = 'LendingPoolCollateralManager',
|
||||||
InitializableAdminUpgradeabilityProxy = 'InitializableAdminUpgradeabilityProxy',
|
InitializableAdminUpgradeabilityProxy = 'InitializableAdminUpgradeabilityProxy',
|
||||||
MockFlashLoanReceiver = 'MockFlashLoanReceiver',
|
MockFlashLoanReceiver = 'MockFlashLoanReceiver',
|
||||||
MockSwapAdapter = 'MockSwapAdapter',
|
|
||||||
WalletBalanceProvider = 'WalletBalanceProvider',
|
WalletBalanceProvider = 'WalletBalanceProvider',
|
||||||
AToken = 'AToken',
|
AToken = 'AToken',
|
||||||
MockAToken = 'MockAToken',
|
MockAToken = 'MockAToken',
|
||||||
|
@ -56,6 +55,9 @@ export enum eContractid {
|
||||||
VariableDebtToken = 'VariableDebtToken',
|
VariableDebtToken = 'VariableDebtToken',
|
||||||
FeeProvider = 'FeeProvider',
|
FeeProvider = 'FeeProvider',
|
||||||
TokenDistributor = 'TokenDistributor',
|
TokenDistributor = 'TokenDistributor',
|
||||||
|
MockUniswapV2Router02 = 'MockUniswapV2Router02',
|
||||||
|
UniswapLiquiditySwapAdapter = 'UniswapLiquiditySwapAdapter',
|
||||||
|
UniswapRepayAdapter = 'UniswapRepayAdapter',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProtocolErrors {
|
export enum ProtocolErrors {
|
||||||
|
|
|
@ -19,8 +19,10 @@ import {
|
||||||
registerContractInJsonDb,
|
registerContractInJsonDb,
|
||||||
getPairsTokenAggregator,
|
getPairsTokenAggregator,
|
||||||
initReserves,
|
initReserves,
|
||||||
deployMockSwapAdapter,
|
|
||||||
deployLendingRateOracle,
|
deployLendingRateOracle,
|
||||||
|
deployMockUniswapRouter,
|
||||||
|
deployUniswapLiquiditySwapAdapter,
|
||||||
|
deployUniswapRepayAdapter,
|
||||||
} from '../helpers/contracts-helpers';
|
} from '../helpers/contracts-helpers';
|
||||||
import {Signer} from 'ethers';
|
import {Signer} from 'ethers';
|
||||||
import {TokenContractId, eContractid, tEthereumAddress, AavePools} from '../helpers/types';
|
import {TokenContractId, eContractid, tEthereumAddress, AavePools} from '../helpers/types';
|
||||||
|
@ -239,8 +241,23 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
|
||||||
const mockFlashLoanReceiver = await deployMockFlashLoanReceiver(addressesProvider.address);
|
const mockFlashLoanReceiver = await deployMockFlashLoanReceiver(addressesProvider.address);
|
||||||
await insertContractAddressInDb(eContractid.MockFlashLoanReceiver, mockFlashLoanReceiver.address);
|
await insertContractAddressInDb(eContractid.MockFlashLoanReceiver, mockFlashLoanReceiver.address);
|
||||||
|
|
||||||
const mockSwapAdapter = await deployMockSwapAdapter(addressesProvider.address);
|
const mockUniswapRouter = await deployMockUniswapRouter();
|
||||||
await insertContractAddressInDb(eContractid.MockSwapAdapter, mockSwapAdapter.address);
|
await insertContractAddressInDb(eContractid.MockUniswapV2Router02, mockUniswapRouter.address);
|
||||||
|
|
||||||
|
const UniswapLiquiditySwapAdapter = await deployUniswapLiquiditySwapAdapter(
|
||||||
|
addressesProvider.address,
|
||||||
|
mockUniswapRouter.address
|
||||||
|
);
|
||||||
|
await insertContractAddressInDb(
|
||||||
|
eContractid.UniswapLiquiditySwapAdapter,
|
||||||
|
UniswapLiquiditySwapAdapter.address
|
||||||
|
);
|
||||||
|
|
||||||
|
const UniswapRepayAdapter = await deployUniswapRepayAdapter(
|
||||||
|
addressesProvider.address,
|
||||||
|
mockUniswapRouter.address
|
||||||
|
);
|
||||||
|
await insertContractAddressInDb(eContractid.UniswapRepayAdapter, UniswapRepayAdapter.address);
|
||||||
|
|
||||||
await deployWalletBalancerProvider(addressesProvider.address);
|
await deployWalletBalancerProvider(addressesProvider.address);
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,9 @@ import {
|
||||||
getMintableErc20,
|
getMintableErc20,
|
||||||
getLendingPoolConfiguratorProxy,
|
getLendingPoolConfiguratorProxy,
|
||||||
getPriceOracle,
|
getPriceOracle,
|
||||||
getMockSwapAdapter,
|
|
||||||
getLendingPoolAddressesProviderRegistry,
|
getLendingPoolAddressesProviderRegistry,
|
||||||
|
getUniswapLiquiditySwapAdapter,
|
||||||
|
getUniswapRepayAdapter,
|
||||||
} from '../../helpers/contracts-helpers';
|
} from '../../helpers/contracts-helpers';
|
||||||
import {tEthereumAddress} from '../../helpers/types';
|
import {tEthereumAddress} from '../../helpers/types';
|
||||||
import {LendingPool} from '../../types/LendingPool';
|
import {LendingPool} from '../../types/LendingPool';
|
||||||
|
@ -25,8 +26,9 @@ import bignumberChai from 'chai-bignumber';
|
||||||
import {almostEqual} from './almost-equal';
|
import {almostEqual} from './almost-equal';
|
||||||
import {PriceOracle} from '../../types/PriceOracle';
|
import {PriceOracle} from '../../types/PriceOracle';
|
||||||
import {LendingPoolAddressesProvider} from '../../types/LendingPoolAddressesProvider';
|
import {LendingPoolAddressesProvider} from '../../types/LendingPoolAddressesProvider';
|
||||||
import {MockSwapAdapter} from '../../types/MockSwapAdapter';
|
|
||||||
import {LendingPoolAddressesProviderRegistry} from '../../types/LendingPoolAddressesProviderRegistry';
|
import {LendingPoolAddressesProviderRegistry} from '../../types/LendingPoolAddressesProviderRegistry';
|
||||||
|
import {UniswapLiquiditySwapAdapter} from '../../types/UniswapLiquiditySwapAdapter';
|
||||||
|
import {UniswapRepayAdapter} from '../../types/UniswapRepayAdapter';
|
||||||
chai.use(bignumberChai());
|
chai.use(bignumberChai());
|
||||||
chai.use(almostEqual());
|
chai.use(almostEqual());
|
||||||
|
|
||||||
|
@ -48,7 +50,8 @@ export interface TestEnv {
|
||||||
usdc: MintableERC20;
|
usdc: MintableERC20;
|
||||||
lend: MintableERC20;
|
lend: MintableERC20;
|
||||||
addressesProvider: LendingPoolAddressesProvider;
|
addressesProvider: LendingPoolAddressesProvider;
|
||||||
mockSwapAdapter: MockSwapAdapter;
|
uniswapLiquiditySwapAdapter: UniswapLiquiditySwapAdapter;
|
||||||
|
uniswapRepayAdapter: UniswapRepayAdapter;
|
||||||
registry: LendingPoolAddressesProviderRegistry;
|
registry: LendingPoolAddressesProviderRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +76,8 @@ const testEnv: TestEnv = {
|
||||||
usdc: {} as MintableERC20,
|
usdc: {} as MintableERC20,
|
||||||
lend: {} as MintableERC20,
|
lend: {} as MintableERC20,
|
||||||
addressesProvider: {} as LendingPoolAddressesProvider,
|
addressesProvider: {} as LendingPoolAddressesProvider,
|
||||||
mockSwapAdapter: {} as MockSwapAdapter,
|
uniswapLiquiditySwapAdapter: {} as UniswapLiquiditySwapAdapter,
|
||||||
|
uniswapRepayAdapter: {} as UniswapRepayAdapter,
|
||||||
registry: {} as LendingPoolAddressesProviderRegistry,
|
registry: {} as LendingPoolAddressesProviderRegistry,
|
||||||
} as TestEnv;
|
} as TestEnv;
|
||||||
|
|
||||||
|
@ -135,7 +139,8 @@ export async function initializeMakeSuite() {
|
||||||
testEnv.lend = await getMintableErc20(lendAddress);
|
testEnv.lend = await getMintableErc20(lendAddress);
|
||||||
testEnv.weth = await getMintableErc20(wethAddress);
|
testEnv.weth = await getMintableErc20(wethAddress);
|
||||||
|
|
||||||
testEnv.mockSwapAdapter = await getMockSwapAdapter();
|
testEnv.uniswapLiquiditySwapAdapter = await getUniswapLiquiditySwapAdapter();
|
||||||
|
testEnv.uniswapRepayAdapter = await getUniswapRepayAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
|
export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
|
||||||
|
|
|
@ -275,7 +275,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('SwapBorrowRateMode', async () => {
|
it('SwapBorrowRateMode', async () => {
|
||||||
const {pool, weth, dai, usdc, users, configurator, mockSwapAdapter} = testEnv;
|
const {pool, weth, dai, usdc, users, configurator} = testEnv;
|
||||||
const user = users[1];
|
const user = users[1];
|
||||||
const amountWETHToDeposit = parseEther('10');
|
const amountWETHToDeposit = parseEther('10');
|
||||||
const amountDAIToDeposit = parseEther('120');
|
const amountDAIToDeposit = parseEther('120');
|
||||||
|
|
805
test/uniswapAdapters.spec.ts
Normal file
805
test/uniswapAdapters.spec.ts
Normal file
|
@ -0,0 +1,805 @@
|
||||||
|
import {makeSuite, TestEnv} from './helpers/make-suite';
|
||||||
|
import {
|
||||||
|
convertToCurrencyDecimals,
|
||||||
|
deployUniswapLiquiditySwapAdapter,
|
||||||
|
deployUniswapRepayAdapter,
|
||||||
|
getContract,
|
||||||
|
getMockUniswapRouter,
|
||||||
|
} from '../helpers/contracts-helpers';
|
||||||
|
import {MockUniswapV2Router02} from '../types/MockUniswapV2Router02';
|
||||||
|
import {Zero} from '@ethersproject/constants';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import {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';
|
||||||
|
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('UniswapLiquiditySwapAdapter', () => {
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('should deploy with correct parameters', async () => {
|
||||||
|
const {addressesProvider} = testEnv;
|
||||||
|
await deployUniswapLiquiditySwapAdapter(
|
||||||
|
addressesProvider.address,
|
||||||
|
mockUniswapRouter.address
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should revert if not valid addresses provider', async () => {
|
||||||
|
expect(
|
||||||
|
deployUniswapLiquiditySwapAdapter(mockUniswapRouter.address, mockUniswapRouter.address)
|
||||||
|
).to.be.reverted;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('executeOperation', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
const {users, weth, dai, 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);
|
||||||
|
|
||||||
|
// Make a deposit for user
|
||||||
|
await weth.mint(parseEther('100'));
|
||||||
|
await weth.approve(pool.address, parseEther('100'));
|
||||||
|
await pool.deposit(weth.address, parseEther('100'), userAddress, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly swap tokens and deposit the out tokens in the pool', async () => {
|
||||||
|
const {users, weth, oracle, dai, aDai, aEth, pool, uniswapLiquiditySwapAdapter} = 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)
|
||||||
|
);
|
||||||
|
|
||||||
|
await mockUniswapRouter.setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
// User will swap liquidity 10 aEth to aDai
|
||||||
|
const liquidityToSwap = parseEther('10');
|
||||||
|
await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap);
|
||||||
|
const userAEthBalanceBefore = await aEth.balanceOf(userAddress);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
// 0,5% slippage
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256'],
|
||||||
|
[dai.address, userAddress, 50]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapLiquiditySwapAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.to.emit(uniswapLiquiditySwapAdapter, 'Swapped')
|
||||||
|
.withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount);
|
||||||
|
|
||||||
|
const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address);
|
||||||
|
const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address);
|
||||||
|
const adapterDaiAllowance = await dai.allowance(
|
||||||
|
uniswapLiquiditySwapAdapter.address,
|
||||||
|
userAddress
|
||||||
|
);
|
||||||
|
const userADaiBalance = await aDai.balanceOf(userAddress);
|
||||||
|
const userAEthBalance = await aEth.balanceOf(userAddress);
|
||||||
|
|
||||||
|
expect(adapterWethBalance).to.be.eq(Zero);
|
||||||
|
expect(adapterDaiBalance).to.be.eq(Zero);
|
||||||
|
expect(adapterDaiAllowance).to.be.eq(Zero);
|
||||||
|
expect(userADaiBalance).to.be.eq(expectedDaiAmount);
|
||||||
|
expect(userAEthBalance).to.be.lt(userAEthBalanceBefore);
|
||||||
|
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work correctly with tokens of different decimals', async () => {
|
||||||
|
const {
|
||||||
|
users,
|
||||||
|
usdc,
|
||||||
|
oracle,
|
||||||
|
dai,
|
||||||
|
aDai,
|
||||||
|
uniswapLiquiditySwapAdapter,
|
||||||
|
pool,
|
||||||
|
deployer,
|
||||||
|
} = testEnv;
|
||||||
|
const user = users[0].signer;
|
||||||
|
const userAddress = users[0].address;
|
||||||
|
|
||||||
|
const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10');
|
||||||
|
const liquidity = await convertToCurrencyDecimals(usdc.address, '20000');
|
||||||
|
|
||||||
|
// Provide liquidity
|
||||||
|
await usdc.mint(liquidity);
|
||||||
|
await usdc.approve(pool.address, liquidity);
|
||||||
|
await pool.deposit(usdc.address, liquidity, deployer.address, 0);
|
||||||
|
|
||||||
|
// Make a deposit for user
|
||||||
|
await usdc.connect(user).mint(amountUSDCtoSwap);
|
||||||
|
await usdc.connect(user).approve(pool.address, amountUSDCtoSwap);
|
||||||
|
await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0);
|
||||||
|
|
||||||
|
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||||
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
|
||||||
|
// usdc 6
|
||||||
|
const collateralDecimals = (await usdc.decimals()).toString();
|
||||||
|
const principalDecimals = (await dai.decimals()).toString();
|
||||||
|
|
||||||
|
const expectedDaiAmount = await convertToCurrencyDecimals(
|
||||||
|
dai.address,
|
||||||
|
new BigNumber(amountUSDCtoSwap.toString())
|
||||||
|
.times(
|
||||||
|
new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||||
|
)
|
||||||
|
.div(
|
||||||
|
new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||||
|
)
|
||||||
|
.toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const aUsdcData = await pool.getReserveData(usdc.address);
|
||||||
|
const aUsdc = await getContract<AToken>(eContractid.AToken, aUsdcData.aTokenAddress);
|
||||||
|
const aUsdcBalance = await aUsdc.balanceOf(userAddress);
|
||||||
|
await aUsdc.connect(user).approve(uniswapLiquiditySwapAdapter.address, aUsdcBalance);
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(amountUSDCtoSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
// 0,5% slippage
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256'],
|
||||||
|
[dai.address, userAddress, 50]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapLiquiditySwapAdapter.address,
|
||||||
|
usdc.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.to.emit(uniswapLiquiditySwapAdapter, 'Swapped')
|
||||||
|
.withArgs(usdc.address, dai.address, flashloanAmount.toString(), expectedDaiAmount);
|
||||||
|
|
||||||
|
const adapterUsdcBalance = await usdc.balanceOf(uniswapLiquiditySwapAdapter.address);
|
||||||
|
const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address);
|
||||||
|
const adapterDaiAllowance = await dai.allowance(
|
||||||
|
uniswapLiquiditySwapAdapter.address,
|
||||||
|
userAddress
|
||||||
|
);
|
||||||
|
const aDaiBalance = await aDai.balanceOf(userAddress);
|
||||||
|
|
||||||
|
expect(adapterUsdcBalance).to.be.eq(Zero);
|
||||||
|
expect(adapterDaiBalance).to.be.eq(Zero);
|
||||||
|
expect(adapterDaiAllowance).to.be.eq(Zero);
|
||||||
|
expect(aDaiBalance).to.be.eq(expectedDaiAmount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should revert if slippage param is not inside limits', async () => {
|
||||||
|
const {users, pool, weth, oracle, dai, aEth, uniswapLiquiditySwapAdapter} = 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(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap);
|
||||||
|
|
||||||
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
const expectedDaiAmount = await convertToCurrencyDecimals(
|
||||||
|
dai.address,
|
||||||
|
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
// User will swap liquidity 10 aEth to aDai
|
||||||
|
const liquidityToSwap = parseEther('10');
|
||||||
|
await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap);
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
// 30% slippage
|
||||||
|
const params1 = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256'],
|
||||||
|
[dai.address, userAddress, 3000]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 0,05% slippage
|
||||||
|
const params2 = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256'],
|
||||||
|
[dai.address, userAddress, 5]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapLiquiditySwapAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params1,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE');
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapLiquiditySwapAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params2,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should revert when swap exceed slippage', async () => {
|
||||||
|
const {users, weth, oracle, dai, aEth, pool, uniswapLiquiditySwapAdapter} = 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(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap);
|
||||||
|
|
||||||
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
const expectedDaiAmount = await convertToCurrencyDecimals(
|
||||||
|
dai.address,
|
||||||
|
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1,5% slippage
|
||||||
|
const returnedDaiAmountWithBigSlippage = new BigNumber(expectedDaiAmount.toString())
|
||||||
|
.multipliedBy(0.985)
|
||||||
|
.toFixed(0);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(returnedDaiAmountWithBigSlippage);
|
||||||
|
|
||||||
|
// User will swap liquidity 10 aEth to aDai
|
||||||
|
const liquidityToSwap = parseEther('10');
|
||||||
|
await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap);
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
// 0,5% slippage
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256'],
|
||||||
|
[dai.address, userAddress, 50]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapLiquiditySwapAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('UniswapRepayAdapter', () => {
|
||||||
|
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', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
const {users, weth, dai, 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);
|
||||||
|
|
||||||
|
// Make a deposit for user
|
||||||
|
await weth.mint(parseEther('100'));
|
||||||
|
await weth.approve(pool.address, parseEther('100'));
|
||||||
|
await pool.deposit(weth.address, parseEther('100'), userAddress, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly swap tokens and repay debt', async () => {
|
||||||
|
const {
|
||||||
|
users,
|
||||||
|
pool,
|
||||||
|
weth,
|
||||||
|
aEth,
|
||||||
|
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 aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
const userAEthBalanceBefore = await aEth.balanceOf(userAddress);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
||||||
|
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.to.emit(uniswapRepayAdapter, 'Swapped')
|
||||||
|
.withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount);
|
||||||
|
|
||||||
|
const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address);
|
||||||
|
const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address);
|
||||||
|
const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress);
|
||||||
|
const userAEthBalance = await aEth.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 with the specified rate mode', async () => {
|
||||||
|
const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aEth} = 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 aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
const userAEthBalanceBefore = await aEth.balanceOf(userAddress);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
||||||
|
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.reverted;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should revert if there is not debt to repay', async () => {
|
||||||
|
const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aEth} = 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 aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
const userAEthBalanceBefore = await aEth.balanceOf(userAddress);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
||||||
|
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.reverted;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should revert when the received amount is less than expected', async () => {
|
||||||
|
const {users, pool, weth, oracle, dai, aEth, uniswapRepayAdapter, deployer} = 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 aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
const insufficientOutput = new BigNumber(expectedDaiAmount.toString())
|
||||||
|
.multipliedBy(0.985)
|
||||||
|
.toFixed(0);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(insufficientOutput);
|
||||||
|
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
||||||
|
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should revert when max amount allowed to swap is bigger than max slippage', async () => {
|
||||||
|
const {users, pool, weth, oracle, dai, aEth, 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 aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const bigMaxAmountToSwap = amountWETHtoSwap.mul(2);
|
||||||
|
const flashloanAmount = new BigNumber(bigMaxAmountToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
||||||
|
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('maxAmountToSwap exceed max slippage');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should swap tokens, repay debt and deposit in pool the left over', async () => {
|
||||||
|
const {
|
||||||
|
users,
|
||||||
|
pool,
|
||||||
|
weth,
|
||||||
|
aEth,
|
||||||
|
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 aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
const userAEthBalanceBefore = await aEth.balanceOf(userAddress);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
const actualWEthSwapped = new BigNumber(flashloanAmount.toString())
|
||||||
|
.multipliedBy(0.995)
|
||||||
|
.toFixed(0);
|
||||||
|
|
||||||
|
const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(actualWEthSwapped);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
||||||
|
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.to.emit(uniswapRepayAdapter, 'Swapped')
|
||||||
|
.withArgs(weth.address, dai.address, actualWEthSwapped.toString(), expectedDaiAmount);
|
||||||
|
|
||||||
|
const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address);
|
||||||
|
const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address);
|
||||||
|
const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress);
|
||||||
|
const userAEthBalance = await aEth.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.gt(userAEthBalanceBefore.sub(liquidityToSwap));
|
||||||
|
expect(userAEthBalance).to.be.gte(
|
||||||
|
userAEthBalanceBefore.sub(liquidityToSwap).add(leftOverWeth.toString())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should swap tokens, repay debt and transfer to user the left over', async () => {
|
||||||
|
const {
|
||||||
|
users,
|
||||||
|
pool,
|
||||||
|
weth,
|
||||||
|
aEth,
|
||||||
|
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 aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
const userAEthBalanceBefore = await aEth.balanceOf(userAddress);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
const actualWEthSwapped = new BigNumber(flashloanAmount.toString())
|
||||||
|
.multipliedBy(0.995)
|
||||||
|
.toFixed(0);
|
||||||
|
|
||||||
|
const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(actualWEthSwapped);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const wethBalanceBefore = await weth.balanceOf(userAddress);
|
||||||
|
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
||||||
|
[dai.address, userAddress, 1, expectedDaiAmount, 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
weth.address,
|
||||||
|
flashloanAmount.toString(),
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.to.emit(uniswapRepayAdapter, 'Swapped')
|
||||||
|
.withArgs(weth.address, dai.address, actualWEthSwapped.toString(), expectedDaiAmount);
|
||||||
|
|
||||||
|
const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address);
|
||||||
|
const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address);
|
||||||
|
const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress);
|
||||||
|
const userAEthBalance = await aEth.balanceOf(userAddress);
|
||||||
|
const wethBalance = await weth.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.gt(userAEthBalanceBefore.sub(liquidityToSwap));
|
||||||
|
expect(wethBalance).to.be.eq(wethBalanceBefore.add(leftOverWeth.toString()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user