mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
- Added tests of repayWithCollateral(), only for self-liquidation.
This commit is contained in:
parent
2cbb1f5714
commit
3aa0dbc570
21
contracts/interfaces/ISwapAdapter.sol
Normal file
21
contracts/interfaces/ISwapAdapter.sol
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-License-Identifier: agpl-3.0
|
||||||
|
pragma solidity ^0.6.8;
|
||||||
|
|
||||||
|
interface ISwapAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Swaps an `amountToSwap` of an asset to another, approving a `fundsDestination` to pull the funds
|
||||||
|
* @param assetToSwapFrom Origin asset
|
||||||
|
* @param assetToSwapTo Destination asset
|
||||||
|
* @param amountToSwap How much `assetToSwapFrom` needs to be swapped
|
||||||
|
* @param fundsDestination Address that will be pulling the swapped funds
|
||||||
|
* @param params Additional variadic field to include extra params
|
||||||
|
*/
|
||||||
|
function executeOperation(
|
||||||
|
address assetToSwapFrom,
|
||||||
|
address assetToSwapTo,
|
||||||
|
uint256 amountToSwap,
|
||||||
|
address fundsDestination,
|
||||||
|
bytes calldata params
|
||||||
|
) external;
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ import {Helpers} from '../libraries/helpers/Helpers.sol';
|
||||||
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
||||||
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
||||||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
||||||
import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol';
|
import {ISwapAdapter} from '../interfaces/ISwapAdapter.sol';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title LendingPoolLiquidationManager contract
|
* @title LendingPoolLiquidationManager contract
|
||||||
|
@ -393,12 +393,14 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
|
|
||||||
vars.collateralAtoken.burn(user, receiver, vars.maxCollateralToLiquidate);
|
vars.collateralAtoken.burn(user, receiver, vars.maxCollateralToLiquidate);
|
||||||
|
|
||||||
|
address principalAToken = debtReserve.aTokenAddress;
|
||||||
|
|
||||||
// Notifies the receiver to proceed, sending as param the underlying already transferred
|
// Notifies the receiver to proceed, sending as param the underlying already transferred
|
||||||
IFlashLoanReceiver(receiver).executeOperation(
|
ISwapAdapter(receiver).executeOperation(
|
||||||
collateral,
|
collateral,
|
||||||
address(vars.collateralAtoken),
|
principal,
|
||||||
vars.maxCollateralToLiquidate,
|
vars.maxCollateralToLiquidate,
|
||||||
0,
|
address(this),
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -406,11 +408,11 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
|
||||||
debtReserve.updateCumulativeIndexesAndTimestamp();
|
debtReserve.updateCumulativeIndexesAndTimestamp();
|
||||||
debtReserve.updateInterestRates(
|
debtReserve.updateInterestRates(
|
||||||
principal,
|
principal,
|
||||||
debtReserve.aTokenAddress,
|
principalAToken,
|
||||||
vars.actualAmountToLiquidate,
|
vars.actualAmountToLiquidate,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
IERC20(principal).transferFrom(receiver, debtReserve.aTokenAddress, vars.actualAmountToLiquidate);
|
IERC20(principal).transferFrom(receiver, principalAToken, vars.actualAmountToLiquidate);
|
||||||
|
|
||||||
if (vars.userVariableDebt >= vars.actualAmountToLiquidate) {
|
if (vars.userVariableDebt >= vars.actualAmountToLiquidate) {
|
||||||
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
|
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
|
||||||
|
|
43
contracts/mocks/flashloan/MockSwapAdapter.sol
Normal file
43
contracts/mocks/flashloan/MockSwapAdapter.sol
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// SPDX-License-Identifier: agpl-3.0
|
||||||
|
pragma solidity ^0.6.8;
|
||||||
|
|
||||||
|
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||||
|
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
|
||||||
|
import {ISwapAdapter} from '../../interfaces/ISwapAdapter.sol';
|
||||||
|
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||||
|
|
||||||
|
contract MockSwapAdapter is ISwapAdapter {
|
||||||
|
|
||||||
|
uint256 amountToReturn;
|
||||||
|
ILendingPoolAddressesProvider public addressesProvider;
|
||||||
|
|
||||||
|
event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount);
|
||||||
|
|
||||||
|
constructor(ILendingPoolAddressesProvider provider) public {
|
||||||
|
addressesProvider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAmountToReturn(uint256 amount) public {
|
||||||
|
amountToReturn = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeOperation(
|
||||||
|
address assetToSwapFrom,
|
||||||
|
address assetToSwapTo,
|
||||||
|
uint256 amountToSwap,
|
||||||
|
address fundsDestination,
|
||||||
|
bytes calldata params
|
||||||
|
) external override {
|
||||||
|
params;
|
||||||
|
IERC20(assetToSwapFrom).transfer(address(1), amountToSwap); // We don't want to keep funds here
|
||||||
|
MintableERC20(assetToSwapTo).mint(amountToReturn);
|
||||||
|
IERC20(assetToSwapTo).approve(fundsDestination, amountToReturn);
|
||||||
|
|
||||||
|
emit Swapped(assetToSwapFrom, assetToSwapTo, amountToSwap, amountToReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function burnAsset(IERC20 asset, uint256 amount) public {
|
||||||
|
uint256 amountToBurn = (amount == type(uint256).max) ? asset.balanceOf(address(this)) : amount;
|
||||||
|
asset.transfer(address(0), amountToBurn);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ 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 { MockSwapAdapter } from '../types/MockSwapAdapter';
|
||||||
|
|
||||||
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
|
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
|
||||||
const currentNetwork = BRE.network.name;
|
const currentNetwork = BRE.network.name;
|
||||||
|
@ -212,6 +213,11 @@ export const deployMockFlashLoanReceiver = async (addressesProvider: tEthereumAd
|
||||||
addressesProvider,
|
addressesProvider,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const deployMockSwapAdapter = async (addressesProvider: tEthereumAddress) =>
|
||||||
|
await deployContract<MockSwapAdapter>(eContractid.MockSwapAdapter, [
|
||||||
|
addressesProvider,
|
||||||
|
]);
|
||||||
|
|
||||||
export const deployWalletBalancerProvider = async (addressesProvider: tEthereumAddress) =>
|
export const deployWalletBalancerProvider = async (addressesProvider: tEthereumAddress) =>
|
||||||
await deployContract<WalletBalanceProvider>(eContractid.WalletBalanceProvider, [
|
await deployContract<WalletBalanceProvider>(eContractid.WalletBalanceProvider, [
|
||||||
addressesProvider,
|
addressesProvider,
|
||||||
|
@ -387,6 +393,15 @@ export const getMockFlashLoanReceiver = async (address?: tEthereumAddress) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getMockSwapAdapter = async (address?: tEthereumAddress) => {
|
||||||
|
return await getContract<MockSwapAdapter>(
|
||||||
|
eContractid.MockSwapAdapter,
|
||||||
|
address ||
|
||||||
|
(await getDb().get(`${eContractid.MockSwapAdapter}.${BRE.network.name}`).value())
|
||||||
|
.address
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const getLendingRateOracle = async (address?: tEthereumAddress) => {
|
export const getLendingRateOracle = async (address?: tEthereumAddress) => {
|
||||||
return await getContract<LendingRateOracle>(
|
return await getContract<LendingRateOracle>(
|
||||||
eContractid.LendingRateOracle,
|
eContractid.LendingRateOracle,
|
||||||
|
|
|
@ -32,6 +32,7 @@ export enum eContractid {
|
||||||
LendingPoolLiquidationManager = 'LendingPoolLiquidationManager',
|
LendingPoolLiquidationManager = 'LendingPoolLiquidationManager',
|
||||||
InitializableAdminUpgradeabilityProxy = 'InitializableAdminUpgradeabilityProxy',
|
InitializableAdminUpgradeabilityProxy = 'InitializableAdminUpgradeabilityProxy',
|
||||||
MockFlashLoanReceiver = 'MockFlashLoanReceiver',
|
MockFlashLoanReceiver = 'MockFlashLoanReceiver',
|
||||||
|
MockSwapAdapter = 'MockSwapAdapter',
|
||||||
WalletBalanceProvider = 'WalletBalanceProvider',
|
WalletBalanceProvider = 'WalletBalanceProvider',
|
||||||
AToken = 'AToken',
|
AToken = 'AToken',
|
||||||
MockAToken = 'MockAToken',
|
MockAToken = 'MockAToken',
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"types-gen": "typechain --target ethers-v5 --outDir ./types './artifacts/*.json'",
|
"types-gen": "typechain --target ethers-v5 --outDir ./types './artifacts/*.json'",
|
||||||
"test": "buidler test",
|
"test": "buidler test",
|
||||||
"test-scenarios": "buidler test test/__setup.spec.ts test/scenario.spec.ts",
|
"test-scenarios": "buidler test test/__setup.spec.ts test/scenario.spec.ts",
|
||||||
|
"test-repay-with-collateral": "buidler test test/__setup.spec.ts test/repay-with-collateral.spec.ts",
|
||||||
"dev:coverage": "buidler coverage",
|
"dev:coverage": "buidler coverage",
|
||||||
"dev:deployment": "buidler dev-deployment",
|
"dev:deployment": "buidler dev-deployment",
|
||||||
"dev:deployExample": "buidler deploy-Example",
|
"dev:deployExample": "buidler deploy-Example",
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
deployStableDebtToken,
|
deployStableDebtToken,
|
||||||
deployVariableDebtToken,
|
deployVariableDebtToken,
|
||||||
deployGenericAToken,
|
deployGenericAToken,
|
||||||
|
deployMockSwapAdapter,
|
||||||
} from '../helpers/contracts-helpers';
|
} from '../helpers/contracts-helpers';
|
||||||
import {LendingPoolAddressesProvider} from '../types/LendingPoolAddressesProvider';
|
import {LendingPoolAddressesProvider} from '../types/LendingPoolAddressesProvider';
|
||||||
import {ContractTransaction, Signer} from 'ethers';
|
import {ContractTransaction, Signer} from 'ethers';
|
||||||
|
@ -503,6 +504,9 @@ 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)
|
||||||
|
await insertContractAddressInDb(eContractid.MockSwapAdapter, mockSwapAdapter.address)
|
||||||
|
|
||||||
await deployWalletBalancerProvider(addressesProvider.address);
|
await deployWalletBalancerProvider(addressesProvider.address);
|
||||||
|
|
||||||
const testHelpers = await deployAaveProtocolTestHelpers(addressesProvider.address);
|
const testHelpers = await deployAaveProtocolTestHelpers(addressesProvider.address);
|
||||||
|
|
|
@ -845,7 +845,7 @@ const getTxCostAndTimestamp = async (tx: ContractReceipt) => {
|
||||||
return {txCost, txTimestamp};
|
return {txCost, txTimestamp};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getContractsData = async (reserve: string, user: string, testEnv: TestEnv) => {
|
export const getContractsData = async (reserve: string, user: string, testEnv: TestEnv) => {
|
||||||
const {pool} = testEnv;
|
const {pool} = testEnv;
|
||||||
const reserveData = await getReserveData(pool, reserve);
|
const reserveData = await getReserveData(pool, reserve);
|
||||||
const userData = await getUserData(pool, reserve, user);
|
const userData = await getUserData(pool, reserve, user);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
getMintableErc20,
|
getMintableErc20,
|
||||||
getLendingPoolConfiguratorProxy,
|
getLendingPoolConfiguratorProxy,
|
||||||
getPriceOracle,
|
getPriceOracle,
|
||||||
|
getMockSwapAdapter,
|
||||||
} 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';
|
||||||
|
@ -23,6 +24,7 @@ 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';
|
||||||
chai.use(bignumberChai());
|
chai.use(bignumberChai());
|
||||||
chai.use(almostEqual());
|
chai.use(almostEqual());
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ export interface TestEnv {
|
||||||
usdc: MintableErc20;
|
usdc: MintableErc20;
|
||||||
lend: MintableErc20;
|
lend: MintableErc20;
|
||||||
addressesProvider: LendingPoolAddressesProvider;
|
addressesProvider: LendingPoolAddressesProvider;
|
||||||
|
mockSwapAdapter: MockSwapAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
let buidlerevmSnapshotId: string = '0x1';
|
let buidlerevmSnapshotId: string = '0x1';
|
||||||
|
@ -67,6 +70,7 @@ const testEnv: TestEnv = {
|
||||||
usdc: {} as MintableErc20,
|
usdc: {} as MintableErc20,
|
||||||
lend: {} as MintableErc20,
|
lend: {} as MintableErc20,
|
||||||
addressesProvider: {} as LendingPoolAddressesProvider,
|
addressesProvider: {} as LendingPoolAddressesProvider,
|
||||||
|
mockSwapAdapter: {} as MockSwapAdapter
|
||||||
} as TestEnv;
|
} as TestEnv;
|
||||||
|
|
||||||
export async function initializeMakeSuite() {
|
export async function initializeMakeSuite() {
|
||||||
|
@ -125,6 +129,8 @@ export async function initializeMakeSuite() {
|
||||||
testEnv.usdc = await getMintableErc20(usdcAddress);
|
testEnv.usdc = await getMintableErc20(usdcAddress);
|
||||||
testEnv.lend = await getMintableErc20(lendAddress);
|
testEnv.lend = await getMintableErc20(lendAddress);
|
||||||
testEnv.weth = await getMintableErc20(wethAddress);
|
testEnv.weth = await getMintableErc20(wethAddress);
|
||||||
|
|
||||||
|
testEnv.mockSwapAdapter = await getMockSwapAdapter()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
|
export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
|
||||||
|
|
471
test/repay-with-collateral.spec.ts
Normal file
471
test/repay-with-collateral.spec.ts
Normal file
|
@ -0,0 +1,471 @@
|
||||||
|
import {TestEnv, makeSuite} from './helpers/make-suite';
|
||||||
|
import {APPROVAL_AMOUNT_LENDING_POOL} from '../helpers/constants';
|
||||||
|
import {ethers} from 'ethers';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import {
|
||||||
|
calcExpectedVariableDebtTokenBalance,
|
||||||
|
calcExpectedStableDebtTokenBalance,
|
||||||
|
} from './helpers/utils/calculations';
|
||||||
|
import {getContractsData} from './helpers/actions';
|
||||||
|
import {waitForTx} from './__setup.spec';
|
||||||
|
import {timeLatest} from '../helpers/misc-utils';
|
||||||
|
import {tEthereumAddress} from '../helpers/types';
|
||||||
|
|
||||||
|
const {expect} = require('chai');
|
||||||
|
const {parseUnits, parseEther} = ethers.utils;
|
||||||
|
|
||||||
|
const expectRepayWithCollateralEvent = (
|
||||||
|
events: ethers.Event[],
|
||||||
|
pool: tEthereumAddress,
|
||||||
|
collateral: tEthereumAddress,
|
||||||
|
borrowing: tEthereumAddress,
|
||||||
|
user: tEthereumAddress
|
||||||
|
) => {
|
||||||
|
if (!events || events.length < 14) {
|
||||||
|
expect(false, 'INVALID_EVENTS_LENGTH_ON_REPAY_COLLATERAL');
|
||||||
|
}
|
||||||
|
|
||||||
|
const repayWithCollateralEvent = events[13];
|
||||||
|
|
||||||
|
expect(repayWithCollateralEvent.address).to.be.equal(pool);
|
||||||
|
expect(`0x${repayWithCollateralEvent.topics[1].slice(26)}`.toLowerCase()).to.be.equal(
|
||||||
|
collateral.toLowerCase()
|
||||||
|
);
|
||||||
|
expect(`0x${repayWithCollateralEvent.topics[2].slice(26)}`).to.be.equal(borrowing.toLowerCase());
|
||||||
|
expect(`0x${repayWithCollateralEvent.topics[3].slice(26)}`.toLowerCase()).to.be.equal(
|
||||||
|
user.toLowerCase()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
|
||||||
|
it('User 1 provides some liquidity for others to borrow', async () => {
|
||||||
|
const {pool, weth, dai, usdc} = testEnv;
|
||||||
|
|
||||||
|
await weth.mint(parseEther('200'));
|
||||||
|
await weth.approve(pool.address, parseEther('200'));
|
||||||
|
await pool.deposit(weth.address, parseEther('200'), 0);
|
||||||
|
await dai.mint(parseEther('20000'));
|
||||||
|
await dai.approve(pool.address, parseEther('20000'));
|
||||||
|
await pool.deposit(dai.address, parseEther('20000'), 0);
|
||||||
|
await usdc.mint(parseEther('20000'));
|
||||||
|
await usdc.approve(pool.address, parseEther('20000'));
|
||||||
|
await pool.deposit(usdc.address, parseEther('20000'), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('User 2 deposit WETH and borrows DAI at Variable', async () => {
|
||||||
|
const {pool, weth, dai, users} = testEnv;
|
||||||
|
const user = users[1];
|
||||||
|
const amountToDeposit = ethers.utils.parseEther('1');
|
||||||
|
const amountToBorrow = ethers.utils.parseEther('20');
|
||||||
|
|
||||||
|
await weth.connect(user.signer).mint(amountToDeposit);
|
||||||
|
|
||||||
|
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
|
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, '0');
|
||||||
|
|
||||||
|
await pool.connect(user.signer).borrow(dai.address, amountToBorrow, 2, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('User 2 tries to repay his DAI Variable loan using his WETH collateral. First half the amount, after that, the rest', async () => {
|
||||||
|
const {pool, weth, dai, users, mockSwapAdapter, oracle} = testEnv;
|
||||||
|
const user = users[1];
|
||||||
|
|
||||||
|
const amountToRepay = parseEther('10');
|
||||||
|
|
||||||
|
const {userData: wethUserDataBefore} = await getContractsData(
|
||||||
|
weth.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||||
|
dai.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||||
|
await waitForTx(
|
||||||
|
await pool
|
||||||
|
.connect(user.signer)
|
||||||
|
.repayWithCollateral(
|
||||||
|
weth.address,
|
||||||
|
dai.address,
|
||||||
|
user.address,
|
||||||
|
amountToRepay,
|
||||||
|
mockSwapAdapter.address,
|
||||||
|
'0x'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const repayWithCollateralTimestamp = await timeLatest();
|
||||||
|
|
||||||
|
const {userData: wethUserDataAfter} = await getContractsData(
|
||||||
|
weth.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||||
|
|
||||||
|
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||||
|
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
|
||||||
|
const collateralDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(weth.address)
|
||||||
|
).decimals.toString();
|
||||||
|
const principalDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(dai.address)
|
||||||
|
).decimals.toString();
|
||||||
|
|
||||||
|
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||||
|
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||||
|
.times(new BigNumber(10).pow(collateralDecimals))
|
||||||
|
.div(
|
||||||
|
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||||
|
)
|
||||||
|
.div(100)
|
||||||
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||||
|
|
||||||
|
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||||
|
daiReserveDataBefore,
|
||||||
|
daiUserDataBefore,
|
||||||
|
new BigNumber(repayWithCollateralTimestamp)
|
||||||
|
).minus(daiUserDataBefore.currentVariableDebt);
|
||||||
|
|
||||||
|
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||||
|
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||||
|
.minus(amountToRepay.toString())
|
||||||
|
.plus(expectedVariableDebtIncrease)
|
||||||
|
.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||||
|
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||||
|
expectedCollateralLiquidated.toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('User 3 deposits WETH and borrows USDC at Variable', async () => {
|
||||||
|
const {pool, weth, usdc, users} = testEnv;
|
||||||
|
const user = users[2];
|
||||||
|
const amountToDeposit = parseEther('10');
|
||||||
|
const amountToBorrow = parseUnits('40', 6);
|
||||||
|
|
||||||
|
await weth.connect(user.signer).mint(amountToDeposit);
|
||||||
|
|
||||||
|
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
|
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, '0');
|
||||||
|
|
||||||
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('User 3 repays completely his USDC loan by swapping his WETH collateral', async () => {
|
||||||
|
const {pool, weth, usdc, users, mockSwapAdapter, oracle} = testEnv;
|
||||||
|
const user = users[2];
|
||||||
|
|
||||||
|
const amountToRepay = parseUnits('10', 6);
|
||||||
|
|
||||||
|
const {userData: wethUserDataBefore} = await getContractsData(
|
||||||
|
weth.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
reserveData: usdcReserveDataBefore,
|
||||||
|
userData: usdcUserDataBefore,
|
||||||
|
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||||
|
|
||||||
|
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||||
|
await waitForTx(
|
||||||
|
await pool
|
||||||
|
.connect(user.signer)
|
||||||
|
.repayWithCollateral(
|
||||||
|
weth.address,
|
||||||
|
usdc.address,
|
||||||
|
user.address,
|
||||||
|
amountToRepay,
|
||||||
|
mockSwapAdapter.address,
|
||||||
|
'0x'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const repayWithCollateralTimestamp = await timeLatest();
|
||||||
|
|
||||||
|
const {userData: wethUserDataAfter} = await getContractsData(
|
||||||
|
weth.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||||
|
usdc.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||||
|
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||||
|
|
||||||
|
const collateralDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(weth.address)
|
||||||
|
).decimals.toString();
|
||||||
|
const principalDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(usdc.address)
|
||||||
|
).decimals.toString();
|
||||||
|
|
||||||
|
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||||
|
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||||
|
.times(new BigNumber(10).pow(collateralDecimals))
|
||||||
|
.div(
|
||||||
|
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||||
|
)
|
||||||
|
.div(100)
|
||||||
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||||
|
|
||||||
|
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||||
|
usdcReserveDataBefore,
|
||||||
|
usdcUserDataBefore,
|
||||||
|
new BigNumber(repayWithCollateralTimestamp)
|
||||||
|
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||||
|
|
||||||
|
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||||
|
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||||
|
.minus(amountToRepay.toString())
|
||||||
|
.plus(expectedVariableDebtIncrease)
|
||||||
|
.toString(),
|
||||||
|
'INVALID_DEBT_POSITION'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||||
|
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||||
|
expectedCollateralLiquidated.toString()
|
||||||
|
),
|
||||||
|
'INVALID_COLLATERAL_POSITION'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('User tries to repay with his collateral a currency he havent borrow', async () => {
|
||||||
|
const {pool, weth, dai, users, mockSwapAdapter} = testEnv;
|
||||||
|
const user = users[2];
|
||||||
|
|
||||||
|
const amountToRepay = parseUnits('10', 6);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user.signer)
|
||||||
|
.repayWithCollateral(
|
||||||
|
weth.address,
|
||||||
|
dai.address,
|
||||||
|
user.address,
|
||||||
|
amountToRepay,
|
||||||
|
mockSwapAdapter.address,
|
||||||
|
'0x'
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('revert CURRRENCY_NOT_BORROWED');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('User tries to repay with his collateral all his variable debt and part of the stable', async () => {
|
||||||
|
const {pool, weth, usdc, users, mockSwapAdapter, oracle} = testEnv;
|
||||||
|
const user = users[2];
|
||||||
|
|
||||||
|
const amountToDeposit = parseEther('20');
|
||||||
|
const amountToBorrowStable = parseUnits('40', 6);
|
||||||
|
const amountToBorrowVariable = parseUnits('40', 6);
|
||||||
|
|
||||||
|
await weth.connect(user.signer).mint(amountToDeposit);
|
||||||
|
|
||||||
|
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, '0');
|
||||||
|
|
||||||
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0);
|
||||||
|
|
||||||
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowStable, 1, 0);
|
||||||
|
|
||||||
|
const amountToRepay = parseUnits('80', 6);
|
||||||
|
|
||||||
|
const {userData: wethUserDataBefore} = await getContractsData(
|
||||||
|
weth.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
reserveData: usdcReserveDataBefore,
|
||||||
|
userData: usdcUserDataBefore,
|
||||||
|
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||||
|
|
||||||
|
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||||
|
const txReceipt = await waitForTx(
|
||||||
|
await pool
|
||||||
|
.connect(user.signer)
|
||||||
|
.repayWithCollateral(
|
||||||
|
weth.address,
|
||||||
|
usdc.address,
|
||||||
|
user.address,
|
||||||
|
amountToRepay,
|
||||||
|
mockSwapAdapter.address,
|
||||||
|
'0x'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const repayWithCollateralTimestamp = await timeLatest();
|
||||||
|
|
||||||
|
const {userData: wethUserDataAfter} = await getContractsData(
|
||||||
|
weth.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||||
|
usdc.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||||
|
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||||
|
|
||||||
|
const collateralDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(weth.address)
|
||||||
|
).decimals.toString();
|
||||||
|
const principalDecimals = (
|
||||||
|
await pool.getReserveConfigurationData(usdc.address)
|
||||||
|
).decimals.toString();
|
||||||
|
|
||||||
|
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||||
|
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||||
|
.times(new BigNumber(10).pow(collateralDecimals))
|
||||||
|
.div(
|
||||||
|
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||||
|
)
|
||||||
|
.div(100)
|
||||||
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||||
|
|
||||||
|
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||||
|
usdcReserveDataBefore,
|
||||||
|
usdcUserDataBefore,
|
||||||
|
new BigNumber(repayWithCollateralTimestamp)
|
||||||
|
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||||
|
|
||||||
|
const expectedStableDebtIncrease = calcExpectedStableDebtTokenBalance(
|
||||||
|
usdcUserDataBefore,
|
||||||
|
new BigNumber(repayWithCollateralTimestamp)
|
||||||
|
).minus(usdcUserDataBefore.currentStableDebt);
|
||||||
|
|
||||||
|
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.equal(
|
||||||
|
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||||
|
.minus(amountToRepay.toString())
|
||||||
|
.plus(expectedVariableDebtIncrease)
|
||||||
|
.gte(0)
|
||||||
|
? new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||||
|
.minus(amountToRepay.toString())
|
||||||
|
.plus(expectedVariableDebtIncrease)
|
||||||
|
.toString()
|
||||||
|
: '0',
|
||||||
|
'INVALID_VARIABLE_DEBT_POSITION'
|
||||||
|
);
|
||||||
|
|
||||||
|
const stableDebtRepaid = new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||||
|
.minus(amountToRepay.toString())
|
||||||
|
.plus(expectedVariableDebtIncrease)
|
||||||
|
.abs();
|
||||||
|
|
||||||
|
expect(usdcUserDataAfter.currentStableDebt).to.be.bignumber.equal(
|
||||||
|
new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||||
|
.minus(stableDebtRepaid)
|
||||||
|
.plus(expectedStableDebtIncrease)
|
||||||
|
.gte(0)
|
||||||
|
? new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||||
|
.minus(stableDebtRepaid)
|
||||||
|
.plus(expectedStableDebtIncrease)
|
||||||
|
.toString()
|
||||||
|
: '0',
|
||||||
|
'INVALID_STABLE_DEBT_POSITION'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||||
|
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||||
|
expectedCollateralLiquidated.toString()
|
||||||
|
),
|
||||||
|
'INVALID_COLLATERAL_POSITION'
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventsEmitted = txReceipt.events || [];
|
||||||
|
|
||||||
|
expectRepayWithCollateralEvent(
|
||||||
|
eventsEmitted,
|
||||||
|
pool.address,
|
||||||
|
weth.address,
|
||||||
|
usdc.address,
|
||||||
|
user.address
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// WIP
|
||||||
|
it('User tries to repay a bigger amount that what can be swapped of a particular collateral, repaying only the maximum allowed by that collateral', async () => {
|
||||||
|
const {pool, weth, dai, users, mockSwapAdapter, oracle} = testEnv;
|
||||||
|
const user = users[3];
|
||||||
|
|
||||||
|
const amountToDepositWeth = parseEther('0.1');
|
||||||
|
const amountToDepositDAI = parseEther('500');
|
||||||
|
const amountToBorrowVariable = parseEther('80');
|
||||||
|
|
||||||
|
await weth.connect(user.signer).mint(amountToDepositWeth);
|
||||||
|
await dai.connect(user.signer).mint(amountToDepositDAI);
|
||||||
|
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
|
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, '0');
|
||||||
|
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, '0');
|
||||||
|
|
||||||
|
await pool.connect(user.signer).borrow(dai.address, amountToBorrowVariable, 2, 0);
|
||||||
|
|
||||||
|
const amountToRepay = parseEther('80');
|
||||||
|
|
||||||
|
const {userData: wethUserDataBefore} = await getContractsData(
|
||||||
|
weth.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||||
|
dai.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('BEFORE');
|
||||||
|
console.log(wethUserDataBefore.currentATokenBalance.toString());
|
||||||
|
console.log(daiUserDataBefore.currentVariableDebt.toString());
|
||||||
|
console.log(daiUserDataBefore.currentStableDebt.toString());
|
||||||
|
|
||||||
|
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||||
|
const txReceipt = await waitForTx(
|
||||||
|
await pool
|
||||||
|
.connect(user.signer)
|
||||||
|
.repayWithCollateral(
|
||||||
|
weth.address,
|
||||||
|
dai.address,
|
||||||
|
user.address,
|
||||||
|
amountToRepay,
|
||||||
|
mockSwapAdapter.address,
|
||||||
|
'0x'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const repayWithCollateralTimestamp = await timeLatest();
|
||||||
|
|
||||||
|
const {userData: wethUserDataAfter} = await getContractsData(
|
||||||
|
weth.address,
|
||||||
|
user.address,
|
||||||
|
testEnv
|
||||||
|
);
|
||||||
|
|
||||||
|
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||||
|
|
||||||
|
console.log('AFTER');
|
||||||
|
console.log(wethUserDataAfter.currentATokenBalance.toString());
|
||||||
|
console.log(daiUserDataAfter.currentVariableDebt.toString());
|
||||||
|
console.log(daiUserDataAfter.currentStableDebt.toString());
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user