mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Removed deprecated /test/ test directory
This commit is contained in:
parent
0a3da08c9c
commit
4080bb5983
|
@ -1,292 +0,0 @@
|
||||||
import rawBRE from 'hardhat';
|
|
||||||
import { MockContract } from 'ethereum-waffle';
|
|
||||||
import {
|
|
||||||
insertContractAddressInDb,
|
|
||||||
getEthersSigners,
|
|
||||||
registerContractInJsonDb,
|
|
||||||
} from '../helpers/contracts-helpers';
|
|
||||||
import {
|
|
||||||
deployLendingPoolAddressesProvider,
|
|
||||||
deployMintableERC20,
|
|
||||||
deployLendingPoolAddressesProviderRegistry,
|
|
||||||
deployLendingPoolConfigurator,
|
|
||||||
deployLendingPool,
|
|
||||||
deployPriceOracle,
|
|
||||||
deployAaveOracle,
|
|
||||||
deployLendingPoolCollateralManager,
|
|
||||||
deployMockFlashLoanReceiver,
|
|
||||||
deployWalletBalancerProvider,
|
|
||||||
deployAaveProtocolDataProvider,
|
|
||||||
deployLendingRateOracle,
|
|
||||||
deployStableAndVariableTokensHelper,
|
|
||||||
deployATokensAndRatesHelper,
|
|
||||||
deployWETHGateway,
|
|
||||||
deployWETHMocked,
|
|
||||||
deployMockUniswapRouter,
|
|
||||||
deployUniswapLiquiditySwapAdapter,
|
|
||||||
deployUniswapRepayAdapter,
|
|
||||||
deployFlashLiquidationAdapter,
|
|
||||||
} from '../helpers/contracts-deployments';
|
|
||||||
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 { initializeMakeSuite } from './helpers/make-suite';
|
|
||||||
|
|
||||||
import {
|
|
||||||
setInitialAssetPricesInOracle,
|
|
||||||
deployAllMockAggregators,
|
|
||||||
setInitialMarketRatesInRatesOracleByHelper,
|
|
||||||
} from '../helpers/oracles-helpers';
|
|
||||||
import { DRE, waitForTx } from '../helpers/misc-utils';
|
|
||||||
import { initReservesByHelper, configureReservesByHelper } from '../helpers/init-helpers';
|
|
||||||
import AaveConfig from '../markets/aave';
|
|
||||||
import { ZERO_ADDRESS } from '../helpers/constants';
|
|
||||||
import {
|
|
||||||
getLendingPool,
|
|
||||||
getLendingPoolConfiguratorProxy,
|
|
||||||
getPairsTokenAggregator,
|
|
||||||
} from '../helpers/contracts-getters';
|
|
||||||
import { WETH9Mocked } from '../types/WETH9Mocked';
|
|
||||||
|
|
||||||
const MOCK_USD_PRICE_IN_WEI = AaveConfig.ProtocolGlobalParams.MockUsdPriceInWei;
|
|
||||||
const ALL_ASSETS_INITIAL_PRICES = AaveConfig.Mocks.AllAssetsInitialPrices;
|
|
||||||
const USD_ADDRESS = AaveConfig.ProtocolGlobalParams.UsdAddress;
|
|
||||||
const MOCK_CHAINLINK_AGGREGATORS_PRICES = AaveConfig.Mocks.AllAssetsInitialPrices;
|
|
||||||
const LENDING_RATE_ORACLE_RATES_COMMON = AaveConfig.LendingRateOracleRatesCommon;
|
|
||||||
|
|
||||||
const deployAllMockTokens = async (deployer: Signer) => {
|
|
||||||
const tokens: { [symbol: string]: MockContract | MintableERC20 | WETH9Mocked } = {};
|
|
||||||
|
|
||||||
const protoConfigData = getReservesConfigByPool(AavePools.proto);
|
|
||||||
|
|
||||||
for (const tokenSymbol of Object.keys(TokenContractId)) {
|
|
||||||
if (tokenSymbol === 'WETH') {
|
|
||||||
tokens[tokenSymbol] = await deployWETHMocked();
|
|
||||||
await registerContractInJsonDb(tokenSymbol.toUpperCase(), tokens[tokenSymbol]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let decimals = 18;
|
|
||||||
|
|
||||||
let configData = (<any>protoConfigData)[tokenSymbol];
|
|
||||||
|
|
||||||
if (!configData) {
|
|
||||||
decimals = 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens[tokenSymbol] = await deployMintableERC20([
|
|
||||||
tokenSymbol,
|
|
||||||
tokenSymbol,
|
|
||||||
configData ? configData.reserveDecimals : 18,
|
|
||||||
]);
|
|
||||||
await registerContractInJsonDb(tokenSymbol.toUpperCase(), tokens[tokenSymbol]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens;
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
|
|
||||||
console.time('setup');
|
|
||||||
const aaveAdmin = await deployer.getAddress();
|
|
||||||
|
|
||||||
const mockTokens = await deployAllMockTokens(deployer);
|
|
||||||
|
|
||||||
const addressesProvider = await deployLendingPoolAddressesProvider(AaveConfig.MarketId);
|
|
||||||
await waitForTx(await addressesProvider.setPoolAdmin(aaveAdmin));
|
|
||||||
|
|
||||||
//setting users[1] as emergency admin, which is in position 2 in the DRE addresses list
|
|
||||||
const addressList = await Promise.all(
|
|
||||||
(await DRE.ethers.getSigners()).map((signer) => signer.getAddress())
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitForTx(await addressesProvider.setEmergencyAdmin(addressList[2]));
|
|
||||||
|
|
||||||
const addressesProviderRegistry = await deployLendingPoolAddressesProviderRegistry();
|
|
||||||
await waitForTx(
|
|
||||||
await addressesProviderRegistry.registerAddressesProvider(addressesProvider.address, 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
const lendingPoolImpl = await deployLendingPool();
|
|
||||||
|
|
||||||
await waitForTx(await addressesProvider.setLendingPoolImpl(lendingPoolImpl.address));
|
|
||||||
|
|
||||||
const lendingPoolAddress = await addressesProvider.getLendingPool();
|
|
||||||
const lendingPoolProxy = await getLendingPool(lendingPoolAddress);
|
|
||||||
|
|
||||||
await insertContractAddressInDb(eContractid.LendingPool, lendingPoolProxy.address);
|
|
||||||
|
|
||||||
const lendingPoolConfiguratorImpl = await deployLendingPoolConfigurator();
|
|
||||||
await waitForTx(
|
|
||||||
await addressesProvider.setLendingPoolConfiguratorImpl(lendingPoolConfiguratorImpl.address)
|
|
||||||
);
|
|
||||||
const lendingPoolConfiguratorProxy = await getLendingPoolConfiguratorProxy(
|
|
||||||
await addressesProvider.getLendingPoolConfigurator()
|
|
||||||
);
|
|
||||||
await insertContractAddressInDb(
|
|
||||||
eContractid.LendingPoolConfigurator,
|
|
||||||
lendingPoolConfiguratorProxy.address
|
|
||||||
);
|
|
||||||
|
|
||||||
// Deploy deployment helpers
|
|
||||||
await deployStableAndVariableTokensHelper([lendingPoolProxy.address, addressesProvider.address]);
|
|
||||||
await deployATokensAndRatesHelper([
|
|
||||||
lendingPoolProxy.address,
|
|
||||||
addressesProvider.address,
|
|
||||||
lendingPoolConfiguratorProxy.address,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const fallbackOracle = await deployPriceOracle();
|
|
||||||
await waitForTx(await fallbackOracle.setEthUsdPrice(MOCK_USD_PRICE_IN_WEI));
|
|
||||||
await setInitialAssetPricesInOracle(
|
|
||||||
ALL_ASSETS_INITIAL_PRICES,
|
|
||||||
{
|
|
||||||
WETH: mockTokens.WETH.address,
|
|
||||||
DAI: mockTokens.DAI.address,
|
|
||||||
TUSD: mockTokens.TUSD.address,
|
|
||||||
USDC: mockTokens.USDC.address,
|
|
||||||
USDT: mockTokens.USDT.address,
|
|
||||||
SUSD: mockTokens.SUSD.address,
|
|
||||||
AAVE: mockTokens.AAVE.address,
|
|
||||||
BAT: mockTokens.BAT.address,
|
|
||||||
MKR: mockTokens.MKR.address,
|
|
||||||
LINK: mockTokens.LINK.address,
|
|
||||||
KNC: mockTokens.KNC.address,
|
|
||||||
WBTC: mockTokens.WBTC.address,
|
|
||||||
MANA: mockTokens.MANA.address,
|
|
||||||
ZRX: mockTokens.ZRX.address,
|
|
||||||
SNX: mockTokens.SNX.address,
|
|
||||||
BUSD: mockTokens.BUSD.address,
|
|
||||||
YFI: mockTokens.BUSD.address,
|
|
||||||
REN: mockTokens.REN.address,
|
|
||||||
UNI: mockTokens.UNI.address,
|
|
||||||
ENJ: mockTokens.ENJ.address,
|
|
||||||
LpDAI: mockTokens.LpDAI.address,
|
|
||||||
LpUSDC: mockTokens.LpUSDC.address,
|
|
||||||
LpUSDT: mockTokens.LpUSDT.address,
|
|
||||||
LpWBTC: mockTokens.LpWBTC.address,
|
|
||||||
LpWETH: mockTokens.LpWETH.address,
|
|
||||||
LpDAIWETH: mockTokens.LpDAIWETH.address,
|
|
||||||
LpWBTCWETH: mockTokens.LpWBTCWETH.address,
|
|
||||||
LpAAVEWETH: mockTokens.LpAAVEWETH.address,
|
|
||||||
LpBATWETH: mockTokens.LpBATWETH.address,
|
|
||||||
LpUSDCDAI: mockTokens.LpUSDCDAI.address,
|
|
||||||
LpCRVWETH: mockTokens.LpCRVWETH.address,
|
|
||||||
LpLINKWETH: mockTokens.LpLINKWETH.address,
|
|
||||||
LpMKRWETH: mockTokens.LpMKRWETH.address,
|
|
||||||
LpRENWETH: mockTokens.LpRENWETH.address,
|
|
||||||
LpSNXWETH: mockTokens.LpSNXWETH.address,
|
|
||||||
LpUNIWETH: mockTokens.LpUNIWETH.address,
|
|
||||||
LpUSDCWETH: mockTokens.LpUSDCWETH.address,
|
|
||||||
LpWBTCUSDC: mockTokens.LpWBTCUSDC.address,
|
|
||||||
LpYFIWETH: mockTokens.LpYFIWETH.address,
|
|
||||||
USD: USD_ADDRESS,
|
|
||||||
},
|
|
||||||
fallbackOracle
|
|
||||||
);
|
|
||||||
|
|
||||||
const mockAggregators = await deployAllMockAggregators(MOCK_CHAINLINK_AGGREGATORS_PRICES);
|
|
||||||
|
|
||||||
const allTokenAddresses = Object.entries(mockTokens).reduce(
|
|
||||||
(accum: { [tokenSymbol: string]: tEthereumAddress }, [tokenSymbol, tokenContract]) => ({
|
|
||||||
...accum,
|
|
||||||
[tokenSymbol]: tokenContract.address,
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const allAggregatorsAddresses = Object.entries(mockAggregators).reduce(
|
|
||||||
(accum: { [tokenSymbol: string]: tEthereumAddress }, [tokenSymbol, aggregator]) => ({
|
|
||||||
...accum,
|
|
||||||
[tokenSymbol]: aggregator.address,
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
const [tokens, aggregators] = getPairsTokenAggregator(allTokenAddresses, allAggregatorsAddresses);
|
|
||||||
|
|
||||||
await deployAaveOracle([tokens, aggregators, fallbackOracle.address, mockTokens.WETH.address]);
|
|
||||||
await waitForTx(await addressesProvider.setPriceOracle(fallbackOracle.address));
|
|
||||||
|
|
||||||
const lendingRateOracle = await deployLendingRateOracle();
|
|
||||||
await waitForTx(await addressesProvider.setLendingRateOracle(lendingRateOracle.address));
|
|
||||||
|
|
||||||
const { USD, ...tokensAddressesWithoutUsd } = allTokenAddresses;
|
|
||||||
const allReservesAddresses = {
|
|
||||||
...tokensAddressesWithoutUsd,
|
|
||||||
};
|
|
||||||
await setInitialMarketRatesInRatesOracleByHelper(
|
|
||||||
LENDING_RATE_ORACLE_RATES_COMMON,
|
|
||||||
allReservesAddresses,
|
|
||||||
lendingRateOracle,
|
|
||||||
aaveAdmin
|
|
||||||
);
|
|
||||||
|
|
||||||
const reservesParams = getReservesConfigByPool(AavePools.proto);
|
|
||||||
|
|
||||||
const testHelpers = await deployAaveProtocolDataProvider(addressesProvider.address);
|
|
||||||
|
|
||||||
await insertContractAddressInDb(eContractid.AaveProtocolDataProvider, testHelpers.address);
|
|
||||||
const admin = await deployer.getAddress();
|
|
||||||
|
|
||||||
console.log('Initialize configuration');
|
|
||||||
|
|
||||||
const config = loadPoolConfig(ConfigNames.Aave);
|
|
||||||
|
|
||||||
const treasuryAddress = await getTreasuryAddress(config);
|
|
||||||
|
|
||||||
await initReservesByHelper(
|
|
||||||
reservesParams,
|
|
||||||
allReservesAddresses,
|
|
||||||
admin,
|
|
||||||
treasuryAddress,
|
|
||||||
ZERO_ADDRESS,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
await configureReservesByHelper(reservesParams, allReservesAddresses, testHelpers, admin);
|
|
||||||
|
|
||||||
const collateralManager = await deployLendingPoolCollateralManager();
|
|
||||||
await waitForTx(
|
|
||||||
await addressesProvider.setLendingPoolCollateralManager(collateralManager.address)
|
|
||||||
);
|
|
||||||
await deployMockFlashLoanReceiver(addressesProvider.address);
|
|
||||||
|
|
||||||
const mockUniswapRouter = await deployMockUniswapRouter();
|
|
||||||
|
|
||||||
const adapterParams: [string, string, string] = [
|
|
||||||
addressesProvider.address,
|
|
||||||
mockUniswapRouter.address,
|
|
||||||
mockTokens.WETH.address,
|
|
||||||
];
|
|
||||||
|
|
||||||
await deployUniswapLiquiditySwapAdapter(adapterParams);
|
|
||||||
await deployUniswapRepayAdapter(adapterParams);
|
|
||||||
await deployFlashLiquidationAdapter(adapterParams);
|
|
||||||
|
|
||||||
await deployWalletBalancerProvider();
|
|
||||||
|
|
||||||
await deployWETHGateway([mockTokens.WETH.address, lendingPoolAddress]);
|
|
||||||
|
|
||||||
console.timeEnd('setup');
|
|
||||||
};
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
await rawBRE.run('set-DRE');
|
|
||||||
const [deployer, secondaryWallet] = await getEthersSigners();
|
|
||||||
const MAINNET_FORK = process.env.MAINNET_FORK === 'true';
|
|
||||||
|
|
||||||
if (MAINNET_FORK) {
|
|
||||||
await rawBRE.run('aave:mainnet');
|
|
||||||
} else {
|
|
||||||
console.log('-> Deploying test environment...');
|
|
||||||
await buildTestEnv(deployer, secondaryWallet);
|
|
||||||
}
|
|
||||||
|
|
||||||
await initializeMakeSuite();
|
|
||||||
console.log('\n***************');
|
|
||||||
console.log('Setup and snapshot finished');
|
|
||||||
console.log('***************\n');
|
|
||||||
});
|
|
|
@ -1,100 +0,0 @@
|
||||||
import { TestEnv, makeSuite } from './helpers/make-suite';
|
|
||||||
import { ZERO_ADDRESS } from '../helpers/constants';
|
|
||||||
import { ProtocolErrors } from '../helpers/types';
|
|
||||||
|
|
||||||
const { expect } = require('chai');
|
|
||||||
|
|
||||||
makeSuite('AddressesProviderRegistry', (testEnv: TestEnv) => {
|
|
||||||
it('Checks the addresses provider is added to the registry', async () => {
|
|
||||||
const { addressesProvider, registry } = testEnv;
|
|
||||||
|
|
||||||
const providers = await registry.getAddressesProvidersList();
|
|
||||||
|
|
||||||
expect(providers.length).to.be.equal(1, 'Invalid length of the addresses providers list');
|
|
||||||
expect(providers[0].toString()).to.be.equal(
|
|
||||||
addressesProvider.address,
|
|
||||||
' Invalid addresses provider added to the list'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tries to register an addresses provider with id 0', async () => {
|
|
||||||
const { users, registry } = testEnv;
|
|
||||||
const { LPAPR_INVALID_ADDRESSES_PROVIDER_ID } = ProtocolErrors;
|
|
||||||
|
|
||||||
await expect(registry.registerAddressesProvider(users[2].address, '0')).to.be.revertedWith(
|
|
||||||
LPAPR_INVALID_ADDRESSES_PROVIDER_ID
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Registers a new mock addresses provider', async () => {
|
|
||||||
const { users, registry } = testEnv;
|
|
||||||
|
|
||||||
//simulating an addresses provider using the users[1] wallet address
|
|
||||||
await registry.registerAddressesProvider(users[1].address, '2');
|
|
||||||
|
|
||||||
const providers = await registry.getAddressesProvidersList();
|
|
||||||
|
|
||||||
expect(providers.length).to.be.equal(2, 'Invalid length of the addresses providers list');
|
|
||||||
expect(providers[1].toString()).to.be.equal(
|
|
||||||
users[1].address,
|
|
||||||
' Invalid addresses provider added to the list'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Removes the mock addresses provider', async () => {
|
|
||||||
const { users, registry, addressesProvider } = testEnv;
|
|
||||||
|
|
||||||
const id = await registry.getAddressesProviderIdByAddress(users[1].address);
|
|
||||||
|
|
||||||
expect(id).to.be.equal('2', 'Invalid isRegistered return value');
|
|
||||||
|
|
||||||
await registry.unregisterAddressesProvider(users[1].address);
|
|
||||||
|
|
||||||
const providers = await registry.getAddressesProvidersList();
|
|
||||||
|
|
||||||
expect(providers.length).to.be.equal(2, 'Invalid length of the addresses providers list');
|
|
||||||
expect(providers[0].toString()).to.be.equal(
|
|
||||||
addressesProvider.address,
|
|
||||||
' Invalid addresses provider added to the list'
|
|
||||||
);
|
|
||||||
expect(providers[1].toString()).to.be.equal(ZERO_ADDRESS, ' Invalid addresses');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to remove a unregistered addressesProvider', async () => {
|
|
||||||
const { LPAPR_PROVIDER_NOT_REGISTERED } = ProtocolErrors;
|
|
||||||
|
|
||||||
const { users, registry } = testEnv;
|
|
||||||
|
|
||||||
await expect(registry.unregisterAddressesProvider(users[2].address)).to.be.revertedWith(
|
|
||||||
LPAPR_PROVIDER_NOT_REGISTERED
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to remove a unregistered addressesProvider', async () => {
|
|
||||||
const { LPAPR_PROVIDER_NOT_REGISTERED } = ProtocolErrors;
|
|
||||||
|
|
||||||
const { users, registry } = testEnv;
|
|
||||||
|
|
||||||
await expect(registry.unregisterAddressesProvider(users[2].address)).to.be.revertedWith(
|
|
||||||
LPAPR_PROVIDER_NOT_REGISTERED
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to add an already added addressesProvider with a different id. Should overwrite the previous id', async () => {
|
|
||||||
const { users, registry, addressesProvider } = testEnv;
|
|
||||||
|
|
||||||
await registry.registerAddressesProvider(addressesProvider.address, '2');
|
|
||||||
|
|
||||||
const providers = await registry.getAddressesProvidersList();
|
|
||||||
|
|
||||||
const id = await registry.getAddressesProviderIdByAddress(addressesProvider.address);
|
|
||||||
|
|
||||||
expect(providers.length).to.be.equal(2, 'Invalid length of the addresses providers list');
|
|
||||||
|
|
||||||
expect(providers[0].toString()).to.be.equal(
|
|
||||||
addressesProvider.address,
|
|
||||||
' Invalid addresses provider added to the list'
|
|
||||||
);
|
|
||||||
expect(providers[1].toString()).to.be.equal(ZERO_ADDRESS, ' Invalid addresses');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,35 +0,0 @@
|
||||||
import { expect } from 'chai';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { ProtocolErrors } from '../helpers/types';
|
|
||||||
|
|
||||||
makeSuite('AToken: Modifiers', (testEnv: TestEnv) => {
|
|
||||||
const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors;
|
|
||||||
|
|
||||||
it('Tries to invoke mint not being the LendingPool', async () => {
|
|
||||||
const { deployer, aDai } = testEnv;
|
|
||||||
await expect(aDai.mint(deployer.address, '1', '1')).to.be.revertedWith(
|
|
||||||
CT_CALLER_MUST_BE_LENDING_POOL
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to invoke burn not being the LendingPool', async () => {
|
|
||||||
const { deployer, aDai } = testEnv;
|
|
||||||
await expect(aDai.burn(deployer.address, deployer.address, '1', '1')).to.be.revertedWith(
|
|
||||||
CT_CALLER_MUST_BE_LENDING_POOL
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to invoke transferOnLiquidation not being the LendingPool', async () => {
|
|
||||||
const { deployer, users, aDai } = testEnv;
|
|
||||||
await expect(
|
|
||||||
aDai.transferOnLiquidation(deployer.address, users[0].address, '1')
|
|
||||||
).to.be.revertedWith(CT_CALLER_MUST_BE_LENDING_POOL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to invoke transferUnderlyingTo not being the LendingPool', async () => {
|
|
||||||
const { deployer, aDai } = testEnv;
|
|
||||||
await expect(aDai.transferUnderlyingTo(deployer.address, '1')).to.be.revertedWith(
|
|
||||||
CT_CALLER_MUST_BE_LENDING_POOL
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,312 +0,0 @@
|
||||||
import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../helpers/constants';
|
|
||||||
import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants';
|
|
||||||
import { buildPermitParams, getSignatureFromTypedData } from '../helpers/contracts-helpers';
|
|
||||||
import { expect } from 'chai';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { DRE } from '../helpers/misc-utils';
|
|
||||||
import { waitForTx } from '../helpers/misc-utils';
|
|
||||||
import { _TypedDataEncoder } from 'ethers/lib/utils';
|
|
||||||
|
|
||||||
const { parseEther } = ethers.utils;
|
|
||||||
|
|
||||||
makeSuite('AToken: Permit', (testEnv: TestEnv) => {
|
|
||||||
it('Checks the domain separator', async () => {
|
|
||||||
const { aDai } = testEnv;
|
|
||||||
const separator = await aDai.DOMAIN_SEPARATOR();
|
|
||||||
|
|
||||||
const domain = {
|
|
||||||
name: await aDai.name(),
|
|
||||||
version: '1',
|
|
||||||
chainId: DRE.network.config.chainId,
|
|
||||||
verifyingContract: aDai.address,
|
|
||||||
};
|
|
||||||
const domainSeparator = _TypedDataEncoder.hashDomain(domain);
|
|
||||||
|
|
||||||
expect(separator).to.be.equal(domainSeparator, 'Invalid domain separator');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Get aDAI for tests', async () => {
|
|
||||||
const { dai, pool, deployer } = testEnv;
|
|
||||||
|
|
||||||
await dai.mint(parseEther('20000'));
|
|
||||||
await dai.approve(pool.address, parseEther('20000'));
|
|
||||||
|
|
||||||
await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Reverts submitting a permit with 0 expiration', async () => {
|
|
||||||
const { aDai, deployer, users } = testEnv;
|
|
||||||
const owner = deployer;
|
|
||||||
const spender = users[1];
|
|
||||||
|
|
||||||
const tokenName = await aDai.name();
|
|
||||||
|
|
||||||
const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
|
||||||
const expiration = 0;
|
|
||||||
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
|
||||||
const permitAmount = ethers.utils.parseEther('2').toString();
|
|
||||||
const msgParams = buildPermitParams(
|
|
||||||
chainId,
|
|
||||||
aDai.address,
|
|
||||||
'1',
|
|
||||||
tokenName,
|
|
||||||
owner.address,
|
|
||||||
spender.address,
|
|
||||||
nonce,
|
|
||||||
permitAmount,
|
|
||||||
expiration.toFixed()
|
|
||||||
);
|
|
||||||
|
|
||||||
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
|
||||||
if (!ownerPrivateKey) {
|
|
||||||
throw new Error('INVALID_OWNER_PK');
|
|
||||||
}
|
|
||||||
|
|
||||||
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'INVALID_ALLOWANCE_BEFORE_PERMIT'
|
|
||||||
);
|
|
||||||
|
|
||||||
const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
aDai
|
|
||||||
.connect(spender.signer)
|
|
||||||
.permit(owner.address, spender.address, permitAmount, expiration, v, r, s)
|
|
||||||
).to.be.revertedWith('INVALID_EXPIRATION');
|
|
||||||
|
|
||||||
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'INVALID_ALLOWANCE_AFTER_PERMIT'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Submits a permit with maximum expiration length', async () => {
|
|
||||||
const { aDai, deployer, users } = testEnv;
|
|
||||||
const owner = deployer;
|
|
||||||
const spender = users[1];
|
|
||||||
|
|
||||||
const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
|
||||||
const deadline = MAX_UINT_AMOUNT;
|
|
||||||
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
|
||||||
const permitAmount = parseEther('2').toString();
|
|
||||||
const msgParams = buildPermitParams(
|
|
||||||
chainId,
|
|
||||||
aDai.address,
|
|
||||||
'1',
|
|
||||||
await aDai.name(),
|
|
||||||
owner.address,
|
|
||||||
spender.address,
|
|
||||||
nonce,
|
|
||||||
deadline,
|
|
||||||
permitAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
|
||||||
if (!ownerPrivateKey) {
|
|
||||||
throw new Error('INVALID_OWNER_PK');
|
|
||||||
}
|
|
||||||
|
|
||||||
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'INVALID_ALLOWANCE_BEFORE_PERMIT'
|
|
||||||
);
|
|
||||||
|
|
||||||
const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
|
||||||
|
|
||||||
await waitForTx(
|
|
||||||
await aDai
|
|
||||||
.connect(spender.signer)
|
|
||||||
.permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect((await aDai._nonces(owner.address)).toNumber()).to.be.equal(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Cancels the previous permit', async () => {
|
|
||||||
const { aDai, deployer, users } = testEnv;
|
|
||||||
const owner = deployer;
|
|
||||||
const spender = users[1];
|
|
||||||
|
|
||||||
const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
|
||||||
const deadline = MAX_UINT_AMOUNT;
|
|
||||||
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
|
||||||
const permitAmount = '0';
|
|
||||||
const msgParams = buildPermitParams(
|
|
||||||
chainId,
|
|
||||||
aDai.address,
|
|
||||||
'1',
|
|
||||||
await aDai.name(),
|
|
||||||
owner.address,
|
|
||||||
spender.address,
|
|
||||||
nonce,
|
|
||||||
deadline,
|
|
||||||
permitAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
|
||||||
if (!ownerPrivateKey) {
|
|
||||||
throw new Error('INVALID_OWNER_PK');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
|
||||||
|
|
||||||
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
|
||||||
ethers.utils.parseEther('2'),
|
|
||||||
'INVALID_ALLOWANCE_BEFORE_PERMIT'
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitForTx(
|
|
||||||
await aDai
|
|
||||||
.connect(spender.signer)
|
|
||||||
.permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
|
|
||||||
);
|
|
||||||
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
|
||||||
permitAmount,
|
|
||||||
'INVALID_ALLOWANCE_AFTER_PERMIT'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect((await aDai._nonces(owner.address)).toNumber()).to.be.equal(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to submit a permit with invalid nonce', async () => {
|
|
||||||
const { aDai, deployer, users } = testEnv;
|
|
||||||
const owner = deployer;
|
|
||||||
const spender = users[1];
|
|
||||||
|
|
||||||
const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
|
||||||
const deadline = MAX_UINT_AMOUNT;
|
|
||||||
const nonce = 1000;
|
|
||||||
const permitAmount = '0';
|
|
||||||
const msgParams = buildPermitParams(
|
|
||||||
chainId,
|
|
||||||
aDai.address,
|
|
||||||
'1',
|
|
||||||
await aDai.name(),
|
|
||||||
owner.address,
|
|
||||||
spender.address,
|
|
||||||
nonce,
|
|
||||||
deadline,
|
|
||||||
permitAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
|
||||||
if (!ownerPrivateKey) {
|
|
||||||
throw new Error('INVALID_OWNER_PK');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
aDai
|
|
||||||
.connect(spender.signer)
|
|
||||||
.permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
|
|
||||||
).to.be.revertedWith('INVALID_SIGNATURE');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to submit a permit with invalid expiration (previous to the current block)', async () => {
|
|
||||||
const { aDai, deployer, users } = testEnv;
|
|
||||||
const owner = deployer;
|
|
||||||
const spender = users[1];
|
|
||||||
|
|
||||||
const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
|
||||||
const expiration = '1';
|
|
||||||
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
|
||||||
const permitAmount = '0';
|
|
||||||
const msgParams = buildPermitParams(
|
|
||||||
chainId,
|
|
||||||
aDai.address,
|
|
||||||
'1',
|
|
||||||
await aDai.name(),
|
|
||||||
owner.address,
|
|
||||||
spender.address,
|
|
||||||
nonce,
|
|
||||||
expiration,
|
|
||||||
permitAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
|
||||||
if (!ownerPrivateKey) {
|
|
||||||
throw new Error('INVALID_OWNER_PK');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
aDai
|
|
||||||
.connect(spender.signer)
|
|
||||||
.permit(owner.address, spender.address, expiration, permitAmount, v, r, s)
|
|
||||||
).to.be.revertedWith('INVALID_EXPIRATION');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to submit a permit with invalid signature', async () => {
|
|
||||||
const { aDai, deployer, users } = testEnv;
|
|
||||||
const owner = deployer;
|
|
||||||
const spender = users[1];
|
|
||||||
|
|
||||||
const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
|
||||||
const deadline = MAX_UINT_AMOUNT;
|
|
||||||
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
|
||||||
const permitAmount = '0';
|
|
||||||
const msgParams = buildPermitParams(
|
|
||||||
chainId,
|
|
||||||
aDai.address,
|
|
||||||
'1',
|
|
||||||
await aDai.name(),
|
|
||||||
owner.address,
|
|
||||||
spender.address,
|
|
||||||
nonce,
|
|
||||||
deadline,
|
|
||||||
permitAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
|
||||||
if (!ownerPrivateKey) {
|
|
||||||
throw new Error('INVALID_OWNER_PK');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
aDai
|
|
||||||
.connect(spender.signer)
|
|
||||||
.permit(owner.address, ZERO_ADDRESS, permitAmount, deadline, v, r, s)
|
|
||||||
).to.be.revertedWith('INVALID_SIGNATURE');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to submit a permit with invalid owner', async () => {
|
|
||||||
const { aDai, deployer, users } = testEnv;
|
|
||||||
const owner = deployer;
|
|
||||||
const spender = users[1];
|
|
||||||
|
|
||||||
const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
|
||||||
const expiration = MAX_UINT_AMOUNT;
|
|
||||||
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
|
||||||
const permitAmount = '0';
|
|
||||||
const msgParams = buildPermitParams(
|
|
||||||
chainId,
|
|
||||||
aDai.address,
|
|
||||||
'1',
|
|
||||||
await aDai.name(),
|
|
||||||
owner.address,
|
|
||||||
spender.address,
|
|
||||||
nonce,
|
|
||||||
expiration,
|
|
||||||
permitAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
|
||||||
if (!ownerPrivateKey) {
|
|
||||||
throw new Error('INVALID_OWNER_PK');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
aDai
|
|
||||||
.connect(spender.signer)
|
|
||||||
.permit(ZERO_ADDRESS, spender.address, expiration, permitAmount, v, r, s)
|
|
||||||
).to.be.revertedWith('INVALID_OWNER');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,99 +0,0 @@
|
||||||
import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../helpers/constants';
|
|
||||||
import { convertToCurrencyDecimals } from '../helpers/contracts-helpers';
|
|
||||||
import { expect } from 'chai';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import { RateMode, ProtocolErrors } from '../helpers/types';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { CommonsConfig } from '../markets/aave/commons';
|
|
||||||
|
|
||||||
const AAVE_REFERRAL = CommonsConfig.ProtocolGlobalParams.AaveReferral;
|
|
||||||
|
|
||||||
makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
|
||||||
const {
|
|
||||||
INVALID_FROM_BALANCE_AFTER_TRANSFER,
|
|
||||||
INVALID_TO_BALANCE_AFTER_TRANSFER,
|
|
||||||
VL_TRANSFER_NOT_ALLOWED,
|
|
||||||
} = ProtocolErrors;
|
|
||||||
|
|
||||||
it('User 0 deposits 1000 DAI, transfers to user 1', async () => {
|
|
||||||
const { users, pool, dai, aDai } = testEnv;
|
|
||||||
|
|
||||||
await dai.connect(users[0].signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//user 1 deposits 1000 DAI
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(users[0].signer)
|
|
||||||
.deposit(dai.address, amountDAItoDeposit, users[0].address, '0');
|
|
||||||
|
|
||||||
await aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit);
|
|
||||||
|
|
||||||
const name = await aDai.name();
|
|
||||||
|
|
||||||
expect(name).to.be.equal('Aave interest bearing DAI');
|
|
||||||
|
|
||||||
const fromBalance = await aDai.balanceOf(users[0].address);
|
|
||||||
const toBalance = await aDai.balanceOf(users[1].address);
|
|
||||||
|
|
||||||
expect(fromBalance.toString()).to.be.equal('0', INVALID_FROM_BALANCE_AFTER_TRANSFER);
|
|
||||||
expect(toBalance.toString()).to.be.equal(
|
|
||||||
amountDAItoDeposit.toString(),
|
|
||||||
INVALID_TO_BALANCE_AFTER_TRANSFER
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User 0 deposits 1 WETH and user 1 tries to borrow the WETH with the received DAI as collateral', async () => {
|
|
||||||
const { users, pool, weth, helpersContract } = testEnv;
|
|
||||||
const userAddress = await pool.signer.getAddress();
|
|
||||||
|
|
||||||
await weth.connect(users[0].signer).mint(await convertToCurrencyDecimals(weth.address, '1'));
|
|
||||||
|
|
||||||
await weth.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(users[0].signer)
|
|
||||||
.deposit(weth.address, ethers.utils.parseEther('1.0'), userAddress, '0');
|
|
||||||
await pool
|
|
||||||
.connect(users[1].signer)
|
|
||||||
.borrow(
|
|
||||||
weth.address,
|
|
||||||
ethers.utils.parseEther('0.1'),
|
|
||||||
RateMode.Stable,
|
|
||||||
AAVE_REFERRAL,
|
|
||||||
users[1].address
|
|
||||||
);
|
|
||||||
|
|
||||||
const userReserveData = await helpersContract.getUserReserveData(
|
|
||||||
weth.address,
|
|
||||||
users[1].address
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userReserveData.currentStableDebt.toString()).to.be.eq(ethers.utils.parseEther('0.1'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User 1 tries to transfer all the DAI used as collateral back to user 0 (revert expected)', async () => {
|
|
||||||
const { users, pool, aDai, dai, weth } = testEnv;
|
|
||||||
|
|
||||||
const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer),
|
|
||||||
VL_TRANSFER_NOT_ALLOWED
|
|
||||||
).to.be.revertedWith(VL_TRANSFER_NOT_ALLOWED);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User 1 tries to transfer a small amount of DAI used as collateral back to user 0', async () => {
|
|
||||||
const { users, pool, aDai, dai, weth } = testEnv;
|
|
||||||
|
|
||||||
const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '100');
|
|
||||||
|
|
||||||
await aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer);
|
|
||||||
|
|
||||||
const user0Balance = await aDai.balanceOf(users[0].address);
|
|
||||||
|
|
||||||
expect(user0Balance.toString()).to.be.eq(aDAItoTransfer.toString());
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,386 +0,0 @@
|
||||||
import { TestEnv, makeSuite } from './helpers/make-suite';
|
|
||||||
import { APPROVAL_AMOUNT_LENDING_POOL, RAY } from '../helpers/constants';
|
|
||||||
import { convertToCurrencyDecimals } from '../helpers/contracts-helpers';
|
|
||||||
import { ProtocolErrors } from '../helpers/types';
|
|
||||||
import { strategyWETH } from '../markets/aave/reservesConfigs';
|
|
||||||
|
|
||||||
const { expect } = require('chai');
|
|
||||||
|
|
||||||
makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => {
|
|
||||||
const {
|
|
||||||
CALLER_NOT_POOL_ADMIN,
|
|
||||||
LPC_RESERVE_LIQUIDITY_NOT_0,
|
|
||||||
RC_INVALID_LTV,
|
|
||||||
RC_INVALID_LIQ_THRESHOLD,
|
|
||||||
RC_INVALID_LIQ_BONUS,
|
|
||||||
RC_INVALID_DECIMALS,
|
|
||||||
RC_INVALID_RESERVE_FACTOR,
|
|
||||||
} = ProtocolErrors;
|
|
||||||
|
|
||||||
it('Reverts trying to set an invalid reserve factor', async () => {
|
|
||||||
const { configurator, weth } = testEnv;
|
|
||||||
|
|
||||||
const invalidReserveFactor = 65536;
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
configurator.setReserveFactor(weth.address, invalidReserveFactor)
|
|
||||||
).to.be.revertedWith(RC_INVALID_RESERVE_FACTOR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deactivates the ETH reserve', async () => {
|
|
||||||
const { configurator, weth, helpersContract } = testEnv;
|
|
||||||
await configurator.deactivateReserve(weth.address);
|
|
||||||
const { isActive } = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
expect(isActive).to.be.equal(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Rectivates the ETH reserve', async () => {
|
|
||||||
const { configurator, weth, helpersContract } = testEnv;
|
|
||||||
await configurator.activateReserve(weth.address);
|
|
||||||
|
|
||||||
const { isActive } = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on deactivateReserve ', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).deactivateReserve(weth.address),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on activateReserve ', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).activateReserve(weth.address),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Freezes the ETH reserve', async () => {
|
|
||||||
const { configurator, weth, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
await configurator.freezeReserve(weth.address);
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(true);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(true);
|
|
||||||
expect(decimals).to.be.equal(strategyWETH.reserveDecimals);
|
|
||||||
expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral);
|
|
||||||
expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold);
|
|
||||||
expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled);
|
|
||||||
expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Unfreezes the ETH reserve', async () => {
|
|
||||||
const { configurator, helpersContract, weth } = testEnv;
|
|
||||||
await configurator.unfreezeReserve(weth.address);
|
|
||||||
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(true);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(false);
|
|
||||||
expect(decimals).to.be.equal(strategyWETH.reserveDecimals);
|
|
||||||
expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral);
|
|
||||||
expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold);
|
|
||||||
expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled);
|
|
||||||
expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on freezeReserve ', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).freezeReserve(weth.address),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on unfreezeReserve ', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).unfreezeReserve(weth.address),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deactivates the ETH reserve for borrowing', async () => {
|
|
||||||
const { configurator, helpersContract, weth } = testEnv;
|
|
||||||
await configurator.disableBorrowingOnReserve(weth.address);
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(false);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(false);
|
|
||||||
expect(decimals).to.be.equal(strategyWETH.reserveDecimals);
|
|
||||||
expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral);
|
|
||||||
expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold);
|
|
||||||
expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled);
|
|
||||||
expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Activates the ETH reserve for borrowing', async () => {
|
|
||||||
const { configurator, weth, helpersContract } = testEnv;
|
|
||||||
await configurator.enableBorrowingOnReserve(weth.address, true);
|
|
||||||
const { variableBorrowIndex } = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(true);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(false);
|
|
||||||
expect(decimals).to.be.equal(strategyWETH.reserveDecimals);
|
|
||||||
expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral);
|
|
||||||
expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold);
|
|
||||||
expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled);
|
|
||||||
expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor);
|
|
||||||
|
|
||||||
expect(variableBorrowIndex.toString()).to.be.equal(RAY);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on disableBorrowingOnReserve ', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).disableBorrowingOnReserve(weth.address),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on enableBorrowingOnReserve ', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).enableBorrowingOnReserve(weth.address, true),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deactivates the ETH reserve as collateral', async () => {
|
|
||||||
const { configurator, helpersContract, weth } = testEnv;
|
|
||||||
await configurator.configureReserveAsCollateral(weth.address, 0, 0, 0);
|
|
||||||
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(true);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(false);
|
|
||||||
expect(decimals).to.be.equal(18);
|
|
||||||
expect(ltv).to.be.equal(0);
|
|
||||||
expect(liquidationThreshold).to.be.equal(0);
|
|
||||||
expect(liquidationBonus).to.be.equal(0);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(true);
|
|
||||||
expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Activates the ETH reserve as collateral', async () => {
|
|
||||||
const { configurator, helpersContract, weth } = testEnv;
|
|
||||||
await configurator.configureReserveAsCollateral(weth.address, '8000', '8250', '10500');
|
|
||||||
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(true);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(false);
|
|
||||||
expect(decimals).to.be.equal(strategyWETH.reserveDecimals);
|
|
||||||
expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral);
|
|
||||||
expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold);
|
|
||||||
expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled);
|
|
||||||
expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on configureReserveAsCollateral ', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator
|
|
||||||
.connect(users[2].signer)
|
|
||||||
.configureReserveAsCollateral(weth.address, '7500', '8000', '10500'),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Disable stable borrow rate on the ETH reserve', async () => {
|
|
||||||
const { configurator, helpersContract, weth } = testEnv;
|
|
||||||
await configurator.disableReserveStableRate(weth.address);
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(true);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(false);
|
|
||||||
expect(decimals).to.be.equal(strategyWETH.reserveDecimals);
|
|
||||||
expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral);
|
|
||||||
expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold);
|
|
||||||
expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(false);
|
|
||||||
expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Enables stable borrow rate on the ETH reserve', async () => {
|
|
||||||
const { configurator, helpersContract, weth } = testEnv;
|
|
||||||
await configurator.enableReserveStableRate(weth.address);
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(true);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(false);
|
|
||||||
expect(decimals).to.be.equal(strategyWETH.reserveDecimals);
|
|
||||||
expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral);
|
|
||||||
expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold);
|
|
||||||
expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(true);
|
|
||||||
expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on disableReserveStableRate', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).disableReserveStableRate(weth.address),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyAaveAdmin on enableReserveStableRate', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).enableReserveStableRate(weth.address),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Changes the reserve factor of WETH', async () => {
|
|
||||||
const { configurator, helpersContract, weth } = testEnv;
|
|
||||||
await configurator.setReserveFactor(weth.address, '1000');
|
|
||||||
const {
|
|
||||||
decimals,
|
|
||||||
ltv,
|
|
||||||
liquidationBonus,
|
|
||||||
liquidationThreshold,
|
|
||||||
reserveFactor,
|
|
||||||
stableBorrowRateEnabled,
|
|
||||||
borrowingEnabled,
|
|
||||||
isActive,
|
|
||||||
isFrozen,
|
|
||||||
} = await helpersContract.getReserveConfigurationData(weth.address);
|
|
||||||
|
|
||||||
expect(borrowingEnabled).to.be.equal(true);
|
|
||||||
expect(isActive).to.be.equal(true);
|
|
||||||
expect(isFrozen).to.be.equal(false);
|
|
||||||
expect(decimals).to.be.equal(strategyWETH.reserveDecimals);
|
|
||||||
expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral);
|
|
||||||
expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold);
|
|
||||||
expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus);
|
|
||||||
expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled);
|
|
||||||
expect(reserveFactor).to.be.equal(1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Check the onlyLendingPoolManager on setReserveFactor', async () => {
|
|
||||||
const { configurator, users, weth } = testEnv;
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[2].signer).setReserveFactor(weth.address, '2000'),
|
|
||||||
CALLER_NOT_POOL_ADMIN
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Reverts when trying to disable the DAI reserve with liquidity on it', async () => {
|
|
||||||
const { dai, pool, configurator } = testEnv;
|
|
||||||
const userAddress = await pool.signer.getAddress();
|
|
||||||
await dai.mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
//user 1 deposits 1000 DAI
|
|
||||||
await pool.deposit(dai.address, amountDAItoDeposit, userAddress, '0');
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
configurator.deactivateReserve(dai.address),
|
|
||||||
LPC_RESERVE_LIQUIDITY_NOT_0
|
|
||||||
).to.be.revertedWith(LPC_RESERVE_LIQUIDITY_NOT_0);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,64 +0,0 @@
|
||||||
import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../helpers/constants';
|
|
||||||
import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants';
|
|
||||||
import { buildPermitParams, getSignatureFromTypedData } from '../helpers/contracts-helpers';
|
|
||||||
import { expect } from 'chai';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import { eEthereumNetwork, ProtocolErrors } from '../helpers/types';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { DRE } from '../helpers/misc-utils';
|
|
||||||
import {
|
|
||||||
ConfigNames,
|
|
||||||
getATokenDomainSeparatorPerNetwork,
|
|
||||||
getTreasuryAddress,
|
|
||||||
loadPoolConfig,
|
|
||||||
} from '../helpers/configuration';
|
|
||||||
import { waitForTx } from '../helpers/misc-utils';
|
|
||||||
import {
|
|
||||||
deployDelegationAwareAToken,
|
|
||||||
deployMintableDelegationERC20,
|
|
||||||
} from '../helpers/contracts-deployments';
|
|
||||||
import { DelegationAwareATokenFactory } from '../types';
|
|
||||||
import { DelegationAwareAToken } from '../types/DelegationAwareAToken';
|
|
||||||
import { MintableDelegationERC20 } from '../types/MintableDelegationERC20';
|
|
||||||
import AaveConfig from '../markets/aave';
|
|
||||||
|
|
||||||
const { parseEther } = ethers.utils;
|
|
||||||
|
|
||||||
makeSuite('AToken: underlying delegation', (testEnv: TestEnv) => {
|
|
||||||
const poolConfig = loadPoolConfig(ConfigNames.Commons);
|
|
||||||
let delegationAToken = <DelegationAwareAToken>{};
|
|
||||||
let delegationERC20 = <MintableDelegationERC20>{};
|
|
||||||
|
|
||||||
it('Deploys a new MintableDelegationERC20 and a DelegationAwareAToken', async () => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
|
|
||||||
delegationERC20 = await deployMintableDelegationERC20(['DEL', 'DEL', '18']);
|
|
||||||
|
|
||||||
delegationAToken = await deployDelegationAwareAToken(
|
|
||||||
[pool.address, delegationERC20.address, await getTreasuryAddress(AaveConfig), ZERO_ADDRESS, 'aDEL', 'aDEL'],
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
//await delegationAToken.initialize(pool.address, ZERO_ADDRESS, delegationERC20.address, ZERO_ADDRESS, '18', 'aDEL', 'aDEL');
|
|
||||||
|
|
||||||
console.log((await delegationAToken.decimals()).toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to delegate with the caller not being the Aave admin', async () => {
|
|
||||||
const { users } = testEnv;
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
delegationAToken.connect(users[1].signer).delegateUnderlyingTo(users[2].address)
|
|
||||||
).to.be.revertedWith(ProtocolErrors.CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to delegate to user 2', async () => {
|
|
||||||
const { users } = testEnv;
|
|
||||||
|
|
||||||
await delegationAToken.delegateUnderlyingTo(users[2].address);
|
|
||||||
|
|
||||||
const delegateeAddress = await delegationERC20.delegatee();
|
|
||||||
|
|
||||||
expect(delegateeAddress).to.be.equal(users[2].address);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,497 +0,0 @@
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
|
|
||||||
import { TestEnv, makeSuite } from './helpers/make-suite';
|
|
||||||
import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../helpers/constants';
|
|
||||||
import { convertToCurrencyDecimals, getContract } from '../helpers/contracts-helpers';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import { MockFlashLoanReceiver } from '../types/MockFlashLoanReceiver';
|
|
||||||
import { ProtocolErrors, eContractid } from '../helpers/types';
|
|
||||||
import { VariableDebtToken } from '../types/VariableDebtToken';
|
|
||||||
import { StableDebtToken } from '../types/StableDebtToken';
|
|
||||||
import {
|
|
||||||
getMockFlashLoanReceiver,
|
|
||||||
getStableDebtToken,
|
|
||||||
getVariableDebtToken,
|
|
||||||
} from '../helpers/contracts-getters';
|
|
||||||
|
|
||||||
const { expect } = require('chai');
|
|
||||||
|
|
||||||
makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|
||||||
let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;
|
|
||||||
const {
|
|
||||||
VL_COLLATERAL_BALANCE_IS_0,
|
|
||||||
TRANSFER_AMOUNT_EXCEEDS_BALANCE,
|
|
||||||
LP_INVALID_FLASHLOAN_MODE,
|
|
||||||
SAFEERC20_LOWLEVEL_CALL,
|
|
||||||
LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN,
|
|
||||||
LP_BORROW_ALLOWANCE_NOT_ENOUGH,
|
|
||||||
} = ProtocolErrors;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
_mockFlashLoanReceiver = await getMockFlashLoanReceiver();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deposits WETH into the reserve', async () => {
|
|
||||||
const { pool, weth } = testEnv;
|
|
||||||
const userAddress = await pool.signer.getAddress();
|
|
||||||
const amountToDeposit = ethers.utils.parseEther('1');
|
|
||||||
|
|
||||||
await weth.mint(amountToDeposit);
|
|
||||||
|
|
||||||
await weth.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
await pool.deposit(weth.address, amountToDeposit, userAddress, '0');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Takes WETH flashloan with mode = 0, returns the funds correctly', async () => {
|
|
||||||
const { pool, helpersContract, weth } = testEnv;
|
|
||||||
|
|
||||||
await pool.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[ethers.utils.parseEther('0.8')],
|
|
||||||
[0],
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
);
|
|
||||||
|
|
||||||
ethers.utils.parseUnits('10000');
|
|
||||||
|
|
||||||
const reserveData = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const currentLiquidityRate = reserveData.liquidityRate;
|
|
||||||
const currentLiquidityIndex = reserveData.liquidityIndex;
|
|
||||||
|
|
||||||
const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString())
|
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
|
||||||
|
|
||||||
expect(totalLiquidity.toString()).to.be.equal('1000720000000000000');
|
|
||||||
expect(currentLiquidityRate.toString()).to.be.equal('0');
|
|
||||||
expect(currentLiquidityIndex.toString()).to.be.equal('1000720000000000000000000000');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => {
|
|
||||||
const { pool, helpersContract, weth } = testEnv;
|
|
||||||
|
|
||||||
const reserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
const txResult = await pool.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
['1000720000000000000'],
|
|
||||||
[0],
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
);
|
|
||||||
|
|
||||||
const reserveData = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const currentLiqudityRate = reserveData.liquidityRate;
|
|
||||||
const currentLiquidityIndex = reserveData.liquidityIndex;
|
|
||||||
|
|
||||||
const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString())
|
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
|
||||||
|
|
||||||
expect(totalLiquidity.toString()).to.be.equal('1001620648000000000');
|
|
||||||
expect(currentLiqudityRate.toString()).to.be.equal('0');
|
|
||||||
expect(currentLiquidityIndex.toString()).to.be.equal('1001620648000000000000000000');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Takes WETH flashloan, does not return the funds with mode = 0. (revert expected)', async () => {
|
|
||||||
const { pool, weth, users } = testEnv;
|
|
||||||
const caller = users[1];
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[ethers.utils.parseEther('0.8')],
|
|
||||||
[0],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
)
|
|
||||||
).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Takes WETH flashloan, simulating a receiver as EOA (revert expected)', async () => {
|
|
||||||
const { pool, weth, users } = testEnv;
|
|
||||||
const caller = users[1];
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
await _mockFlashLoanReceiver.setSimulateEOA(true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[ethers.utils.parseEther('0.8')],
|
|
||||||
[0],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
)
|
|
||||||
).to.be.revertedWith(LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Takes a WETH flashloan with an invalid mode. (revert expected)', async () => {
|
|
||||||
const { pool, weth, users } = testEnv;
|
|
||||||
const caller = users[1];
|
|
||||||
await _mockFlashLoanReceiver.setSimulateEOA(false);
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[ethers.utils.parseEther('0.8')],
|
|
||||||
[4],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
)
|
|
||||||
).to.be.reverted;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Caller deposits 1000 DAI as collateral, Takes WETH flashloan with mode = 2, does not return the funds. A variable loan for caller is created', async () => {
|
|
||||||
const { dai, pool, weth, users, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const caller = users[1];
|
|
||||||
|
|
||||||
await dai.connect(caller.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
await dai.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await pool.connect(caller.signer).deposit(dai.address, amountToDeposit, caller.address, '0');
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[ethers.utils.parseEther('0.8')],
|
|
||||||
[2],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
);
|
|
||||||
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const wethDebtToken = await getVariableDebtToken(variableDebtTokenAddress);
|
|
||||||
|
|
||||||
const callerDebt = await wethDebtToken.balanceOf(caller.address);
|
|
||||||
|
|
||||||
expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
|
|
||||||
const { pool, weth, users } = testEnv;
|
|
||||||
const caller = users[1];
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool.connect(caller.signer).flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
['1004415000000000000'], //slightly higher than the available liquidity
|
|
||||||
[2],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
),
|
|
||||||
TRANSFER_AMOUNT_EXCEEDS_BALANCE
|
|
||||||
).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => {
|
|
||||||
const { pool, deployer, weth, users } = testEnv;
|
|
||||||
const caller = users[1];
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool.flashLoan(
|
|
||||||
deployer.address,
|
|
||||||
[weth.address],
|
|
||||||
['1000000000000000000'],
|
|
||||||
[2],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
)
|
|
||||||
).to.be.reverted;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deposits USDC into the reserve', async () => {
|
|
||||||
const { usdc, pool } = testEnv;
|
|
||||||
const userAddress = await pool.signer.getAddress();
|
|
||||||
|
|
||||||
await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
|
||||||
|
|
||||||
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const amountToDeposit = await convertToCurrencyDecimals(usdc.address, '1000');
|
|
||||||
|
|
||||||
await pool.deposit(usdc.address, amountToDeposit, userAddress, '0');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => {
|
|
||||||
const { usdc, pool, helpersContract, deployer: depositor } = testEnv;
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
|
|
||||||
|
|
||||||
const reserveDataBefore = await helpersContract.getReserveData(usdc.address);
|
|
||||||
|
|
||||||
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
|
|
||||||
|
|
||||||
await pool.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[usdc.address],
|
|
||||||
[flashloanAmount],
|
|
||||||
[0],
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
);
|
|
||||||
|
|
||||||
const reserveDataAfter = helpersContract.getReserveData(usdc.address);
|
|
||||||
|
|
||||||
const reserveData = await helpersContract.getReserveData(usdc.address);
|
|
||||||
const userData = await helpersContract.getUserReserveData(usdc.address, depositor.address);
|
|
||||||
|
|
||||||
const totalLiquidity = reserveData.availableLiquidity
|
|
||||||
.add(reserveData.totalStableDebt)
|
|
||||||
.add(reserveData.totalVariableDebt)
|
|
||||||
.toString();
|
|
||||||
const currentLiqudityRate = reserveData.liquidityRate.toString();
|
|
||||||
const currentLiquidityIndex = reserveData.liquidityIndex.toString();
|
|
||||||
const currentUserBalance = userData.currentATokenBalance.toString();
|
|
||||||
|
|
||||||
const expectedLiquidity = await convertToCurrencyDecimals(usdc.address, '1000.450');
|
|
||||||
|
|
||||||
expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity');
|
|
||||||
expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate');
|
|
||||||
expect(currentLiquidityIndex).to.be.equal(
|
|
||||||
new BigNumber('1.00045').multipliedBy(oneRay).toFixed(),
|
|
||||||
'Invalid liquidity index'
|
|
||||||
);
|
|
||||||
expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Takes out a 500 USDC flashloan with mode = 0, does not return the funds. (revert expected)', async () => {
|
|
||||||
const { usdc, pool, users } = testEnv;
|
|
||||||
const caller = users[2];
|
|
||||||
|
|
||||||
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[usdc.address],
|
|
||||||
[flashloanAmount],
|
|
||||||
[2],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
)
|
|
||||||
).to.be.revertedWith(VL_COLLATERAL_BALANCE_IS_0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Caller deposits 5 WETH as collateral, Takes a USDC flashloan with mode = 2, does not return the funds. A loan for caller is created', async () => {
|
|
||||||
const { usdc, pool, weth, users, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const caller = users[2];
|
|
||||||
|
|
||||||
await weth.connect(caller.signer).mint(await convertToCurrencyDecimals(weth.address, '5'));
|
|
||||||
|
|
||||||
await weth.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const amountToDeposit = await convertToCurrencyDecimals(weth.address, '5');
|
|
||||||
|
|
||||||
await pool.connect(caller.signer).deposit(weth.address, amountToDeposit, caller.address, '0');
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[usdc.address],
|
|
||||||
[flashloanAmount],
|
|
||||||
[2],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
);
|
|
||||||
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
usdc.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const usdcDebtToken = await getVariableDebtToken(variableDebtTokenAddress);
|
|
||||||
|
|
||||||
const callerDebt = await usdcDebtToken.balanceOf(caller.address);
|
|
||||||
|
|
||||||
expect(callerDebt.toString()).to.be.equal('500000000', 'Invalid user debt');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Caller deposits 1000 DAI as collateral, Takes a WETH flashloan with mode = 0, does not approve the transfer of the funds', async () => {
|
|
||||||
const { dai, pool, weth, users } = testEnv;
|
|
||||||
const caller = users[3];
|
|
||||||
|
|
||||||
await dai.connect(caller.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
await dai.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await pool.connect(caller.signer).deposit(dai.address, amountToDeposit, caller.address, '0');
|
|
||||||
|
|
||||||
const flashAmount = ethers.utils.parseEther('0.8');
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
|
|
||||||
await _mockFlashLoanReceiver.setAmountToApprove(flashAmount.div(2));
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[flashAmount],
|
|
||||||
[0],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
)
|
|
||||||
).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Caller takes a WETH flashloan with mode = 1', async () => {
|
|
||||||
const { dai, pool, weth, users, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const caller = users[3];
|
|
||||||
|
|
||||||
const flashAmount = ethers.utils.parseEther('0.8');
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[flashAmount],
|
|
||||||
[1],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
);
|
|
||||||
|
|
||||||
const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const wethDebtToken = await getStableDebtToken(stableDebtTokenAddress);
|
|
||||||
|
|
||||||
const callerDebt = await wethDebtToken.balanceOf(caller.address);
|
|
||||||
|
|
||||||
expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user without allowance', async () => {
|
|
||||||
const { dai, pool, weth, users, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const caller = users[5];
|
|
||||||
const onBehalfOf = users[4];
|
|
||||||
|
|
||||||
// Deposit 1000 dai for onBehalfOf user
|
|
||||||
await dai.connect(onBehalfOf.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
await dai.connect(onBehalfOf.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(onBehalfOf.signer)
|
|
||||||
.deposit(dai.address, amountToDeposit, onBehalfOf.address, '0');
|
|
||||||
|
|
||||||
const flashAmount = ethers.utils.parseEther('0.8');
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[flashAmount],
|
|
||||||
[1],
|
|
||||||
onBehalfOf.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
)
|
|
||||||
).to.be.revertedWith(LP_BORROW_ALLOWANCE_NOT_ENOUGH);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user with allowance. A loan for onBehalfOf is creatd.', async () => {
|
|
||||||
const { dai, pool, weth, users, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const caller = users[5];
|
|
||||||
const onBehalfOf = users[4];
|
|
||||||
|
|
||||||
const flashAmount = ethers.utils.parseEther('0.8');
|
|
||||||
|
|
||||||
const reserveData = await pool.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const stableDebtToken = await getVariableDebtToken(reserveData.stableDebtTokenAddress);
|
|
||||||
|
|
||||||
// Deposited for onBehalfOf user already, delegate borrow allowance
|
|
||||||
await stableDebtToken.connect(onBehalfOf.signer).approveDelegation(caller.address, flashAmount);
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[flashAmount],
|
|
||||||
[1],
|
|
||||||
onBehalfOf.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
);
|
|
||||||
|
|
||||||
const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const wethDebtToken = await getStableDebtToken(stableDebtTokenAddress);
|
|
||||||
|
|
||||||
const onBehalfOfDebt = await wethDebtToken.balanceOf(onBehalfOf.address);
|
|
||||||
|
|
||||||
expect(onBehalfOfDebt.toString()).to.be.equal(
|
|
||||||
'800000000000000000',
|
|
||||||
'Invalid onBehalfOf user debt'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,772 +0,0 @@
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
|
|
||||||
import {
|
|
||||||
calcExpectedReserveDataAfterBorrow,
|
|
||||||
calcExpectedReserveDataAfterDeposit,
|
|
||||||
calcExpectedReserveDataAfterRepay,
|
|
||||||
calcExpectedReserveDataAfterStableRateRebalance,
|
|
||||||
calcExpectedReserveDataAfterSwapRateMode,
|
|
||||||
calcExpectedReserveDataAfterWithdraw,
|
|
||||||
calcExpectedUserDataAfterBorrow,
|
|
||||||
calcExpectedUserDataAfterDeposit,
|
|
||||||
calcExpectedUserDataAfterRepay,
|
|
||||||
calcExpectedUserDataAfterSetUseAsCollateral,
|
|
||||||
calcExpectedUserDataAfterStableRateRebalance,
|
|
||||||
calcExpectedUserDataAfterSwapRateMode,
|
|
||||||
calcExpectedUserDataAfterWithdraw,
|
|
||||||
} from './utils/calculations';
|
|
||||||
import { getReserveAddressFromSymbol, getReserveData, getUserData } from './utils/helpers';
|
|
||||||
|
|
||||||
import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers';
|
|
||||||
import {
|
|
||||||
getAToken,
|
|
||||||
getMintableERC20,
|
|
||||||
getStableDebtToken,
|
|
||||||
getVariableDebtToken,
|
|
||||||
} from '../../helpers/contracts-getters';
|
|
||||||
import { MAX_UINT_AMOUNT, ONE_YEAR } from '../../helpers/constants';
|
|
||||||
import { SignerWithAddress, TestEnv } from './make-suite';
|
|
||||||
import { advanceTimeAndBlock, DRE, timeLatest, waitForTx } from '../../helpers/misc-utils';
|
|
||||||
|
|
||||||
import chai from 'chai';
|
|
||||||
import { ReserveData, UserReserveData } from './utils/interfaces';
|
|
||||||
import { ContractReceipt } from 'ethers';
|
|
||||||
import { AToken } from '../../types/AToken';
|
|
||||||
import { RateMode, tEthereumAddress } from '../../helpers/types';
|
|
||||||
|
|
||||||
const { expect } = chai;
|
|
||||||
|
|
||||||
const almostEqualOrEqual = function (
|
|
||||||
this: any,
|
|
||||||
expected: ReserveData | UserReserveData,
|
|
||||||
actual: ReserveData | UserReserveData
|
|
||||||
) {
|
|
||||||
const keys = Object.keys(actual);
|
|
||||||
|
|
||||||
keys.forEach((key) => {
|
|
||||||
if (
|
|
||||||
key === 'lastUpdateTimestamp' ||
|
|
||||||
key === 'marketStableRate' ||
|
|
||||||
key === 'symbol' ||
|
|
||||||
key === 'aTokenAddress' ||
|
|
||||||
key === 'decimals' ||
|
|
||||||
key === 'totalStableDebtLastUpdated'
|
|
||||||
) {
|
|
||||||
// skipping consistency check on accessory data
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.assert(actual[key] != undefined, `Property ${key} is undefined in the actual data`);
|
|
||||||
expect(expected[key] != undefined, `Property ${key} is undefined in the expected data`);
|
|
||||||
|
|
||||||
if (expected[key] == null || actual[key] == null) {
|
|
||||||
console.log('Found a undefined value for Key ', key, ' value ', expected[key], actual[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actual[key] instanceof BigNumber) {
|
|
||||||
const actualValue = (<BigNumber>actual[key]).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
const expectedValue = (<BigNumber>expected[key]).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
this.assert(
|
|
||||||
actualValue.eq(expectedValue) ||
|
|
||||||
actualValue.plus(1).eq(expectedValue) ||
|
|
||||||
actualValue.eq(expectedValue.plus(1)) ||
|
|
||||||
actualValue.plus(2).eq(expectedValue) ||
|
|
||||||
actualValue.eq(expectedValue.plus(2)) ||
|
|
||||||
actualValue.plus(3).eq(expectedValue) ||
|
|
||||||
actualValue.eq(expectedValue.plus(3)),
|
|
||||||
`expected #{act} to be almost equal or equal #{exp} for property ${key}`,
|
|
||||||
`expected #{act} to be almost equal or equal #{exp} for property ${key}`,
|
|
||||||
expectedValue.toFixed(0),
|
|
||||||
actualValue.toFixed(0)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.assert(
|
|
||||||
actual[key] !== null &&
|
|
||||||
expected[key] !== null &&
|
|
||||||
actual[key].toString() === expected[key].toString(),
|
|
||||||
`expected #{act} to be equal #{exp} for property ${key}`,
|
|
||||||
`expected #{act} to be equal #{exp} for property ${key}`,
|
|
||||||
expected[key],
|
|
||||||
actual[key]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
chai.use(function (chai: any, utils: any) {
|
|
||||||
chai.Assertion.overwriteMethod('almostEqualOrEqual', function (original: any) {
|
|
||||||
return function (this: any, expected: ReserveData | UserReserveData) {
|
|
||||||
const actual = (expected as ReserveData)
|
|
||||||
? <ReserveData>this._obj
|
|
||||||
: <UserReserveData>this._obj;
|
|
||||||
|
|
||||||
almostEqualOrEqual.apply(this, [expected, actual]);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
interface ActionsConfig {
|
|
||||||
skipIntegrityCheck: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const configuration: ActionsConfig = <ActionsConfig>{};
|
|
||||||
|
|
||||||
export const mint = async (reserveSymbol: string, amount: string, user: SignerWithAddress) => {
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const token = await getMintableERC20(reserve);
|
|
||||||
|
|
||||||
await waitForTx(
|
|
||||||
await token.connect(user.signer).mint(await convertToCurrencyDecimals(reserve, amount))
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const approve = async (reserveSymbol: string, user: SignerWithAddress, testEnv: TestEnv) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const token = await getMintableERC20(reserve);
|
|
||||||
|
|
||||||
await waitForTx(
|
|
||||||
await token.connect(user.signer).approve(pool.address, '100000000000000000000000000000')
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deposit = async (
|
|
||||||
reserveSymbol: string,
|
|
||||||
amount: string,
|
|
||||||
sender: SignerWithAddress,
|
|
||||||
onBehalfOf: tEthereumAddress,
|
|
||||||
sendValue: string,
|
|
||||||
expectedResult: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
revertMessage?: string
|
|
||||||
) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const amountToDeposit = await convertToCurrencyDecimals(reserve, amount);
|
|
||||||
|
|
||||||
const txOptions: any = {};
|
|
||||||
|
|
||||||
const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData(
|
|
||||||
reserve,
|
|
||||||
onBehalfOf,
|
|
||||||
testEnv,
|
|
||||||
sender.address
|
|
||||||
);
|
|
||||||
|
|
||||||
if (sendValue) {
|
|
||||||
txOptions.value = await convertToCurrencyDecimals(reserve, sendValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log("Depositing %s %s, expecting %s", amountToDeposit.toString(), reserveSymbol, expectedResult);
|
|
||||||
|
|
||||||
if (expectedResult === 'success') {
|
|
||||||
const txResult = await waitForTx(
|
|
||||||
await pool
|
|
||||||
.connect(sender.signer)
|
|
||||||
.deposit(reserve, amountToDeposit, onBehalfOf, '0', txOptions)
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
|
||||||
reserveData: reserveDataAfter,
|
|
||||||
userData: userDataAfter,
|
|
||||||
timestamp,
|
|
||||||
} = await getContractsData(reserve, onBehalfOf, testEnv, sender.address);
|
|
||||||
|
|
||||||
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
|
|
||||||
|
|
||||||
const expectedReserveData = calcExpectedReserveDataAfterDeposit(
|
|
||||||
amountToDeposit.toString(),
|
|
||||||
reserveDataBefore,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedUserReserveData = calcExpectedUserDataAfterDeposit(
|
|
||||||
amountToDeposit.toString(),
|
|
||||||
reserveDataBefore,
|
|
||||||
expectedReserveData,
|
|
||||||
userDataBefore,
|
|
||||||
txTimestamp,
|
|
||||||
timestamp,
|
|
||||||
txCost
|
|
||||||
);
|
|
||||||
|
|
||||||
expectEqual(reserveDataAfter, expectedReserveData);
|
|
||||||
expectEqual(userDataAfter, expectedUserReserveData);
|
|
||||||
|
|
||||||
// truffleAssert.eventEmitted(txResult, "Deposit", (ev: any) => {
|
|
||||||
// const {_reserve, _user, _amount} = ev;
|
|
||||||
// return (
|
|
||||||
// _reserve === reserve &&
|
|
||||||
// _user === user &&
|
|
||||||
// new BigNumber(_amount).isEqualTo(new BigNumber(amountToDeposit))
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
} else if (expectedResult === 'revert') {
|
|
||||||
await expect(
|
|
||||||
pool.connect(sender.signer).deposit(reserve, amountToDeposit, onBehalfOf, '0', txOptions),
|
|
||||||
revertMessage
|
|
||||||
).to.be.reverted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const withdraw = async (
|
|
||||||
reserveSymbol: string,
|
|
||||||
amount: string,
|
|
||||||
user: SignerWithAddress,
|
|
||||||
expectedResult: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
revertMessage?: string
|
|
||||||
) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
|
|
||||||
const {
|
|
||||||
aTokenInstance,
|
|
||||||
reserve,
|
|
||||||
userData: userDataBefore,
|
|
||||||
reserveData: reserveDataBefore,
|
|
||||||
} = await getDataBeforeAction(reserveSymbol, user.address, testEnv);
|
|
||||||
|
|
||||||
let amountToWithdraw = '0';
|
|
||||||
|
|
||||||
if (amount !== '-1') {
|
|
||||||
amountToWithdraw = (await convertToCurrencyDecimals(reserve, amount)).toString();
|
|
||||||
} else {
|
|
||||||
amountToWithdraw = MAX_UINT_AMOUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expectedResult === 'success') {
|
|
||||||
const txResult = await waitForTx(
|
|
||||||
await pool.connect(user.signer).withdraw(reserve, amountToWithdraw, user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
|
||||||
reserveData: reserveDataAfter,
|
|
||||||
userData: userDataAfter,
|
|
||||||
timestamp,
|
|
||||||
} = await getContractsData(reserve, user.address, testEnv);
|
|
||||||
|
|
||||||
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
|
|
||||||
|
|
||||||
const expectedReserveData = calcExpectedReserveDataAfterWithdraw(
|
|
||||||
amountToWithdraw,
|
|
||||||
reserveDataBefore,
|
|
||||||
userDataBefore,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedUserData = calcExpectedUserDataAfterWithdraw(
|
|
||||||
amountToWithdraw,
|
|
||||||
reserveDataBefore,
|
|
||||||
expectedReserveData,
|
|
||||||
userDataBefore,
|
|
||||||
txTimestamp,
|
|
||||||
timestamp,
|
|
||||||
txCost
|
|
||||||
);
|
|
||||||
|
|
||||||
expectEqual(reserveDataAfter, expectedReserveData);
|
|
||||||
expectEqual(userDataAfter, expectedUserData);
|
|
||||||
|
|
||||||
// truffleAssert.eventEmitted(txResult, "Redeem", (ev: any) => {
|
|
||||||
// const {_from, _value} = ev;
|
|
||||||
// return (
|
|
||||||
// _from === user && new BigNumber(_value).isEqualTo(actualAmountRedeemed)
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
} else if (expectedResult === 'revert') {
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).withdraw(reserve, amountToWithdraw, user.address),
|
|
||||||
revertMessage
|
|
||||||
).to.be.reverted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const delegateBorrowAllowance = async (
|
|
||||||
reserve: string,
|
|
||||||
amount: string,
|
|
||||||
interestRateMode: string,
|
|
||||||
user: SignerWithAddress,
|
|
||||||
receiver: tEthereumAddress,
|
|
||||||
expectedResult: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
revertMessage?: string
|
|
||||||
) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
|
|
||||||
const reserveAddress: tEthereumAddress = await getReserveAddressFromSymbol(reserve);
|
|
||||||
|
|
||||||
const amountToDelegate: string = await (
|
|
||||||
await convertToCurrencyDecimals(reserveAddress, amount)
|
|
||||||
).toString();
|
|
||||||
|
|
||||||
const reserveData = await pool.getReserveData(reserveAddress);
|
|
||||||
|
|
||||||
const debtToken =
|
|
||||||
interestRateMode === '1'
|
|
||||||
? await getStableDebtToken(reserveData.stableDebtTokenAddress)
|
|
||||||
: await getVariableDebtToken(reserveData.variableDebtTokenAddress);
|
|
||||||
|
|
||||||
const delegateAllowancePromise = debtToken
|
|
||||||
.connect(user.signer)
|
|
||||||
.approveDelegation(receiver, amountToDelegate);
|
|
||||||
|
|
||||||
if (expectedResult === 'revert' && revertMessage) {
|
|
||||||
await expect(delegateAllowancePromise, revertMessage).to.be.revertedWith(revertMessage);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
await waitForTx(await delegateAllowancePromise);
|
|
||||||
const allowance = await debtToken.borrowAllowance(user.address, receiver);
|
|
||||||
expect(allowance.toString()).to.be.equal(
|
|
||||||
amountToDelegate,
|
|
||||||
'borrowAllowance is set incorrectly'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const borrow = async (
|
|
||||||
reserveSymbol: string,
|
|
||||||
amount: string,
|
|
||||||
interestRateMode: string,
|
|
||||||
user: SignerWithAddress,
|
|
||||||
onBehalfOf: tEthereumAddress,
|
|
||||||
timeTravel: string,
|
|
||||||
expectedResult: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
revertMessage?: string
|
|
||||||
) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData(
|
|
||||||
reserve,
|
|
||||||
onBehalfOf,
|
|
||||||
testEnv,
|
|
||||||
user.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const amountToBorrow = await convertToCurrencyDecimals(reserve, amount);
|
|
||||||
//console.log("Borrowing %s %s with rate mode %s expecting", amountToBorrow.toString(), reserveSymbol, interestRateMode, expectedResult);
|
|
||||||
if (expectedResult === 'success') {
|
|
||||||
const txResult = await waitForTx(
|
|
||||||
await pool
|
|
||||||
.connect(user.signer)
|
|
||||||
.borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
|
|
||||||
|
|
||||||
if (timeTravel) {
|
|
||||||
const secondsToTravel = new BigNumber(timeTravel).multipliedBy(ONE_YEAR).div(365).toNumber();
|
|
||||||
|
|
||||||
await advanceTimeAndBlock(secondsToTravel);
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
reserveData: reserveDataAfter,
|
|
||||||
userData: userDataAfter,
|
|
||||||
timestamp,
|
|
||||||
} = await getContractsData(reserve, onBehalfOf, testEnv, user.address);
|
|
||||||
|
|
||||||
const expectedReserveData = calcExpectedReserveDataAfterBorrow(
|
|
||||||
amountToBorrow.toString(),
|
|
||||||
interestRateMode,
|
|
||||||
reserveDataBefore,
|
|
||||||
userDataBefore,
|
|
||||||
txTimestamp,
|
|
||||||
timestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedUserData = calcExpectedUserDataAfterBorrow(
|
|
||||||
amountToBorrow.toString(),
|
|
||||||
interestRateMode,
|
|
||||||
reserveDataBefore,
|
|
||||||
expectedReserveData,
|
|
||||||
userDataBefore,
|
|
||||||
txTimestamp,
|
|
||||||
timestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
expectEqual(reserveDataAfter, expectedReserveData);
|
|
||||||
expectEqual(userDataAfter, expectedUserData);
|
|
||||||
|
|
||||||
// truffleAssert.eventEmitted(txResult, "Borrow", (ev: any) => {
|
|
||||||
// const {
|
|
||||||
// _reserve,
|
|
||||||
// _user,
|
|
||||||
// _amount,
|
|
||||||
// _borrowRateMode,
|
|
||||||
// _borrowRate,
|
|
||||||
// _originationFee,
|
|
||||||
// } = ev;
|
|
||||||
// return (
|
|
||||||
// _reserve.toLowerCase() === reserve.toLowerCase() &&
|
|
||||||
// _user.toLowerCase() === user.toLowerCase() &&
|
|
||||||
// new BigNumber(_amount).eq(amountToBorrow) &&
|
|
||||||
// new BigNumber(_borrowRateMode).eq(expectedUserData.borrowRateMode) &&
|
|
||||||
// new BigNumber(_borrowRate).eq(expectedUserData.borrowRate) &&
|
|
||||||
// new BigNumber(_originationFee).eq(
|
|
||||||
// expectedUserData.originationFee.minus(userDataBefore.originationFee)
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
} else if (expectedResult === 'revert') {
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf),
|
|
||||||
revertMessage
|
|
||||||
).to.be.reverted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const repay = async (
|
|
||||||
reserveSymbol: string,
|
|
||||||
amount: string,
|
|
||||||
rateMode: string,
|
|
||||||
user: SignerWithAddress,
|
|
||||||
onBehalfOf: SignerWithAddress,
|
|
||||||
sendValue: string,
|
|
||||||
expectedResult: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
revertMessage?: string
|
|
||||||
) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData(
|
|
||||||
reserve,
|
|
||||||
onBehalfOf.address,
|
|
||||||
testEnv
|
|
||||||
);
|
|
||||||
|
|
||||||
let amountToRepay = '0';
|
|
||||||
|
|
||||||
if (amount !== '-1') {
|
|
||||||
amountToRepay = (await convertToCurrencyDecimals(reserve, amount)).toString();
|
|
||||||
} else {
|
|
||||||
amountToRepay = MAX_UINT_AMOUNT;
|
|
||||||
}
|
|
||||||
amountToRepay = '0x' + new BigNumber(amountToRepay).toString(16);
|
|
||||||
|
|
||||||
const txOptions: any = {};
|
|
||||||
|
|
||||||
if (sendValue) {
|
|
||||||
const valueToSend = await convertToCurrencyDecimals(reserve, sendValue);
|
|
||||||
txOptions.value = '0x' + new BigNumber(valueToSend.toString()).toString(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expectedResult === 'success') {
|
|
||||||
const txResult = await waitForTx(
|
|
||||||
await pool
|
|
||||||
.connect(user.signer)
|
|
||||||
.repay(reserve, amountToRepay, rateMode, onBehalfOf.address, txOptions)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
|
|
||||||
|
|
||||||
const {
|
|
||||||
reserveData: reserveDataAfter,
|
|
||||||
userData: userDataAfter,
|
|
||||||
timestamp,
|
|
||||||
} = await getContractsData(reserve, onBehalfOf.address, testEnv);
|
|
||||||
|
|
||||||
const expectedReserveData = calcExpectedReserveDataAfterRepay(
|
|
||||||
amountToRepay,
|
|
||||||
<RateMode>rateMode,
|
|
||||||
reserveDataBefore,
|
|
||||||
userDataBefore,
|
|
||||||
txTimestamp,
|
|
||||||
timestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedUserData = calcExpectedUserDataAfterRepay(
|
|
||||||
amountToRepay,
|
|
||||||
<RateMode>rateMode,
|
|
||||||
reserveDataBefore,
|
|
||||||
expectedReserveData,
|
|
||||||
userDataBefore,
|
|
||||||
user.address,
|
|
||||||
onBehalfOf.address,
|
|
||||||
txTimestamp,
|
|
||||||
timestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
expectEqual(reserveDataAfter, expectedReserveData);
|
|
||||||
expectEqual(userDataAfter, expectedUserData);
|
|
||||||
|
|
||||||
// truffleAssert.eventEmitted(txResult, "Repay", (ev: any) => {
|
|
||||||
// const {_reserve, _user, _repayer} = ev;
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// _reserve.toLowerCase() === reserve.toLowerCase() &&
|
|
||||||
// _user.toLowerCase() === onBehalfOf.toLowerCase() &&
|
|
||||||
// _repayer.toLowerCase() === user.toLowerCase()
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
} else if (expectedResult === 'revert') {
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(user.signer)
|
|
||||||
.repay(reserve, amountToRepay, rateMode, onBehalfOf.address, txOptions),
|
|
||||||
revertMessage
|
|
||||||
).to.be.reverted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setUseAsCollateral = async (
|
|
||||||
reserveSymbol: string,
|
|
||||||
user: SignerWithAddress,
|
|
||||||
useAsCollateral: string,
|
|
||||||
expectedResult: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
revertMessage?: string
|
|
||||||
) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData(
|
|
||||||
reserve,
|
|
||||||
user.address,
|
|
||||||
testEnv
|
|
||||||
);
|
|
||||||
|
|
||||||
const useAsCollateralBool = useAsCollateral.toLowerCase() === 'true';
|
|
||||||
|
|
||||||
if (expectedResult === 'success') {
|
|
||||||
const txResult = await waitForTx(
|
|
||||||
await pool.connect(user.signer).setUserUseReserveAsCollateral(reserve, useAsCollateralBool)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { txCost } = await getTxCostAndTimestamp(txResult);
|
|
||||||
|
|
||||||
const { userData: userDataAfter } = await getContractsData(reserve, user.address, testEnv);
|
|
||||||
|
|
||||||
const expectedUserData = calcExpectedUserDataAfterSetUseAsCollateral(
|
|
||||||
useAsCollateral.toLocaleLowerCase() === 'true',
|
|
||||||
reserveDataBefore,
|
|
||||||
userDataBefore,
|
|
||||||
txCost
|
|
||||||
);
|
|
||||||
|
|
||||||
expectEqual(userDataAfter, expectedUserData);
|
|
||||||
// if (useAsCollateralBool) {
|
|
||||||
// truffleAssert.eventEmitted(txResult, 'ReserveUsedAsCollateralEnabled', (ev: any) => {
|
|
||||||
// const {_reserve, _user} = ev;
|
|
||||||
// return _reserve === reserve && _user === user;
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// truffleAssert.eventEmitted(txResult, 'ReserveUsedAsCollateralDisabled', (ev: any) => {
|
|
||||||
// const {_reserve, _user} = ev;
|
|
||||||
// return _reserve === reserve && _user === user;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
} else if (expectedResult === 'revert') {
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).setUserUseReserveAsCollateral(reserve, useAsCollateralBool),
|
|
||||||
revertMessage
|
|
||||||
).to.be.reverted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const swapBorrowRateMode = async (
|
|
||||||
reserveSymbol: string,
|
|
||||||
user: SignerWithAddress,
|
|
||||||
rateMode: string,
|
|
||||||
expectedResult: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
revertMessage?: string
|
|
||||||
) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData(
|
|
||||||
reserve,
|
|
||||||
user.address,
|
|
||||||
testEnv
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expectedResult === 'success') {
|
|
||||||
const txResult = await waitForTx(
|
|
||||||
await pool.connect(user.signer).swapBorrowRateMode(reserve, rateMode)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
|
|
||||||
|
|
||||||
const { reserveData: reserveDataAfter, userData: userDataAfter } = await getContractsData(
|
|
||||||
reserve,
|
|
||||||
user.address,
|
|
||||||
testEnv
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedReserveData = calcExpectedReserveDataAfterSwapRateMode(
|
|
||||||
reserveDataBefore,
|
|
||||||
userDataBefore,
|
|
||||||
rateMode,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedUserData = calcExpectedUserDataAfterSwapRateMode(
|
|
||||||
reserveDataBefore,
|
|
||||||
expectedReserveData,
|
|
||||||
userDataBefore,
|
|
||||||
rateMode,
|
|
||||||
txCost,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
expectEqual(reserveDataAfter, expectedReserveData);
|
|
||||||
expectEqual(userDataAfter, expectedUserData);
|
|
||||||
|
|
||||||
// truffleAssert.eventEmitted(txResult, "Swap", (ev: any) => {
|
|
||||||
// const {_user, _reserve, _newRateMode, _newRate} = ev;
|
|
||||||
// return (
|
|
||||||
// _user === user &&
|
|
||||||
// _reserve == reserve &&
|
|
||||||
// new BigNumber(_newRateMode).eq(expectedUserData.borrowRateMode) &&
|
|
||||||
// new BigNumber(_newRate).eq(expectedUserData.borrowRate)
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
} else if (expectedResult === 'revert') {
|
|
||||||
await expect(pool.connect(user.signer).swapBorrowRateMode(reserve, rateMode), revertMessage).to
|
|
||||||
.be.reverted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const rebalanceStableBorrowRate = async (
|
|
||||||
reserveSymbol: string,
|
|
||||||
user: SignerWithAddress,
|
|
||||||
target: SignerWithAddress,
|
|
||||||
expectedResult: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
revertMessage?: string
|
|
||||||
) => {
|
|
||||||
const { pool } = testEnv;
|
|
||||||
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData(
|
|
||||||
reserve,
|
|
||||||
target.address,
|
|
||||||
testEnv
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expectedResult === 'success') {
|
|
||||||
const txResult = await waitForTx(
|
|
||||||
await pool.connect(user.signer).rebalanceStableBorrowRate(reserve, target.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
|
|
||||||
|
|
||||||
const { reserveData: reserveDataAfter, userData: userDataAfter } = await getContractsData(
|
|
||||||
reserve,
|
|
||||||
target.address,
|
|
||||||
testEnv
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedReserveData = calcExpectedReserveDataAfterStableRateRebalance(
|
|
||||||
reserveDataBefore,
|
|
||||||
userDataBefore,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedUserData = calcExpectedUserDataAfterStableRateRebalance(
|
|
||||||
reserveDataBefore,
|
|
||||||
expectedReserveData,
|
|
||||||
userDataBefore,
|
|
||||||
txCost,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
expectEqual(reserveDataAfter, expectedReserveData);
|
|
||||||
expectEqual(userDataAfter, expectedUserData);
|
|
||||||
|
|
||||||
// truffleAssert.eventEmitted(txResult, 'RebalanceStableBorrowRate', (ev: any) => {
|
|
||||||
// const {_user, _reserve, _newStableRate} = ev;
|
|
||||||
// return (
|
|
||||||
// _user.toLowerCase() === target.toLowerCase() &&
|
|
||||||
// _reserve.toLowerCase() === reserve.toLowerCase() &&
|
|
||||||
// new BigNumber(_newStableRate).eq(expectedUserData.borrowRate)
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
} else if (expectedResult === 'revert') {
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).rebalanceStableBorrowRate(reserve, target.address),
|
|
||||||
revertMessage
|
|
||||||
).to.be.reverted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const expectEqual = (
|
|
||||||
actual: UserReserveData | ReserveData,
|
|
||||||
expected: UserReserveData | ReserveData
|
|
||||||
) => {
|
|
||||||
if (!configuration.skipIntegrityCheck) {
|
|
||||||
// @ts-ignore
|
|
||||||
expect(actual).to.be.almostEqualOrEqual(expected);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ActionData {
|
|
||||||
reserve: string;
|
|
||||||
reserveData: ReserveData;
|
|
||||||
userData: UserReserveData;
|
|
||||||
aTokenInstance: AToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getDataBeforeAction = async (
|
|
||||||
reserveSymbol: string,
|
|
||||||
user: tEthereumAddress,
|
|
||||||
testEnv: TestEnv
|
|
||||||
): Promise<ActionData> => {
|
|
||||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
|
||||||
|
|
||||||
const { reserveData, userData } = await getContractsData(reserve, user, testEnv);
|
|
||||||
const aTokenInstance = await getAToken(reserveData.aTokenAddress);
|
|
||||||
return {
|
|
||||||
reserve,
|
|
||||||
reserveData,
|
|
||||||
userData,
|
|
||||||
aTokenInstance,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getTxCostAndTimestamp = async (tx: ContractReceipt) => {
|
|
||||||
if (!tx.blockNumber || !tx.transactionHash || !tx.cumulativeGasUsed) {
|
|
||||||
throw new Error('No tx blocknumber');
|
|
||||||
}
|
|
||||||
const txTimestamp = new BigNumber((await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp);
|
|
||||||
|
|
||||||
const txInfo = await DRE.ethers.provider.getTransaction(tx.transactionHash);
|
|
||||||
const txCost = new BigNumber(tx.cumulativeGasUsed.toString()).multipliedBy(
|
|
||||||
txInfo.gasPrice.toString()
|
|
||||||
);
|
|
||||||
|
|
||||||
return { txCost, txTimestamp };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getContractsData = async (
|
|
||||||
reserve: string,
|
|
||||||
user: string,
|
|
||||||
testEnv: TestEnv,
|
|
||||||
sender?: string
|
|
||||||
) => {
|
|
||||||
const { pool, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const [userData, reserveData, timestamp] = await Promise.all([
|
|
||||||
getUserData(pool, helpersContract, reserve, user, sender || user),
|
|
||||||
getReserveData(helpersContract, reserve),
|
|
||||||
timeLatest(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
reserveData,
|
|
||||||
userData,
|
|
||||||
timestamp: new BigNumber(timestamp),
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,31 +0,0 @@
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
|
|
||||||
function almostEqualAssertion(this: any, expected: any, actual: any, message: string): any {
|
|
||||||
this.assert(
|
|
||||||
expected.plus(new BigNumber(1)).eq(actual) ||
|
|
||||||
expected.plus(new BigNumber(2)).eq(actual) ||
|
|
||||||
actual.plus(new BigNumber(1)).eq(expected) ||
|
|
||||||
actual.plus(new BigNumber(2)).eq(expected) ||
|
|
||||||
expected.eq(actual),
|
|
||||||
`${message} expected #{act} to be almost equal #{exp}`,
|
|
||||||
`${message} expected #{act} to be different from #{exp}`,
|
|
||||||
expected.toString(),
|
|
||||||
actual.toString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function almostEqual() {
|
|
||||||
return function (chai: any, utils: any) {
|
|
||||||
chai.Assertion.overwriteMethod('almostEqual', function (original: any) {
|
|
||||||
return function (this: any, value: any, message: string) {
|
|
||||||
if (utils.flag(this, 'bignumber')) {
|
|
||||||
var expected = new BigNumber(value);
|
|
||||||
var actual = new BigNumber(this._obj);
|
|
||||||
almostEqualAssertion.apply(this, [expected, actual, message]);
|
|
||||||
} else {
|
|
||||||
original.apply(this, arguments);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,191 +0,0 @@
|
||||||
import { evmRevert, evmSnapshot, DRE } from '../../helpers/misc-utils';
|
|
||||||
import { Signer } from 'ethers';
|
|
||||||
import {
|
|
||||||
getLendingPool,
|
|
||||||
getLendingPoolAddressesProvider,
|
|
||||||
getAaveProtocolDataProvider,
|
|
||||||
getAToken,
|
|
||||||
getMintableERC20,
|
|
||||||
getLendingPoolConfiguratorProxy,
|
|
||||||
getPriceOracle,
|
|
||||||
getLendingPoolAddressesProviderRegistry,
|
|
||||||
getWETHMocked,
|
|
||||||
getWETHGateway,
|
|
||||||
getUniswapLiquiditySwapAdapter,
|
|
||||||
getUniswapRepayAdapter,
|
|
||||||
getFlashLiquidationAdapter,
|
|
||||||
} from '../../helpers/contracts-getters';
|
|
||||||
import { eEthereumNetwork, tEthereumAddress } from '../../helpers/types';
|
|
||||||
import { LendingPool } from '../../types/LendingPool';
|
|
||||||
import { AaveProtocolDataProvider } from '../../types/AaveProtocolDataProvider';
|
|
||||||
import { MintableERC20 } from '../../types/MintableERC20';
|
|
||||||
import { AToken } from '../../types/AToken';
|
|
||||||
import { LendingPoolConfigurator } from '../../types/LendingPoolConfigurator';
|
|
||||||
|
|
||||||
import chai from 'chai';
|
|
||||||
// @ts-ignore
|
|
||||||
import bignumberChai from 'chai-bignumber';
|
|
||||||
import { almostEqual } from './almost-equal';
|
|
||||||
import { PriceOracle } from '../../types/PriceOracle';
|
|
||||||
import { LendingPoolAddressesProvider } from '../../types/LendingPoolAddressesProvider';
|
|
||||||
import { LendingPoolAddressesProviderRegistry } from '../../types/LendingPoolAddressesProviderRegistry';
|
|
||||||
import { getEthersSigners } from '../../helpers/contracts-helpers';
|
|
||||||
import { UniswapLiquiditySwapAdapter } from '../../types/UniswapLiquiditySwapAdapter';
|
|
||||||
import { UniswapRepayAdapter } from '../../types/UniswapRepayAdapter';
|
|
||||||
import { getParamPerNetwork } from '../../helpers/contracts-helpers';
|
|
||||||
import { WETH9Mocked } from '../../types/WETH9Mocked';
|
|
||||||
import { WETHGateway } from '../../types/WETHGateway';
|
|
||||||
import { solidity } from 'ethereum-waffle';
|
|
||||||
import { AaveConfig } from '../../markets/aave';
|
|
||||||
import { FlashLiquidationAdapter } from '../../types';
|
|
||||||
import { HardhatRuntimeEnvironment } from 'hardhat/types';
|
|
||||||
import { usingTenderly } from '../../helpers/tenderly-utils';
|
|
||||||
|
|
||||||
chai.use(bignumberChai());
|
|
||||||
chai.use(almostEqual());
|
|
||||||
chai.use(solidity);
|
|
||||||
|
|
||||||
export interface SignerWithAddress {
|
|
||||||
signer: Signer;
|
|
||||||
address: tEthereumAddress;
|
|
||||||
}
|
|
||||||
export interface TestEnv {
|
|
||||||
deployer: SignerWithAddress;
|
|
||||||
users: SignerWithAddress[];
|
|
||||||
pool: LendingPool;
|
|
||||||
configurator: LendingPoolConfigurator;
|
|
||||||
oracle: PriceOracle;
|
|
||||||
helpersContract: AaveProtocolDataProvider;
|
|
||||||
weth: WETH9Mocked;
|
|
||||||
aWETH: AToken;
|
|
||||||
dai: MintableERC20;
|
|
||||||
aDai: AToken;
|
|
||||||
usdc: MintableERC20;
|
|
||||||
aave: MintableERC20;
|
|
||||||
addressesProvider: LendingPoolAddressesProvider;
|
|
||||||
uniswapLiquiditySwapAdapter: UniswapLiquiditySwapAdapter;
|
|
||||||
uniswapRepayAdapter: UniswapRepayAdapter;
|
|
||||||
registry: LendingPoolAddressesProviderRegistry;
|
|
||||||
wethGateway: WETHGateway;
|
|
||||||
flashLiquidationAdapter: FlashLiquidationAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
let buidlerevmSnapshotId: string = '0x1';
|
|
||||||
const setBuidlerevmSnapshotId = (id: string) => {
|
|
||||||
buidlerevmSnapshotId = id;
|
|
||||||
};
|
|
||||||
|
|
||||||
const testEnv: TestEnv = {
|
|
||||||
deployer: {} as SignerWithAddress,
|
|
||||||
users: [] as SignerWithAddress[],
|
|
||||||
pool: {} as LendingPool,
|
|
||||||
configurator: {} as LendingPoolConfigurator,
|
|
||||||
helpersContract: {} as AaveProtocolDataProvider,
|
|
||||||
oracle: {} as PriceOracle,
|
|
||||||
weth: {} as WETH9Mocked,
|
|
||||||
aWETH: {} as AToken,
|
|
||||||
dai: {} as MintableERC20,
|
|
||||||
aDai: {} as AToken,
|
|
||||||
usdc: {} as MintableERC20,
|
|
||||||
aave: {} as MintableERC20,
|
|
||||||
addressesProvider: {} as LendingPoolAddressesProvider,
|
|
||||||
uniswapLiquiditySwapAdapter: {} as UniswapLiquiditySwapAdapter,
|
|
||||||
uniswapRepayAdapter: {} as UniswapRepayAdapter,
|
|
||||||
flashLiquidationAdapter: {} as FlashLiquidationAdapter,
|
|
||||||
registry: {} as LendingPoolAddressesProviderRegistry,
|
|
||||||
wethGateway: {} as WETHGateway,
|
|
||||||
} as TestEnv;
|
|
||||||
|
|
||||||
export async function initializeMakeSuite() {
|
|
||||||
const [_deployer, ...restSigners] = await getEthersSigners();
|
|
||||||
const deployer: SignerWithAddress = {
|
|
||||||
address: await _deployer.getAddress(),
|
|
||||||
signer: _deployer,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const signer of restSigners) {
|
|
||||||
testEnv.users.push({
|
|
||||||
signer,
|
|
||||||
address: await signer.getAddress(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
testEnv.deployer = deployer;
|
|
||||||
testEnv.pool = await getLendingPool();
|
|
||||||
|
|
||||||
testEnv.configurator = await getLendingPoolConfiguratorProxy();
|
|
||||||
|
|
||||||
testEnv.addressesProvider = await getLendingPoolAddressesProvider();
|
|
||||||
|
|
||||||
if (process.env.MAINNET_FORK === 'true') {
|
|
||||||
testEnv.registry = await getLendingPoolAddressesProviderRegistry(
|
|
||||||
getParamPerNetwork(AaveConfig.ProviderRegistry, eEthereumNetwork.main)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
testEnv.registry = await getLendingPoolAddressesProviderRegistry();
|
|
||||||
testEnv.oracle = await getPriceOracle();
|
|
||||||
}
|
|
||||||
|
|
||||||
testEnv.helpersContract = await getAaveProtocolDataProvider();
|
|
||||||
|
|
||||||
const allTokens = await testEnv.helpersContract.getAllATokens();
|
|
||||||
const aDaiAddress = allTokens.find((aToken) => aToken.symbol === 'aDAI')?.tokenAddress;
|
|
||||||
|
|
||||||
const aWEthAddress = allTokens.find((aToken) => aToken.symbol === 'aWETH')?.tokenAddress;
|
|
||||||
|
|
||||||
const reservesTokens = await testEnv.helpersContract.getAllReservesTokens();
|
|
||||||
|
|
||||||
const daiAddress = reservesTokens.find((token) => token.symbol === 'DAI')?.tokenAddress;
|
|
||||||
const usdcAddress = reservesTokens.find((token) => token.symbol === 'USDC')?.tokenAddress;
|
|
||||||
const aaveAddress = reservesTokens.find((token) => token.symbol === 'AAVE')?.tokenAddress;
|
|
||||||
const wethAddress = reservesTokens.find((token) => token.symbol === 'WETH')?.tokenAddress;
|
|
||||||
|
|
||||||
if (!aDaiAddress || !aWEthAddress) {
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
if (!daiAddress || !usdcAddress || !aaveAddress || !wethAddress) {
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
testEnv.aDai = await getAToken(aDaiAddress);
|
|
||||||
testEnv.aWETH = await getAToken(aWEthAddress);
|
|
||||||
|
|
||||||
testEnv.dai = await getMintableERC20(daiAddress);
|
|
||||||
testEnv.usdc = await getMintableERC20(usdcAddress);
|
|
||||||
testEnv.aave = await getMintableERC20(aaveAddress);
|
|
||||||
testEnv.weth = await getWETHMocked(wethAddress);
|
|
||||||
testEnv.wethGateway = await getWETHGateway();
|
|
||||||
|
|
||||||
testEnv.uniswapLiquiditySwapAdapter = await getUniswapLiquiditySwapAdapter();
|
|
||||||
testEnv.uniswapRepayAdapter = await getUniswapRepayAdapter();
|
|
||||||
testEnv.flashLiquidationAdapter = await getFlashLiquidationAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
const setSnapshot = async () => {
|
|
||||||
const hre = DRE as HardhatRuntimeEnvironment;
|
|
||||||
if (usingTenderly()) {
|
|
||||||
setBuidlerevmSnapshotId((await hre.tenderlyRPC.getHead()) || '0x1');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setBuidlerevmSnapshotId(await evmSnapshot());
|
|
||||||
};
|
|
||||||
|
|
||||||
const revertHead = async () => {
|
|
||||||
const hre = DRE as HardhatRuntimeEnvironment;
|
|
||||||
if (usingTenderly()) {
|
|
||||||
await hre.tenderlyRPC.setHead(buidlerevmSnapshotId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await evmRevert(buidlerevmSnapshotId);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
|
|
||||||
describe(name, () => {
|
|
||||||
before(async () => {
|
|
||||||
await setSnapshot();
|
|
||||||
});
|
|
||||||
tests(testEnv);
|
|
||||||
after(async () => {
|
|
||||||
await revertHead();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,237 +0,0 @@
|
||||||
import { TestEnv, SignerWithAddress } from './make-suite';
|
|
||||||
import {
|
|
||||||
mint,
|
|
||||||
approve,
|
|
||||||
deposit,
|
|
||||||
borrow,
|
|
||||||
withdraw,
|
|
||||||
repay,
|
|
||||||
setUseAsCollateral,
|
|
||||||
swapBorrowRateMode,
|
|
||||||
rebalanceStableBorrowRate,
|
|
||||||
delegateBorrowAllowance,
|
|
||||||
} from './actions';
|
|
||||||
import { RateMode } from '../../helpers/types';
|
|
||||||
|
|
||||||
export interface Action {
|
|
||||||
name: string;
|
|
||||||
args?: any;
|
|
||||||
expected: string;
|
|
||||||
revertMessage?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Story {
|
|
||||||
description: string;
|
|
||||||
actions: Action[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Scenario {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
stories: Story[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const executeStory = async (story: Story, testEnv: TestEnv) => {
|
|
||||||
for (const action of story.actions) {
|
|
||||||
const { users } = testEnv;
|
|
||||||
await executeAction(action, users, testEnv);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const executeAction = async (action: Action, users: SignerWithAddress[], testEnv: TestEnv) => {
|
|
||||||
const { reserve, user: userIndex, borrowRateMode } = action.args;
|
|
||||||
const { name, expected, revertMessage } = action;
|
|
||||||
|
|
||||||
if (!name || name === '') {
|
|
||||||
throw 'Action name is missing';
|
|
||||||
}
|
|
||||||
if (!reserve || reserve === '') {
|
|
||||||
throw 'Invalid reserve selected for deposit';
|
|
||||||
}
|
|
||||||
if (!userIndex || userIndex === '') {
|
|
||||||
throw `Invalid user selected to deposit into the ${reserve} reserve`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!expected || expected === '') {
|
|
||||||
throw `An expected resut for action ${name} is required`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rateMode: string = RateMode.None;
|
|
||||||
|
|
||||||
if (borrowRateMode) {
|
|
||||||
if (borrowRateMode === 'none') {
|
|
||||||
rateMode = RateMode.None;
|
|
||||||
} else if (borrowRateMode === 'stable') {
|
|
||||||
rateMode = RateMode.Stable;
|
|
||||||
} else if (borrowRateMode === 'variable') {
|
|
||||||
rateMode = RateMode.Variable;
|
|
||||||
} else {
|
|
||||||
//random value, to test improper selection of the parameter
|
|
||||||
rateMode = '4';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = users[parseInt(userIndex)];
|
|
||||||
|
|
||||||
switch (name) {
|
|
||||||
case 'mint':
|
|
||||||
const { amount } = action.args;
|
|
||||||
|
|
||||||
if (!amount || amount === '') {
|
|
||||||
throw `Invalid amount of ${reserve} to mint`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await mint(reserve, amount, user);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'approve':
|
|
||||||
await approve(reserve, user, testEnv);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'deposit':
|
|
||||||
{
|
|
||||||
const { amount, sendValue, onBehalfOf: onBehalfOfIndex } = action.args;
|
|
||||||
const onBehalfOf = onBehalfOfIndex
|
|
||||||
? users[parseInt(onBehalfOfIndex)].address
|
|
||||||
: user.address;
|
|
||||||
|
|
||||||
if (!amount || amount === '') {
|
|
||||||
throw `Invalid amount to deposit into the ${reserve} reserve`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await deposit(
|
|
||||||
reserve,
|
|
||||||
amount,
|
|
||||||
user,
|
|
||||||
onBehalfOf,
|
|
||||||
sendValue,
|
|
||||||
expected,
|
|
||||||
testEnv,
|
|
||||||
revertMessage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'delegateBorrowAllowance':
|
|
||||||
{
|
|
||||||
const { amount, toUser: toUserIndex } = action.args;
|
|
||||||
const toUser = users[parseInt(toUserIndex, 10)].address;
|
|
||||||
if (!amount || amount === '') {
|
|
||||||
throw `Invalid amount to deposit into the ${reserve} reserve`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await delegateBorrowAllowance(
|
|
||||||
reserve,
|
|
||||||
amount,
|
|
||||||
rateMode,
|
|
||||||
user,
|
|
||||||
toUser,
|
|
||||||
expected,
|
|
||||||
testEnv,
|
|
||||||
revertMessage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'withdraw':
|
|
||||||
{
|
|
||||||
const { amount } = action.args;
|
|
||||||
|
|
||||||
if (!amount || amount === '') {
|
|
||||||
throw `Invalid amount to withdraw from the ${reserve} reserve`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await withdraw(reserve, amount, user, expected, testEnv, revertMessage);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'borrow':
|
|
||||||
{
|
|
||||||
const { amount, timeTravel, onBehalfOf: onBehalfOfIndex } = action.args;
|
|
||||||
|
|
||||||
const onBehalfOf = onBehalfOfIndex
|
|
||||||
? users[parseInt(onBehalfOfIndex)].address
|
|
||||||
: user.address;
|
|
||||||
|
|
||||||
if (!amount || amount === '') {
|
|
||||||
throw `Invalid amount to borrow from the ${reserve} reserve`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await borrow(
|
|
||||||
reserve,
|
|
||||||
amount,
|
|
||||||
rateMode,
|
|
||||||
user,
|
|
||||||
onBehalfOf,
|
|
||||||
timeTravel,
|
|
||||||
expected,
|
|
||||||
testEnv,
|
|
||||||
revertMessage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'repay':
|
|
||||||
{
|
|
||||||
const { amount, borrowRateMode, sendValue } = action.args;
|
|
||||||
let { onBehalfOf: onBehalfOfIndex } = action.args;
|
|
||||||
|
|
||||||
if (!amount || amount === '') {
|
|
||||||
throw `Invalid amount to repay into the ${reserve} reserve`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let userToRepayOnBehalf: SignerWithAddress;
|
|
||||||
if (!onBehalfOfIndex || onBehalfOfIndex === '') {
|
|
||||||
console.log(
|
|
||||||
'WARNING: No onBehalfOf specified for a repay action. Defaulting to the repayer address'
|
|
||||||
);
|
|
||||||
userToRepayOnBehalf = user;
|
|
||||||
} else {
|
|
||||||
userToRepayOnBehalf = users[parseInt(onBehalfOfIndex)];
|
|
||||||
}
|
|
||||||
|
|
||||||
await repay(
|
|
||||||
reserve,
|
|
||||||
amount,
|
|
||||||
rateMode,
|
|
||||||
user,
|
|
||||||
userToRepayOnBehalf,
|
|
||||||
sendValue,
|
|
||||||
expected,
|
|
||||||
testEnv,
|
|
||||||
revertMessage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'setUseAsCollateral':
|
|
||||||
{
|
|
||||||
const { useAsCollateral } = action.args;
|
|
||||||
|
|
||||||
if (!useAsCollateral || useAsCollateral === '') {
|
|
||||||
throw `A valid value for useAsCollateral needs to be set when calling setUseReserveAsCollateral on reserve ${reserve}`;
|
|
||||||
}
|
|
||||||
await setUseAsCollateral(reserve, user, useAsCollateral, expected, testEnv, revertMessage);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'swapBorrowRateMode':
|
|
||||||
await swapBorrowRateMode(reserve, user, rateMode, expected, testEnv, revertMessage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rebalanceStableBorrowRate':
|
|
||||||
{
|
|
||||||
const { target: targetIndex } = action.args;
|
|
||||||
|
|
||||||
if (!targetIndex || targetIndex === '') {
|
|
||||||
throw `A target must be selected when trying to rebalance a stable rate`;
|
|
||||||
}
|
|
||||||
const target = users[parseInt(targetIndex)];
|
|
||||||
|
|
||||||
await rebalanceStableBorrowRate(reserve, user, target, expected, testEnv, revertMessage);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw `Invalid action requested: ${name}`;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,142 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: Borrow negatives (reverts)",
|
|
||||||
"description": "Test cases for the deposit function.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 DAI, user 1 deposits 1 WETH as collateral and tries to borrow 100 DAI with rate mode NONE (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "none",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Invalid interest rate mode selected"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 DAI, user 1 deposits 1 WETH as collateral and tries to borrow 100 DAI with an invalid rate mode (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "invalid",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Invalid interest rate mode selected"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,656 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: Borrow/repay (stable rate)",
|
|
||||||
"description": "Test cases for the borrow function, stable mode.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 DAI, user 1 deposits 1 WETH as collateral and borrows 100 DAI at stable rate",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 tries to borrow the rest of the DAI liquidity (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "900",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "There is not enough collateral to cover a new borrow"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 repays half of the DAI borrow after one year",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 10 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "10",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "50",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 repays the rest of the DAI borrow after one year",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 15 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "15",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws the deposited DAI plus interest",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 1000 DAI, user 2 tries to borrow 1000 DAI at a stable rate without any collateral (revert expected) User 1 withdrawws",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "The collateral balance is 0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 DAI, user 1,2,3,4 deposit 1 WETH each and borrow 100 DAI at stable rate. Everything is repaid, user 0 withdraws",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "2",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "3",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "4"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "4"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "4"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "4",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 15 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "15",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 20 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "20",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "2",
|
|
||||||
"onBehalfOf": "2",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 30 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "30",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "3",
|
|
||||||
"onBehalfOf": "3",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 30 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "30",
|
|
||||||
"user": "4"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "4"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "4",
|
|
||||||
"onBehalfOf": "4",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 DAI, user 1 deposits 2 WETH and borrow 100 DAI at stable rate first, then 100 DAI at variable rate, repays everything. User 0 withdraws",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 50 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "50",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,945 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: Borrow/repay (variable rate)",
|
|
||||||
"description": "Test cases for the borrow function, variable mode.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 2 deposits 1 DAI to account for rounding errors",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 DAI, user 1 deposits 1 WETH as collateral and borrows 100 DAI at variable rate",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 tries to borrow the rest of the DAI liquidity (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "900",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "There is not enough collateral to cover a new borrow"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 tries to repay 0 DAI (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "0",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Amount must be greater than 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 repays a small amount of DAI, enough to cover a small part of the interest",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1.25",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 repays the DAI borrow after one year",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 10 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "10",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws the deposited DAI plus interest",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 withdraws the collateral",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 2 deposits a small amount of WETH to account for rounding errors",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.001",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.001",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1 WETH, user 1 deposits 100 LINK as collateral and borrows 0.5 ETH at variable rate",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "LINK",
|
|
||||||
"amount": "100",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "LINK",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "LINK",
|
|
||||||
"amount": "100",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.5",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 tries to repay 0 ETH",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Amount must be greater than 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 2 tries to repay everything on behalf of user 1 using uint(-1) (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "2",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"onBehalfOf": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "To repay on behalf of an user an explicit amount to repay is needed"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 3 repays a small amount of WETH on behalf of user 1",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.2",
|
|
||||||
"user": "3",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"onBehalfOf": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 repays the WETH borrow after one year",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws the deposited WETH plus interest",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 withdraws the collateral",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "LINK",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"description": "User 2 deposits 1 USDC to account for rounding errors",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 USDC, user 1 deposits 1 WETH as collateral and borrows 100 USDC at variable rate",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 tries to borrow the rest of the USDC liquidity (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "900",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "There is not enough collateral to cover a new borrow"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 repays the USDC borrow after one year",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 10 USDC to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "10",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws the deposited USDC plus interest",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 withdraws the collateral",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 1000 DAI, user 3 tries to borrow 1000 DAI without any collateral (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "The collateral balance is 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "user 3 deposits 0.1 ETH collateral to borrow 100 DAI; 0.1 ETH is not enough to borrow 100 DAI (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.1",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "There is not enough collateral to cover a new borrow"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "user 3 withdraws the 0.1 ETH",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 1000 USDC, user 3 tries to borrow 1000 USDC without any collateral (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "The collateral balance is 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "user 3 deposits 0.1 ETH collateral to borrow 100 USDC; 0.1 ETH is not enough to borrow 100 USDC (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.1",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "There is not enough collateral to cover a new borrow"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "user 3 withdraws the 0.1 ETH",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 DAI, user 6 deposits 2 WETH and borrow 100 DAI at variable rate first, then 100 DAI at stable rate, repays everything. User 0 withdraws",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "6"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "6"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "6"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "6",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "6",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 50 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "50",
|
|
||||||
"user": "6"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "6"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "6",
|
|
||||||
"onBehalfOf": "6",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "6",
|
|
||||||
"onBehalfOf": "6",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: credit delegation",
|
|
||||||
"description": "Test cases for the credit delegation related functions.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 3 deposits 1000 WETH. User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH on variable to user 4, user 4 borrows 1 WETH variable on behalf of user 0",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "3"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "delegateBorrowAllowance",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "0",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"toUser": "4"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "4",
|
|
||||||
"onBehalfOf": "0",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 4 trying to borrow 1 WETH stable on behalf of user 0, revert expected",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "4",
|
|
||||||
"onBehalfOf": "0",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "59"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 3 WETH variable on behalf of user 0, revert expected",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "delegateBorrowAllowance",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"toUser": "4"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "3",
|
|
||||||
"user": "4",
|
|
||||||
"onBehalfOf": "0",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "59"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 delegates borrowing of 1 WETH on stable to user 2, user 2 borrows 1 WETH stable on behalf of user 0",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "delegateBorrowAllowance",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"toUser": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "2",
|
|
||||||
"onBehalfOf": "0",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,266 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: Deposit",
|
|
||||||
"description": "Test cases for the deposit function.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 0 Deposits 1000 DAI in an empty reserve",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 1000 DAI after user 0",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 USDC in an empty reserve",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 1000 USDC after user 0",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1 WETH in an empty reserve",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 1 WETH after user 0",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 0 WETH (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Amount must be greater than 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 0 DAI",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "0",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Amount must be greater than 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 deposits 100 DAI on behalf of user 2, user 2 tries to borrow 0.1 WETH",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.1",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "2"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,198 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: Rebalance stable rate",
|
|
||||||
"description": "Test cases for the rebalanceStableBorrowRate() function.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 0 tries to rebalance user 1 who has no borrows in progress (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "rebalanceStableBorrowRate",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0",
|
|
||||||
"target": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "User does not have any stable rate loan for this reserve"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 USDC, user 1 deposits 7 WETH, borrows 250 USDC at a stable rate, user 0 rebalances user 1 (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "7",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "7",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "250",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "rebalanceStableBorrowRate",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0",
|
|
||||||
"target": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "The user borrow is variable and cannot be rebalanced"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 borrows another 200 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "200",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "rebalanceStableBorrowRate",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0",
|
|
||||||
"target": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Interest rate rebalance conditions were not met"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 borrows another 200 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "200",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "rebalanceStableBorrowRate",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0",
|
|
||||||
"target": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Interest rate rebalance conditions were not met"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 borrows another 100 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "280",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "rebalanceStableBorrowRate",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0",
|
|
||||||
"target": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Interest rate rebalance conditions were not met"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"description": "User 1 borrows the remaining USDC (usage ratio = 100%) at variable. User 0 rebalances user 1",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "20",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "rebalanceStableBorrowRate",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0",
|
|
||||||
"target": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,236 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: Usage as collateral",
|
|
||||||
"description": "Test cases for the setUserUseReserveAsCollateral() function.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 0 Deposits 1000 DAI, disables DAI as collateral",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "setUseAsCollateral",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0",
|
|
||||||
"useAsCollateral": "false"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 Deposits 2 WETH, disables WETH as collateral, borrows 400 DAI (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "setUseAsCollateral",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1",
|
|
||||||
"useAsCollateral": "false"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "400",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "The collateral balance is 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 enables WETH as collateral, borrows 400 DAI",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "setUseAsCollateral",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1",
|
|
||||||
"useAsCollateral": "true"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "400",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 disables WETH as collateral (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "setUseAsCollateral",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1",
|
|
||||||
"useAsCollateral": "false"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "User deposit is already being used as collateral"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 Deposits 10 AAVE, disables WETH as collateral. Should revert as 10 AAVE are not enough to cover the debt (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "AAVE",
|
|
||||||
"amount": "10",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "AAVE",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "AAVE",
|
|
||||||
"amount": "10",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "setUseAsCollateral",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1",
|
|
||||||
"useAsCollateral": "false"
|
|
||||||
},
|
|
||||||
"expected": "revert"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 Deposits 640 more AAVE (enough to cover the DAI debt), disables WETH as collateral",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "AAVE",
|
|
||||||
"amount": "640",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "AAVE",
|
|
||||||
"amount": "640",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "setUseAsCollateral",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1",
|
|
||||||
"useAsCollateral": "false"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 disables AAVE as collateral (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "setUseAsCollateral",
|
|
||||||
"args": {
|
|
||||||
"reserve": "AAVE",
|
|
||||||
"user": "1",
|
|
||||||
"useAsCollateral": "false"
|
|
||||||
},
|
|
||||||
"expected": "revert"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 reenables WETH as collateral",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "setUseAsCollateral",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1",
|
|
||||||
"useAsCollateral": "true"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: Swap rate mode",
|
|
||||||
"description": "Test cases for the swapBorrowRateMode() function.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 0 tries to swap rate mode without any variable rate loan in progress (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "swapBorrowRateMode",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "User does not have a variable rate loan in progress on this reserve"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 tries to swap rate mode without any stable rate loan in progress (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "swapBorrowRateMode",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "User does not have a stable rate loan in progress on this reserve"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 deposits 1000 DAI, user 1 deposits 2 ETH as collateral, borrows 100 DAI at variable rate and swaps to stable after one year",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "2",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "variable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "swapBorrowRateMode",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 1 borrows another 100 DAI, and swaps back to variable after one year, repays the loan",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"borrowRateMode": "stable",
|
|
||||||
"user": "1",
|
|
||||||
"timeTravel": "365"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "swapBorrowRateMode",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"description": "Mint 50 DAI to cover the interest",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "50",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "repay",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1",
|
|
||||||
"onBehalfOf": "1",
|
|
||||||
"borrowRateMode": "variable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: Redeem negative test cases",
|
|
||||||
"description": "Redeem function.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "Users 0 Deposits 1000 DAI and tries to redeem 0 DAI (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "0",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Amount to redeem needs to be > 0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Users 0 tries to redeem 1100 DAI from the 1000 DAI deposited (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1100",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "User cannot redeem more than the available balance"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Users 1 deposits 1 WETH, borrows 100 DAI, tries to redeem the 1 WETH deposited (revert expected)",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"user": "1",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "revert",
|
|
||||||
"revertMessage": "Transfer cannot be allowed."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,340 +0,0 @@
|
||||||
{
|
|
||||||
"title": "LendingPool: withdraw",
|
|
||||||
"description": "withdraw function.",
|
|
||||||
"stories": [
|
|
||||||
{
|
|
||||||
"description": "User 0 Deposits 1000 DAI in an empty reserve",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws half of the deposited DAI",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "500",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws remaining half of the deposited DAI",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 Deposits 1000 USDC in an empty reserve",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws half of the deposited USDC",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "500",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws remaining half of the deposited USDC",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 Deposits 1 WETH in an empty reserve",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws half of the deposited WETH",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.5",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "User 0 withdraws remaining half of the deposited ETH",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Users 0 and 1 Deposit 1000 DAI, both withdraw",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Users 0 deposits 1000 DAI, user 1 Deposit 1000 USDC and 1 WETH, borrows 100 DAI. User 1 tries to withdraw all the USDC",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "0"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "1000",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mint",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "approve",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deposit",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "borrow",
|
|
||||||
"args": {
|
|
||||||
"reserve": "DAI",
|
|
||||||
"amount": "100",
|
|
||||||
"user": "1",
|
|
||||||
"borrowRateMode": "stable"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "USDC",
|
|
||||||
"amount": "-1",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Users 1 tries to withdraw 0.05 WETH, which does not bring the HF below 1",
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"name": "withdraw",
|
|
||||||
"args": {
|
|
||||||
"reserve": "WETH",
|
|
||||||
"amount": "0.05",
|
|
||||||
"user": "1"
|
|
||||||
},
|
|
||||||
"expected": "success"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,128 +0,0 @@
|
||||||
import { LendingPool } from '../../../types/LendingPool';
|
|
||||||
import { ReserveData, UserReserveData } from './interfaces';
|
|
||||||
import {
|
|
||||||
getLendingRateOracle,
|
|
||||||
getIErc20Detailed,
|
|
||||||
getMintableERC20,
|
|
||||||
getAToken,
|
|
||||||
getStableDebtToken,
|
|
||||||
getVariableDebtToken,
|
|
||||||
} from '../../../helpers/contracts-getters';
|
|
||||||
import { tEthereumAddress } from '../../../helpers/types';
|
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
import { getDb, DRE } from '../../../helpers/misc-utils';
|
|
||||||
import { AaveProtocolDataProvider } from '../../../types/AaveProtocolDataProvider';
|
|
||||||
|
|
||||||
export const getReserveData = async (
|
|
||||||
helper: AaveProtocolDataProvider,
|
|
||||||
reserve: tEthereumAddress
|
|
||||||
): Promise<ReserveData> => {
|
|
||||||
const [reserveData, tokenAddresses, rateOracle, token] = await Promise.all([
|
|
||||||
helper.getReserveData(reserve),
|
|
||||||
helper.getReserveTokensAddresses(reserve),
|
|
||||||
getLendingRateOracle(),
|
|
||||||
getIErc20Detailed(reserve),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const stableDebtToken = await getStableDebtToken(tokenAddresses.stableDebtTokenAddress);
|
|
||||||
const variableDebtToken = await getVariableDebtToken(tokenAddresses.variableDebtTokenAddress);
|
|
||||||
|
|
||||||
const { 0: principalStableDebt } = await stableDebtToken.getSupplyData();
|
|
||||||
const totalStableDebtLastUpdated = await stableDebtToken.getTotalSupplyLastUpdated();
|
|
||||||
|
|
||||||
const scaledVariableDebt = await variableDebtToken.scaledTotalSupply();
|
|
||||||
|
|
||||||
const rate = (await rateOracle.getMarketBorrowRate(reserve)).toString();
|
|
||||||
const symbol = await token.symbol();
|
|
||||||
const decimals = new BigNumber(await token.decimals());
|
|
||||||
|
|
||||||
const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString())
|
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
|
||||||
|
|
||||||
const utilizationRate = new BigNumber(
|
|
||||||
totalLiquidity.eq(0)
|
|
||||||
? 0
|
|
||||||
: new BigNumber(reserveData.totalStableDebt.toString())
|
|
||||||
.plus(reserveData.totalVariableDebt.toString())
|
|
||||||
.rayDiv(totalLiquidity)
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalLiquidity,
|
|
||||||
utilizationRate,
|
|
||||||
availableLiquidity: new BigNumber(reserveData.availableLiquidity.toString()),
|
|
||||||
totalStableDebt: new BigNumber(reserveData.totalStableDebt.toString()),
|
|
||||||
totalVariableDebt: new BigNumber(reserveData.totalVariableDebt.toString()),
|
|
||||||
liquidityRate: new BigNumber(reserveData.liquidityRate.toString()),
|
|
||||||
variableBorrowRate: new BigNumber(reserveData.variableBorrowRate.toString()),
|
|
||||||
stableBorrowRate: new BigNumber(reserveData.stableBorrowRate.toString()),
|
|
||||||
averageStableBorrowRate: new BigNumber(reserveData.averageStableBorrowRate.toString()),
|
|
||||||
liquidityIndex: new BigNumber(reserveData.liquidityIndex.toString()),
|
|
||||||
variableBorrowIndex: new BigNumber(reserveData.variableBorrowIndex.toString()),
|
|
||||||
lastUpdateTimestamp: new BigNumber(reserveData.lastUpdateTimestamp),
|
|
||||||
totalStableDebtLastUpdated: new BigNumber(totalStableDebtLastUpdated),
|
|
||||||
principalStableDebt: new BigNumber(principalStableDebt.toString()),
|
|
||||||
scaledVariableDebt: new BigNumber(scaledVariableDebt.toString()),
|
|
||||||
address: reserve,
|
|
||||||
aTokenAddress: tokenAddresses.aTokenAddress,
|
|
||||||
symbol,
|
|
||||||
decimals,
|
|
||||||
marketStableRate: new BigNumber(rate),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getUserData = async (
|
|
||||||
pool: LendingPool,
|
|
||||||
helper: AaveProtocolDataProvider,
|
|
||||||
reserve: string,
|
|
||||||
user: tEthereumAddress,
|
|
||||||
sender?: tEthereumAddress
|
|
||||||
): Promise<UserReserveData> => {
|
|
||||||
const [userData, scaledATokenBalance] = await Promise.all([
|
|
||||||
helper.getUserReserveData(reserve, user),
|
|
||||||
getATokenUserData(reserve, user, helper),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const token = await getMintableERC20(reserve);
|
|
||||||
const walletBalance = new BigNumber((await token.balanceOf(sender || user)).toString());
|
|
||||||
|
|
||||||
return {
|
|
||||||
scaledATokenBalance: new BigNumber(scaledATokenBalance),
|
|
||||||
currentATokenBalance: new BigNumber(userData.currentATokenBalance.toString()),
|
|
||||||
currentStableDebt: new BigNumber(userData.currentStableDebt.toString()),
|
|
||||||
currentVariableDebt: new BigNumber(userData.currentVariableDebt.toString()),
|
|
||||||
principalStableDebt: new BigNumber(userData.principalStableDebt.toString()),
|
|
||||||
scaledVariableDebt: new BigNumber(userData.scaledVariableDebt.toString()),
|
|
||||||
stableBorrowRate: new BigNumber(userData.stableBorrowRate.toString()),
|
|
||||||
liquidityRate: new BigNumber(userData.liquidityRate.toString()),
|
|
||||||
usageAsCollateralEnabled: userData.usageAsCollateralEnabled,
|
|
||||||
stableRateLastUpdated: new BigNumber(userData.stableRateLastUpdated.toString()),
|
|
||||||
walletBalance,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getReserveAddressFromSymbol = async (symbol: string) => {
|
|
||||||
const token = await getMintableERC20(
|
|
||||||
(await getDb().get(`${symbol}.${DRE.network.name}`).value()).address
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
throw `Could not find instance for contract ${symbol}`;
|
|
||||||
}
|
|
||||||
return token.address;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getATokenUserData = async (
|
|
||||||
reserve: string,
|
|
||||||
user: string,
|
|
||||||
helpersContract: AaveProtocolDataProvider
|
|
||||||
) => {
|
|
||||||
const aTokenAddress: string = (await helpersContract.getReserveTokensAddresses(reserve))
|
|
||||||
.aTokenAddress;
|
|
||||||
|
|
||||||
const aToken = await getAToken(aTokenAddress);
|
|
||||||
|
|
||||||
const scaledBalance = await aToken.scaledBalanceOf(user);
|
|
||||||
return scaledBalance.toString();
|
|
||||||
};
|
|
|
@ -1,40 +0,0 @@
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
|
|
||||||
export interface UserReserveData {
|
|
||||||
scaledATokenBalance: BigNumber;
|
|
||||||
currentATokenBalance: BigNumber;
|
|
||||||
currentStableDebt: BigNumber;
|
|
||||||
currentVariableDebt: BigNumber;
|
|
||||||
principalStableDebt: BigNumber;
|
|
||||||
scaledVariableDebt: BigNumber;
|
|
||||||
liquidityRate: BigNumber;
|
|
||||||
stableBorrowRate: BigNumber;
|
|
||||||
stableRateLastUpdated: BigNumber;
|
|
||||||
usageAsCollateralEnabled: Boolean;
|
|
||||||
walletBalance: BigNumber;
|
|
||||||
[key: string]: BigNumber | string | Boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ReserveData {
|
|
||||||
address: string;
|
|
||||||
symbol: string;
|
|
||||||
decimals: BigNumber;
|
|
||||||
totalLiquidity: BigNumber;
|
|
||||||
availableLiquidity: BigNumber;
|
|
||||||
totalStableDebt: BigNumber;
|
|
||||||
totalVariableDebt: BigNumber;
|
|
||||||
principalStableDebt: BigNumber;
|
|
||||||
scaledVariableDebt: BigNumber;
|
|
||||||
averageStableBorrowRate: BigNumber;
|
|
||||||
variableBorrowRate: BigNumber;
|
|
||||||
stableBorrowRate: BigNumber;
|
|
||||||
utilizationRate: BigNumber;
|
|
||||||
liquidityIndex: BigNumber;
|
|
||||||
variableBorrowIndex: BigNumber;
|
|
||||||
aTokenAddress: string;
|
|
||||||
marketStableRate: BigNumber;
|
|
||||||
lastUpdateTimestamp: BigNumber;
|
|
||||||
totalStableDebtLastUpdated: BigNumber;
|
|
||||||
liquidityRate: BigNumber;
|
|
||||||
[key: string]: BigNumber | string;
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
import {
|
|
||||||
RAY,
|
|
||||||
WAD,
|
|
||||||
HALF_RAY,
|
|
||||||
HALF_WAD,
|
|
||||||
WAD_RAY_RATIO,
|
|
||||||
HALF_PERCENTAGE,
|
|
||||||
PERCENTAGE_FACTOR,
|
|
||||||
} from '../../../helpers/constants';
|
|
||||||
|
|
||||||
declare module 'bignumber.js' {
|
|
||||||
interface BigNumber {
|
|
||||||
ray: () => BigNumber;
|
|
||||||
wad: () => BigNumber;
|
|
||||||
halfRay: () => BigNumber;
|
|
||||||
halfWad: () => BigNumber;
|
|
||||||
halfPercentage: () => BigNumber;
|
|
||||||
wadMul: (a: BigNumber) => BigNumber;
|
|
||||||
wadDiv: (a: BigNumber) => BigNumber;
|
|
||||||
rayMul: (a: BigNumber) => BigNumber;
|
|
||||||
rayDiv: (a: BigNumber) => BigNumber;
|
|
||||||
percentMul: (a: BigNumber) => BigNumber;
|
|
||||||
percentDiv: (a: BigNumber) => BigNumber;
|
|
||||||
rayToWad: () => BigNumber;
|
|
||||||
wadToRay: () => BigNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BigNumber.prototype.ray = (): BigNumber => {
|
|
||||||
return new BigNumber(RAY).decimalPlaces(0);
|
|
||||||
};
|
|
||||||
BigNumber.prototype.wad = (): BigNumber => {
|
|
||||||
return new BigNumber(WAD).decimalPlaces(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.halfRay = (): BigNumber => {
|
|
||||||
return new BigNumber(HALF_RAY).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.halfWad = (): BigNumber => {
|
|
||||||
return new BigNumber(HALF_WAD).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.wadMul = function (b: BigNumber): BigNumber {
|
|
||||||
return this.halfWad().plus(this.multipliedBy(b)).div(WAD).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.wadDiv = function (a: BigNumber): BigNumber {
|
|
||||||
const halfA = a.div(2).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
return halfA.plus(this.multipliedBy(WAD)).div(a).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.rayMul = function (a: BigNumber): BigNumber {
|
|
||||||
return this.halfRay().plus(this.multipliedBy(a)).div(RAY).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.rayDiv = function (a: BigNumber): BigNumber {
|
|
||||||
const halfA = a.div(2).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
return halfA
|
|
||||||
.plus(this.multipliedBy(RAY))
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN)
|
|
||||||
.div(a)
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.rayToWad = function (): BigNumber {
|
|
||||||
const halfRatio = new BigNumber(WAD_RAY_RATIO).div(2);
|
|
||||||
|
|
||||||
return halfRatio.plus(this).div(WAD_RAY_RATIO).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.wadToRay = function (): BigNumber {
|
|
||||||
return this.multipliedBy(WAD_RAY_RATIO).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.halfPercentage = (): BigNumber => {
|
|
||||||
return new BigNumber(HALF_PERCENTAGE).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.percentMul = function (b: BigNumber): BigNumber {
|
|
||||||
return this.halfPercentage()
|
|
||||||
.plus(this.multipliedBy(b))
|
|
||||||
.div(PERCENTAGE_FACTOR)
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
||||||
|
|
||||||
BigNumber.prototype.percentDiv = function (a: BigNumber): BigNumber {
|
|
||||||
const halfA = a.div(2).decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
return halfA
|
|
||||||
.plus(this.multipliedBy(PERCENTAGE_FACTOR))
|
|
||||||
.div(a)
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
};
|
|
|
@ -1,101 +0,0 @@
|
||||||
import { expect } from 'chai';
|
|
||||||
import { createRandomAddress } from '../helpers/misc-utils';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { ProtocolErrors } from '../helpers/types';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import { ZERO_ADDRESS } from '../helpers/constants';
|
|
||||||
import { waitForTx } from '../helpers/misc-utils';
|
|
||||||
import { deployLendingPool } from '../helpers/contracts-deployments';
|
|
||||||
|
|
||||||
const { utils } = ethers;
|
|
||||||
|
|
||||||
makeSuite('LendingPoolAddressesProvider', (testEnv: TestEnv) => {
|
|
||||||
it('Test the accessibility of the LendingPoolAddressesProvider', async () => {
|
|
||||||
const { addressesProvider, users } = testEnv;
|
|
||||||
const mockAddress = createRandomAddress();
|
|
||||||
const { INVALID_OWNER_REVERT_MSG } = ProtocolErrors;
|
|
||||||
|
|
||||||
await addressesProvider.transferOwnership(users[1].address);
|
|
||||||
|
|
||||||
for (const contractFunction of [
|
|
||||||
addressesProvider.setMarketId,
|
|
||||||
addressesProvider.setLendingPoolImpl,
|
|
||||||
addressesProvider.setLendingPoolConfiguratorImpl,
|
|
||||||
addressesProvider.setLendingPoolCollateralManager,
|
|
||||||
addressesProvider.setPoolAdmin,
|
|
||||||
addressesProvider.setPriceOracle,
|
|
||||||
addressesProvider.setLendingRateOracle,
|
|
||||||
]) {
|
|
||||||
await expect(contractFunction(mockAddress)).to.be.revertedWith(INVALID_OWNER_REVERT_MSG);
|
|
||||||
}
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
addressesProvider.setAddress(utils.keccak256(utils.toUtf8Bytes('RANDOM_ID')), mockAddress)
|
|
||||||
).to.be.revertedWith(INVALID_OWNER_REVERT_MSG);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
addressesProvider.setAddressAsProxy(
|
|
||||||
utils.keccak256(utils.toUtf8Bytes('RANDOM_ID')),
|
|
||||||
mockAddress
|
|
||||||
)
|
|
||||||
).to.be.revertedWith(INVALID_OWNER_REVERT_MSG);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tests adding a proxied address with `setAddressAsProxy()`', async () => {
|
|
||||||
const { addressesProvider, users } = testEnv;
|
|
||||||
const { INVALID_OWNER_REVERT_MSG } = ProtocolErrors;
|
|
||||||
|
|
||||||
const currentAddressesProviderOwner = users[1];
|
|
||||||
|
|
||||||
const mockLendingPool = await deployLendingPool();
|
|
||||||
const proxiedAddressId = utils.keccak256(utils.toUtf8Bytes('RANDOM_PROXIED'));
|
|
||||||
|
|
||||||
const proxiedAddressSetReceipt = await waitForTx(
|
|
||||||
await addressesProvider
|
|
||||||
.connect(currentAddressesProviderOwner.signer)
|
|
||||||
.setAddressAsProxy(proxiedAddressId, mockLendingPool.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!proxiedAddressSetReceipt.events || proxiedAddressSetReceipt.events?.length < 1) {
|
|
||||||
throw new Error('INVALID_EVENT_EMMITED');
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(proxiedAddressSetReceipt.events[0].event).to.be.equal('ProxyCreated');
|
|
||||||
expect(proxiedAddressSetReceipt.events[1].event).to.be.equal('AddressSet');
|
|
||||||
expect(proxiedAddressSetReceipt.events[1].args?.id).to.be.equal(proxiedAddressId);
|
|
||||||
expect(proxiedAddressSetReceipt.events[1].args?.newAddress).to.be.equal(
|
|
||||||
mockLendingPool.address
|
|
||||||
);
|
|
||||||
expect(proxiedAddressSetReceipt.events[1].args?.hasProxy).to.be.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tests adding a non proxied address with `setAddress()`', async () => {
|
|
||||||
const { addressesProvider, users } = testEnv;
|
|
||||||
const { INVALID_OWNER_REVERT_MSG } = ProtocolErrors;
|
|
||||||
|
|
||||||
const currentAddressesProviderOwner = users[1];
|
|
||||||
const mockNonProxiedAddress = createRandomAddress();
|
|
||||||
const nonProxiedAddressId = utils.keccak256(utils.toUtf8Bytes('RANDOM_NON_PROXIED'));
|
|
||||||
|
|
||||||
const nonProxiedAddressSetReceipt = await waitForTx(
|
|
||||||
await addressesProvider
|
|
||||||
.connect(currentAddressesProviderOwner.signer)
|
|
||||||
.setAddress(nonProxiedAddressId, mockNonProxiedAddress)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockNonProxiedAddress.toLowerCase()).to.be.equal(
|
|
||||||
(await addressesProvider.getAddress(nonProxiedAddressId)).toLowerCase()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!nonProxiedAddressSetReceipt.events || nonProxiedAddressSetReceipt.events?.length < 1) {
|
|
||||||
throw new Error('INVALID_EVENT_EMMITED');
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(nonProxiedAddressSetReceipt.events[0].event).to.be.equal('AddressSet');
|
|
||||||
expect(nonProxiedAddressSetReceipt.events[0].args?.id).to.be.equal(nonProxiedAddressId);
|
|
||||||
expect(nonProxiedAddressSetReceipt.events[0].args?.newAddress).to.be.equal(
|
|
||||||
mockNonProxiedAddress
|
|
||||||
);
|
|
||||||
expect(nonProxiedAddressSetReceipt.events[0].args?.hasProxy).to.be.equal(false);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,379 +0,0 @@
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
|
|
||||||
import { DRE } from '../helpers/misc-utils';
|
|
||||||
import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../helpers/constants';
|
|
||||||
import { convertToCurrencyDecimals } from '../helpers/contracts-helpers';
|
|
||||||
import { makeSuite } from './helpers/make-suite';
|
|
||||||
import { ProtocolErrors, RateMode } from '../helpers/types';
|
|
||||||
import { calcExpectedVariableDebtTokenBalance } from './helpers/utils/calculations';
|
|
||||||
import { getUserData, getReserveData } from './helpers/utils/helpers';
|
|
||||||
|
|
||||||
const chai = require('chai');
|
|
||||||
const { expect } = chai;
|
|
||||||
|
|
||||||
makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => {
|
|
||||||
const {
|
|
||||||
LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD,
|
|
||||||
INVALID_HF,
|
|
||||||
LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER,
|
|
||||||
LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED,
|
|
||||||
LP_IS_PAUSED,
|
|
||||||
} = ProtocolErrors;
|
|
||||||
|
|
||||||
it('Deposits WETH, borrows DAI/Check liquidation fails because health factor is above 1', async () => {
|
|
||||||
const { dai, weth, users, pool, oracle } = testEnv;
|
|
||||||
const depositor = users[0];
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
//mints DAI to depositor
|
|
||||||
await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//user 1 deposits 1000 DAI
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
await pool
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.deposit(dai.address, amountDAItoDeposit, depositor.address, '0');
|
|
||||||
|
|
||||||
const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1');
|
|
||||||
|
|
||||||
//mints WETH to borrower
|
|
||||||
await weth.connect(borrower.signer).mint(amountETHtoDeposit);
|
|
||||||
|
|
||||||
//approve protocol to access borrower wallet
|
|
||||||
await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//user 2 deposits 1 WETH
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.deposit(weth.address, amountETHtoDeposit, borrower.address, '0');
|
|
||||||
|
|
||||||
//user 2 borrows
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
|
|
||||||
const amountDAIToBorrow = await convertToCurrencyDecimals(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
|
||||||
.div(daiPrice.toString())
|
|
||||||
.multipliedBy(0.95)
|
|
||||||
.toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0', borrower.address);
|
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.bignumber.equal(
|
|
||||||
'8250',
|
|
||||||
'Invalid liquidation threshold'
|
|
||||||
);
|
|
||||||
|
|
||||||
//someone tries to liquidate user 2
|
|
||||||
await expect(
|
|
||||||
pool.liquidationCall(weth.address, dai.address, borrower.address, 1, true)
|
|
||||||
).to.be.revertedWith(LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Drop the health factor below 1', async () => {
|
|
||||||
const { dai, users, pool, oracle } = testEnv;
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
|
|
||||||
await oracle.setAssetPrice(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(daiPrice.toString()).multipliedBy(1.15).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(
|
|
||||||
oneEther.toString(),
|
|
||||||
INVALID_HF
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to liquidate a different currency than the loan principal', async () => {
|
|
||||||
const { pool, users, weth } = testEnv;
|
|
||||||
const borrower = users[1];
|
|
||||||
//user 2 tries to borrow
|
|
||||||
await expect(
|
|
||||||
pool.liquidationCall(weth.address, weth.address, borrower.address, oneEther.toString(), true)
|
|
||||||
).revertedWith(LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to liquidate a different collateral than the borrower collateral', async () => {
|
|
||||||
const { pool, dai, users } = testEnv;
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool.liquidationCall(dai.address, dai.address, borrower.address, oneEther.toString(), true)
|
|
||||||
).revertedWith(LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Liquidates the borrow', async () => {
|
|
||||||
const { pool, dai, weth, aWETH, aDai, users, oracle, helpersContract, deployer } = testEnv;
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
//mints dai to the caller
|
|
||||||
|
|
||||||
await dai.mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const daiReserveDataBefore = await getReserveData(helpersContract, dai.address);
|
|
||||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentVariableDebt.toString())
|
|
||||||
.div(2)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
const tx = await pool.liquidationCall(
|
|
||||||
weth.address,
|
|
||||||
dai.address,
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const userReserveDataAfter = await helpersContract.getUserReserveData(
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
const daiReserveDataAfter = await helpersContract.getReserveData(dai.address);
|
|
||||||
const ethReserveDataAfter = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const collateralPrice = (await oracle.getAssetPrice(weth.address)).toString();
|
|
||||||
const principalPrice = (await oracle.getAssetPrice(dai.address)).toString();
|
|
||||||
|
|
||||||
const collateralDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(weth.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const principalDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(dai.address)
|
|
||||||
).decimals.toString();
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice)
|
|
||||||
.times(new BigNumber(amountToLiquidate).times(105))
|
|
||||||
.times(new BigNumber(10).pow(collateralDecimals))
|
|
||||||
.div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals)))
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
if (!tx.blockNumber) {
|
|
||||||
expect(false, 'Invalid block number');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const txTimestamp = new BigNumber(
|
|
||||||
(await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const variableDebtBeforeTx = calcExpectedVariableDebtTokenBalance(
|
|
||||||
daiReserveDataBefore,
|
|
||||||
userReserveDataBefore,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt(
|
|
||||||
oneEther.toFixed(0),
|
|
||||||
'Invalid health factor'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.currentVariableDebt.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(variableDebtBeforeTx).minus(amountToLiquidate).toFixed(0),
|
|
||||||
'Invalid user borrow balance after liquidation'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(daiReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.plus(amountToLiquidate)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid principal available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the liquidity index of the principal reserve needs to be bigger than the index before
|
|
||||||
expect(daiReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte(
|
|
||||||
daiReserveDataBefore.liquidityIndex.toString(),
|
|
||||||
'Invalid liquidity index'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the principal APY after a liquidation needs to be lower than the APY before
|
|
||||||
expect(daiReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt(
|
|
||||||
daiReserveDataBefore.liquidityRate.toString(),
|
|
||||||
'Invalid liquidity APY'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(ethReserveDataBefore.availableLiquidity.toString()).toFixed(0),
|
|
||||||
'Invalid collateral available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
(await helpersContract.getUserReserveData(weth.address, deployer.address))
|
|
||||||
.usageAsCollateralEnabled
|
|
||||||
).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User 3 deposits 1000 USDC, user 4 1 WETH, user 4 borrows - drops HF, liquidates the borrow', async () => {
|
|
||||||
const { users, pool, usdc, oracle, weth, helpersContract } = testEnv;
|
|
||||||
const depositor = users[3];
|
|
||||||
const borrower = users[4];
|
|
||||||
|
|
||||||
//mints USDC to depositor
|
|
||||||
await usdc
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await usdc.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//user 3 deposits 1000 USDC
|
|
||||||
const amountUSDCtoDeposit = await convertToCurrencyDecimals(usdc.address, '1000');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.deposit(usdc.address, amountUSDCtoDeposit, depositor.address, '0');
|
|
||||||
|
|
||||||
//user 4 deposits 1 ETH
|
|
||||||
const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1');
|
|
||||||
|
|
||||||
//mints WETH to borrower
|
|
||||||
await weth.connect(borrower.signer).mint(amountETHtoDeposit);
|
|
||||||
|
|
||||||
//approve protocol to access borrower wallet
|
|
||||||
await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.deposit(weth.address, amountETHtoDeposit, borrower.address, '0');
|
|
||||||
|
|
||||||
//user 4 borrows
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
|
||||||
|
|
||||||
const amountUSDCToBorrow = await convertToCurrencyDecimals(
|
|
||||||
usdc.address,
|
|
||||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
|
||||||
.div(usdcPrice.toString())
|
|
||||||
.multipliedBy(0.9502)
|
|
||||||
.toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
|
|
||||||
|
|
||||||
//drops HF below 1
|
|
||||||
|
|
||||||
await oracle.setAssetPrice(
|
|
||||||
usdc.address,
|
|
||||||
new BigNumber(usdcPrice.toString()).multipliedBy(1.12).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
//mints dai to the liquidator
|
|
||||||
|
|
||||||
await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const userReserveDataBefore = await helpersContract.getUserReserveData(
|
|
||||||
usdc.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const usdcReserveDataBefore = await helpersContract.getReserveData(usdc.address);
|
|
||||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
|
||||||
.multipliedBy(0.5)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
await pool.liquidationCall(
|
|
||||||
weth.address,
|
|
||||||
usdc.address,
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const userReserveDataAfter = await helpersContract.getUserReserveData(
|
|
||||||
usdc.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
const usdcReserveDataAfter = await helpersContract.getReserveData(usdc.address);
|
|
||||||
const ethReserveDataAfter = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const collateralPrice = (await oracle.getAssetPrice(weth.address)).toString();
|
|
||||||
const principalPrice = (await oracle.getAssetPrice(usdc.address)).toString();
|
|
||||||
|
|
||||||
const collateralDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(weth.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const principalDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
|
||||||
).decimals.toString();
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice)
|
|
||||||
.times(new BigNumber(amountToLiquidate).times(105))
|
|
||||||
.times(new BigNumber(10).pow(collateralDecimals))
|
|
||||||
.div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals)))
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt(
|
|
||||||
oneEther.toFixed(0),
|
|
||||||
'Invalid health factor'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
|
||||||
.minus(amountToLiquidate)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid user borrow balance after liquidation'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(usdcReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(usdcReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.plus(amountToLiquidate)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid principal available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the liquidity index of the principal reserve needs to be bigger than the index before
|
|
||||||
expect(usdcReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte(
|
|
||||||
usdcReserveDataBefore.liquidityIndex.toString(),
|
|
||||||
'Invalid liquidity index'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the principal APY after a liquidation needs to be lower than the APY before
|
|
||||||
expect(usdcReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt(
|
|
||||||
usdcReserveDataBefore.liquidityRate.toString(),
|
|
||||||
'Invalid liquidity APY'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(ethReserveDataBefore.availableLiquidity.toString()).toFixed(0),
|
|
||||||
'Invalid collateral available liquidity'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,492 +0,0 @@
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
|
|
||||||
import { DRE, increaseTime } from '../helpers/misc-utils';
|
|
||||||
import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../helpers/constants';
|
|
||||||
import { convertToCurrencyDecimals } from '../helpers/contracts-helpers';
|
|
||||||
import { makeSuite } from './helpers/make-suite';
|
|
||||||
import { ProtocolErrors, RateMode } from '../helpers/types';
|
|
||||||
import { calcExpectedStableDebtTokenBalance } from './helpers/utils/calculations';
|
|
||||||
import { getUserData } from './helpers/utils/helpers';
|
|
||||||
import { CommonsConfig } from '../markets/aave/commons';
|
|
||||||
|
|
||||||
import { parseEther } from 'ethers/lib/utils';
|
|
||||||
|
|
||||||
const chai = require('chai');
|
|
||||||
|
|
||||||
const { expect } = chai;
|
|
||||||
|
|
||||||
makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', (testEnv) => {
|
|
||||||
const { INVALID_HF } = ProtocolErrors;
|
|
||||||
|
|
||||||
before('Before LendingPool liquidation: set config', () => {
|
|
||||||
BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN });
|
|
||||||
});
|
|
||||||
|
|
||||||
after('After LendingPool liquidation: reset config', () => {
|
|
||||||
BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("It's not possible to liquidate on a non-active collateral or a non active principal", async () => {
|
|
||||||
const { configurator, weth, pool, users, dai } = testEnv;
|
|
||||||
const user = users[1];
|
|
||||||
await configurator.deactivateReserve(weth.address);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool.liquidationCall(weth.address, dai.address, user.address, parseEther('1000'), false)
|
|
||||||
).to.be.revertedWith('2');
|
|
||||||
|
|
||||||
await configurator.activateReserve(weth.address);
|
|
||||||
|
|
||||||
await configurator.deactivateReserve(dai.address);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool.liquidationCall(weth.address, dai.address, user.address, parseEther('1000'), false)
|
|
||||||
).to.be.revertedWith('2');
|
|
||||||
|
|
||||||
await configurator.activateReserve(dai.address);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deposits WETH, borrows DAI', async () => {
|
|
||||||
const { dai, weth, users, pool, oracle } = testEnv;
|
|
||||||
const depositor = users[0];
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
//mints DAI to depositor
|
|
||||||
await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//user 1 deposits 1000 DAI
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.deposit(dai.address, amountDAItoDeposit, depositor.address, '0');
|
|
||||||
//user 2 deposits 1 ETH
|
|
||||||
const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1');
|
|
||||||
|
|
||||||
//mints WETH to borrower
|
|
||||||
await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access the borrower wallet
|
|
||||||
await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.deposit(weth.address, amountETHtoDeposit, borrower.address, '0');
|
|
||||||
|
|
||||||
//user 2 borrows
|
|
||||||
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
|
|
||||||
const amountDAIToBorrow = await convertToCurrencyDecimals(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
|
||||||
.div(daiPrice.toString())
|
|
||||||
.multipliedBy(0.95)
|
|
||||||
.toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address);
|
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.bignumber.equal(
|
|
||||||
'8250',
|
|
||||||
INVALID_HF
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Drop the health factor below 1', async () => {
|
|
||||||
const { dai, weth, users, pool, oracle } = testEnv;
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
|
|
||||||
await oracle.setAssetPrice(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(daiPrice.toString()).multipliedBy(1.18).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(
|
|
||||||
oneEther.toFixed(0),
|
|
||||||
INVALID_HF
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Liquidates the borrow', async () => {
|
|
||||||
const { dai, weth, users, pool, oracle, helpersContract } = testEnv;
|
|
||||||
const liquidator = users[3];
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
//mints dai to the liquidator
|
|
||||||
await dai.connect(liquidator.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access the liquidator wallet
|
|
||||||
await dai.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const daiReserveDataBefore = await helpersContract.getReserveData(dai.address);
|
|
||||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0);
|
|
||||||
|
|
||||||
await increaseTime(100);
|
|
||||||
|
|
||||||
const tx = await pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.liquidationCall(weth.address, dai.address, borrower.address, amountToLiquidate, false);
|
|
||||||
|
|
||||||
const userReserveDataAfter = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const daiReserveDataAfter = await helpersContract.getReserveData(dai.address);
|
|
||||||
const ethReserveDataAfter = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
|
||||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
|
|
||||||
const collateralDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(weth.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const principalDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(dai.address)
|
|
||||||
).decimals.toString();
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
|
||||||
.times(new BigNumber(amountToLiquidate).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);
|
|
||||||
|
|
||||||
if (!tx.blockNumber) {
|
|
||||||
expect(false, 'Invalid block number');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const txTimestamp = new BigNumber(
|
|
||||||
(await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const stableDebtBeforeTx = calcExpectedStableDebtTokenBalance(
|
|
||||||
userReserveDataBefore.principalStableDebt,
|
|
||||||
userReserveDataBefore.stableBorrowRate,
|
|
||||||
userReserveDataBefore.stableRateLastUpdated,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
|
||||||
stableDebtBeforeTx.minus(amountToLiquidate).toFixed(0),
|
|
||||||
'Invalid user debt after liquidation'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the liquidity index of the principal reserve needs to be bigger than the index before
|
|
||||||
expect(daiReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte(
|
|
||||||
daiReserveDataBefore.liquidityIndex.toString(),
|
|
||||||
'Invalid liquidity index'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the principal APY after a liquidation needs to be lower than the APY before
|
|
||||||
expect(daiReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt(
|
|
||||||
daiReserveDataBefore.liquidityRate.toString(),
|
|
||||||
'Invalid liquidity APY'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(daiReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.plus(amountToLiquidate)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid principal available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(ethReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.minus(expectedCollateralLiquidated)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid collateral available liquidity'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User 3 deposits 1000 USDC, user 4 1 WETH, user 4 borrows - drops HF, liquidates the borrow', async () => {
|
|
||||||
const { usdc, users, pool, oracle, weth, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const depositor = users[3];
|
|
||||||
const borrower = users[4];
|
|
||||||
const liquidator = users[5];
|
|
||||||
|
|
||||||
//mints USDC to depositor
|
|
||||||
await usdc
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await usdc.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//depositor deposits 1000 USDC
|
|
||||||
const amountUSDCtoDeposit = await convertToCurrencyDecimals(usdc.address, '1000');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.deposit(usdc.address, amountUSDCtoDeposit, depositor.address, '0');
|
|
||||||
|
|
||||||
//borrower deposits 1 ETH
|
|
||||||
const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1');
|
|
||||||
|
|
||||||
//mints WETH to borrower
|
|
||||||
await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access the borrower wallet
|
|
||||||
await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.deposit(weth.address, amountETHtoDeposit, borrower.address, '0');
|
|
||||||
|
|
||||||
//borrower borrows
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
|
||||||
|
|
||||||
const amountUSDCToBorrow = await convertToCurrencyDecimals(
|
|
||||||
usdc.address,
|
|
||||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
|
||||||
.div(usdcPrice.toString())
|
|
||||||
.multipliedBy(0.9502)
|
|
||||||
.toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
|
|
||||||
|
|
||||||
//drops HF below 1
|
|
||||||
await oracle.setAssetPrice(
|
|
||||||
usdc.address,
|
|
||||||
new BigNumber(usdcPrice.toString()).multipliedBy(1.12).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
//mints dai to the liquidator
|
|
||||||
|
|
||||||
await usdc
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await usdc.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const userReserveDataBefore = await helpersContract.getUserReserveData(
|
|
||||||
usdc.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const usdcReserveDataBefore = await helpersContract.getReserveData(usdc.address);
|
|
||||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const amountToLiquidate = DRE.ethers.BigNumber.from(
|
|
||||||
userReserveDataBefore.currentStableDebt.toString()
|
|
||||||
)
|
|
||||||
.div(2)
|
|
||||||
.toString();
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.liquidationCall(weth.address, usdc.address, borrower.address, amountToLiquidate, false);
|
|
||||||
|
|
||||||
const userReserveDataAfter = await helpersContract.getUserReserveData(
|
|
||||||
usdc.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
const usdcReserveDataAfter = await helpersContract.getReserveData(usdc.address);
|
|
||||||
const ethReserveDataAfter = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
|
||||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
|
||||||
|
|
||||||
const collateralDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(weth.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const principalDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
|
||||||
).decimals.toString();
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
|
||||||
.times(new BigNumber(amountToLiquidate).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);
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt(
|
|
||||||
oneEther.toFixed(0),
|
|
||||||
'Invalid health factor'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
|
||||||
.minus(amountToLiquidate)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid user borrow balance after liquidation'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the liquidity index of the principal reserve needs to be bigger than the index before
|
|
||||||
expect(usdcReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte(
|
|
||||||
usdcReserveDataBefore.liquidityIndex.toString(),
|
|
||||||
'Invalid liquidity index'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the principal APY after a liquidation needs to be lower than the APY before
|
|
||||||
expect(usdcReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt(
|
|
||||||
usdcReserveDataBefore.liquidityRate.toString(),
|
|
||||||
'Invalid liquidity APY'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(usdcReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(usdcReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.plus(amountToLiquidate)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid principal available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(ethReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.minus(expectedCollateralLiquidated)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid collateral available liquidity'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User 4 deposits 10 AAVE - drops HF, liquidates the AAVE, which results on a lower amount being liquidated', async () => {
|
|
||||||
const { aave, usdc, users, pool, oracle, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const depositor = users[3];
|
|
||||||
const borrower = users[4];
|
|
||||||
const liquidator = users[5];
|
|
||||||
|
|
||||||
//mints AAVE to borrower
|
|
||||||
await aave.connect(borrower.signer).mint(await convertToCurrencyDecimals(aave.address, '10'));
|
|
||||||
|
|
||||||
//approve protocol to access the borrower wallet
|
|
||||||
await aave.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//borrower deposits 10 AAVE
|
|
||||||
const amountToDeposit = await convertToCurrencyDecimals(aave.address, '10');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.deposit(aave.address, amountToDeposit, borrower.address, '0');
|
|
||||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
|
||||||
|
|
||||||
//drops HF below 1
|
|
||||||
await oracle.setAssetPrice(
|
|
||||||
usdc.address,
|
|
||||||
new BigNumber(usdcPrice.toString()).multipliedBy(1.14).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
//mints usdc to the liquidator
|
|
||||||
await usdc
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await usdc.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const userReserveDataBefore = await helpersContract.getUserReserveData(
|
|
||||||
usdc.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const usdcReserveDataBefore = await helpersContract.getReserveData(usdc.address);
|
|
||||||
const aaveReserveDataBefore = await helpersContract.getReserveData(aave.address);
|
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
|
||||||
.div(2)
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
const collateralPrice = await oracle.getAssetPrice(aave.address);
|
|
||||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.liquidationCall(aave.address, usdc.address, borrower.address, amountToLiquidate, false);
|
|
||||||
|
|
||||||
const userReserveDataAfter = await helpersContract.getUserReserveData(
|
|
||||||
usdc.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
const usdcReserveDataAfter = await helpersContract.getReserveData(usdc.address);
|
|
||||||
const aaveReserveDataAfter = await helpersContract.getReserveData(aave.address);
|
|
||||||
|
|
||||||
const aaveConfiguration = await helpersContract.getReserveConfigurationData(aave.address);
|
|
||||||
const collateralDecimals = aaveConfiguration.decimals.toString();
|
|
||||||
const liquidationBonus = aaveConfiguration.liquidationBonus.toString();
|
|
||||||
|
|
||||||
const principalDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
|
||||||
).decimals.toString();
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = oneEther.multipliedBy('10');
|
|
||||||
|
|
||||||
const expectedPrincipal = new BigNumber(collateralPrice.toString())
|
|
||||||
.times(expectedCollateralLiquidated)
|
|
||||||
.times(new BigNumber(10).pow(principalDecimals))
|
|
||||||
.div(
|
|
||||||
new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
|
||||||
)
|
|
||||||
.times(10000)
|
|
||||||
.div(liquidationBonus.toString())
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt(
|
|
||||||
oneEther.toFixed(0),
|
|
||||||
'Invalid health factor'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
|
||||||
.minus(expectedPrincipal)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid user borrow balance after liquidation'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(usdcReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(usdcReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.plus(expectedPrincipal)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid principal available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(aaveReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(aaveReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.minus(expectedCollateralLiquidated)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid collateral available liquidity'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,366 +0,0 @@
|
||||||
import { MAX_UINT_AMOUNT } from '../../helpers/constants';
|
|
||||||
import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers';
|
|
||||||
import { makeSuite, TestEnv } from '../helpers/make-suite';
|
|
||||||
import { parseEther } from 'ethers/lib/utils';
|
|
||||||
import { DRE, waitForTx } from '../../helpers/misc-utils';
|
|
||||||
import { BigNumber } from 'ethers';
|
|
||||||
import { getStableDebtToken, getVariableDebtToken } from '../../helpers/contracts-getters';
|
|
||||||
import { deploySelfdestructTransferMock } from '../../helpers/contracts-deployments';
|
|
||||||
import { IUniswapV2Router02Factory } from '../../types/IUniswapV2Router02Factory';
|
|
||||||
|
|
||||||
const { expect } = require('chai');
|
|
||||||
|
|
||||||
const UNISWAP_ROUTER = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D';
|
|
||||||
|
|
||||||
makeSuite('Mainnet Check list', (testEnv: TestEnv) => {
|
|
||||||
const zero = BigNumber.from('0');
|
|
||||||
const depositSize = parseEther('5');
|
|
||||||
const daiSize = parseEther('10000');
|
|
||||||
it('Deposit WETH', async () => {
|
|
||||||
const { users, wethGateway, aWETH, pool } = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
|
|
||||||
// Deposit with native ETH
|
|
||||||
await wethGateway.connect(user.signer).depositETH(user.address, '0', { value: depositSize });
|
|
||||||
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero);
|
|
||||||
expect(aTokensBalance).to.be.gte(depositSize);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Withdraw WETH - Partial', async () => {
|
|
||||||
const { users, wethGateway, aWETH, pool } = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
const priorEthersBalance = await user.signer.getBalance();
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero, 'User should have aTokens.');
|
|
||||||
|
|
||||||
// Partially withdraw native ETH
|
|
||||||
const partialWithdraw = await convertToCurrencyDecimals(aWETH.address, '2');
|
|
||||||
|
|
||||||
// Approve the aTokens to Gateway so Gateway can withdraw and convert to Ether
|
|
||||||
const approveTx = await aWETH
|
|
||||||
.connect(user.signer)
|
|
||||||
.approve(wethGateway.address, MAX_UINT_AMOUNT);
|
|
||||||
const { gasUsed: approveGas } = await waitForTx(approveTx);
|
|
||||||
|
|
||||||
// Partial Withdraw and send native Ether to user
|
|
||||||
const { gasUsed: withdrawGas } = await waitForTx(
|
|
||||||
await wethGateway.connect(user.signer).withdrawETH(partialWithdraw, user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const afterPartialEtherBalance = await user.signer.getBalance();
|
|
||||||
const afterPartialATokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
const gasCosts = approveGas.add(withdrawGas).mul(approveTx.gasPrice);
|
|
||||||
|
|
||||||
expect(afterPartialEtherBalance).to.be.equal(
|
|
||||||
priorEthersBalance.add(partialWithdraw).sub(gasCosts),
|
|
||||||
'User ETHER balance should contain the partial withdraw'
|
|
||||||
);
|
|
||||||
expect(afterPartialATokensBalance).to.be.equal(
|
|
||||||
aTokensBalance.sub(partialWithdraw),
|
|
||||||
'User aWETH balance should be substracted'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Withdraw WETH - Full', async () => {
|
|
||||||
const { users, aWETH, wethGateway, pool } = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
const priorEthersBalance = await user.signer.getBalance();
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero, 'User should have aTokens.');
|
|
||||||
|
|
||||||
// Approve the aTokens to Gateway so Gateway can withdraw and convert to Ether
|
|
||||||
const approveTx = await aWETH
|
|
||||||
.connect(user.signer)
|
|
||||||
.approve(wethGateway.address, MAX_UINT_AMOUNT);
|
|
||||||
const { gasUsed: approveGas } = await waitForTx(approveTx);
|
|
||||||
|
|
||||||
// Full withdraw
|
|
||||||
const { gasUsed: withdrawGas } = await waitForTx(
|
|
||||||
await wethGateway.connect(user.signer).withdrawETH(MAX_UINT_AMOUNT, user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const afterFullEtherBalance = await user.signer.getBalance();
|
|
||||||
const afterFullATokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
const gasCosts = approveGas.add(withdrawGas).mul(approveTx.gasPrice);
|
|
||||||
|
|
||||||
expect(afterFullEtherBalance).to.be.eq(
|
|
||||||
priorEthersBalance.add(aTokensBalance).sub(gasCosts),
|
|
||||||
'User ETHER balance should contain the full withdraw'
|
|
||||||
);
|
|
||||||
expect(afterFullATokensBalance).to.be.eq(0, 'User aWETH balance should be zero');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Borrow stable WETH and Full Repay with ETH', async () => {
|
|
||||||
const { users, wethGateway, aWETH, dai, aDai, weth, pool, helpersContract } = testEnv;
|
|
||||||
const borrowSize = parseEther('1');
|
|
||||||
const repaySize = borrowSize.add(borrowSize.mul(5).div(100));
|
|
||||||
const user = users[1];
|
|
||||||
|
|
||||||
const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const stableDebtToken = await getStableDebtToken(stableDebtTokenAddress);
|
|
||||||
|
|
||||||
// Deposit 10000 DAI
|
|
||||||
await dai.connect(user.signer).mint(daiSize);
|
|
||||||
await dai.connect(user.signer).approve(pool.address, daiSize);
|
|
||||||
await pool.connect(user.signer).deposit(dai.address, daiSize, user.address, '0');
|
|
||||||
|
|
||||||
const aTokensBalance = await aDai.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero);
|
|
||||||
expect(aTokensBalance).to.be.gte(daiSize);
|
|
||||||
|
|
||||||
// Borrow WETH with WETH as collateral
|
|
||||||
await waitForTx(
|
|
||||||
await pool.connect(user.signer).borrow(weth.address, borrowSize, '1', '0', user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtBalance = await stableDebtToken.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(debtBalance).to.be.gt(zero);
|
|
||||||
|
|
||||||
// Full Repay WETH with native ETH
|
|
||||||
await waitForTx(
|
|
||||||
await wethGateway
|
|
||||||
.connect(user.signer)
|
|
||||||
.repayETH(MAX_UINT_AMOUNT, '1', user.address, { value: repaySize })
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtBalanceAfterRepay = await stableDebtToken.balanceOf(user.address);
|
|
||||||
expect(debtBalanceAfterRepay).to.be.eq(zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Borrow variable WETH and Full Repay with ETH', async () => {
|
|
||||||
const { users, wethGateway, aWETH, weth, pool, helpersContract } = testEnv;
|
|
||||||
const borrowSize = parseEther('1');
|
|
||||||
const repaySize = borrowSize.add(borrowSize.mul(5).div(100));
|
|
||||||
const user = users[1];
|
|
||||||
|
|
||||||
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const varDebtToken = await getVariableDebtToken(variableDebtTokenAddress);
|
|
||||||
|
|
||||||
// Deposit with native ETH
|
|
||||||
await wethGateway.connect(user.signer).depositETH(user.address, '0', { value: depositSize });
|
|
||||||
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero);
|
|
||||||
expect(aTokensBalance).to.be.gte(depositSize);
|
|
||||||
|
|
||||||
// Borrow WETH with WETH as collateral
|
|
||||||
await waitForTx(
|
|
||||||
await pool.connect(user.signer).borrow(weth.address, borrowSize, '2', '0', user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtBalance = await varDebtToken.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(debtBalance).to.be.gt(zero);
|
|
||||||
|
|
||||||
// Partial Repay WETH loan with native ETH
|
|
||||||
const partialPayment = repaySize.div(2);
|
|
||||||
await waitForTx(
|
|
||||||
await wethGateway
|
|
||||||
.connect(user.signer)
|
|
||||||
.repayETH(partialPayment, '2', user.address, { value: partialPayment })
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtBalanceAfterPartialRepay = await varDebtToken.balanceOf(user.address);
|
|
||||||
expect(debtBalanceAfterPartialRepay).to.be.lt(debtBalance);
|
|
||||||
|
|
||||||
// Full Repay WETH loan with native ETH
|
|
||||||
await waitForTx(
|
|
||||||
await wethGateway
|
|
||||||
.connect(user.signer)
|
|
||||||
.repayETH(MAX_UINT_AMOUNT, '2', user.address, { value: repaySize })
|
|
||||||
);
|
|
||||||
const debtBalanceAfterFullRepay = await varDebtToken.balanceOf(user.address);
|
|
||||||
expect(debtBalanceAfterFullRepay).to.be.eq(zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Borrow ETH via delegateApprove ETH and repays back', async () => {
|
|
||||||
const { users, wethGateway, aWETH, weth, helpersContract } = testEnv;
|
|
||||||
const borrowSize = parseEther('1');
|
|
||||||
const user = users[2];
|
|
||||||
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
const varDebtToken = await getVariableDebtToken(variableDebtTokenAddress);
|
|
||||||
|
|
||||||
const priorDebtBalance = await varDebtToken.balanceOf(user.address);
|
|
||||||
expect(priorDebtBalance).to.be.eq(zero);
|
|
||||||
|
|
||||||
// Deposit WETH with native ETH
|
|
||||||
await wethGateway.connect(user.signer).depositETH(user.address, '0', { value: depositSize });
|
|
||||||
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero);
|
|
||||||
expect(aTokensBalance).to.be.gte(depositSize);
|
|
||||||
|
|
||||||
// Delegates borrowing power of WETH to WETHGateway
|
|
||||||
await waitForTx(
|
|
||||||
await varDebtToken.connect(user.signer).approveDelegation(wethGateway.address, borrowSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Borrows ETH with WETH as collateral
|
|
||||||
await waitForTx(await wethGateway.connect(user.signer).borrowETH(borrowSize, '2', '0'));
|
|
||||||
|
|
||||||
const debtBalance = await varDebtToken.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(debtBalance).to.be.gt(zero);
|
|
||||||
|
|
||||||
// Full Repay WETH loan with native ETH
|
|
||||||
await waitForTx(
|
|
||||||
await wethGateway
|
|
||||||
.connect(user.signer)
|
|
||||||
.repayETH(MAX_UINT_AMOUNT, '2', user.address, { value: borrowSize.mul(2) })
|
|
||||||
);
|
|
||||||
const debtBalanceAfterFullRepay = await varDebtToken.balanceOf(user.address);
|
|
||||||
expect(debtBalanceAfterFullRepay).to.be.eq(zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should revert if receiver function receives Ether if not WETH', async () => {
|
|
||||||
const { users, wethGateway } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
const amount = parseEther('1');
|
|
||||||
|
|
||||||
// Call receiver function (empty data + value)
|
|
||||||
await expect(
|
|
||||||
user.signer.sendTransaction({
|
|
||||||
to: wethGateway.address,
|
|
||||||
value: amount,
|
|
||||||
gasLimit: DRE.network.config.gas,
|
|
||||||
})
|
|
||||||
).to.be.revertedWith('Receive not allowed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should revert if fallback functions is called with Ether', async () => {
|
|
||||||
const { users, wethGateway } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
const amount = parseEther('1');
|
|
||||||
const fakeABI = ['function wantToCallFallback()'];
|
|
||||||
const abiCoder = new DRE.ethers.utils.Interface(fakeABI);
|
|
||||||
const fakeMethodEncoded = abiCoder.encodeFunctionData('wantToCallFallback', []);
|
|
||||||
|
|
||||||
// Call fallback function with value
|
|
||||||
await expect(
|
|
||||||
user.signer.sendTransaction({
|
|
||||||
to: wethGateway.address,
|
|
||||||
data: fakeMethodEncoded,
|
|
||||||
value: amount,
|
|
||||||
gasLimit: DRE.network.config.gas,
|
|
||||||
})
|
|
||||||
).to.be.revertedWith('Fallback not allowed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should revert if fallback functions is called', async () => {
|
|
||||||
const { users, wethGateway } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
|
|
||||||
const fakeABI = ['function wantToCallFallback()'];
|
|
||||||
const abiCoder = new DRE.ethers.utils.Interface(fakeABI);
|
|
||||||
const fakeMethodEncoded = abiCoder.encodeFunctionData('wantToCallFallback', []);
|
|
||||||
|
|
||||||
// Call fallback function without value
|
|
||||||
await expect(
|
|
||||||
user.signer.sendTransaction({
|
|
||||||
to: wethGateway.address,
|
|
||||||
data: fakeMethodEncoded,
|
|
||||||
gasLimit: DRE.network.config.gas,
|
|
||||||
})
|
|
||||||
).to.be.revertedWith('Fallback not allowed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Getters should retrieve correct state', async () => {
|
|
||||||
const { aWETH, weth, pool, wethGateway } = testEnv;
|
|
||||||
|
|
||||||
const WETHAddress = await wethGateway.getWETHAddress();
|
|
||||||
const aWETHAddress = await wethGateway.getAWETHAddress();
|
|
||||||
const poolAddress = await wethGateway.getLendingPoolAddress();
|
|
||||||
|
|
||||||
expect(WETHAddress).to.be.equal(weth.address);
|
|
||||||
expect(aWETHAddress).to.be.equal(aWETH.address);
|
|
||||||
expect(poolAddress).to.be.equal(pool.address);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Owner can do emergency token recovery', async () => {
|
|
||||||
const { users, weth, dai, wethGateway, deployer } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
const amount = parseEther('1');
|
|
||||||
|
|
||||||
const uniswapRouter = IUniswapV2Router02Factory.connect(UNISWAP_ROUTER, user.signer);
|
|
||||||
await uniswapRouter.swapETHForExactTokens(
|
|
||||||
amount, // 1 DAI
|
|
||||||
[weth.address, dai.address], // Uniswap paths WETH - DAI
|
|
||||||
user.address,
|
|
||||||
(await DRE.ethers.provider.getBlock('latest')).timestamp + 300,
|
|
||||||
{
|
|
||||||
value: amount, // 1 Ether, we get refund of the unneeded Ether to buy 1 DAI
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const daiBalanceAfterMint = await dai.balanceOf(user.address);
|
|
||||||
|
|
||||||
await dai.connect(user.signer).transfer(wethGateway.address, amount);
|
|
||||||
const daiBalanceAfterBadTransfer = await dai.balanceOf(user.address);
|
|
||||||
expect(daiBalanceAfterBadTransfer).to.be.eq(
|
|
||||||
daiBalanceAfterMint.sub(amount),
|
|
||||||
'User should have lost the funds here.'
|
|
||||||
);
|
|
||||||
|
|
||||||
await wethGateway
|
|
||||||
.connect(deployer.signer)
|
|
||||||
.emergencyTokenTransfer(dai.address, user.address, amount);
|
|
||||||
const daiBalanceAfterRecovery = await dai.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(daiBalanceAfterRecovery).to.be.eq(
|
|
||||||
daiBalanceAfterMint,
|
|
||||||
'User should recover the funds due emergency token transfer'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Owner can do emergency native ETH recovery', async () => {
|
|
||||||
const { users, wethGateway, deployer } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
const amount = parseEther('1');
|
|
||||||
const userBalancePriorCall = await user.signer.getBalance();
|
|
||||||
|
|
||||||
// Deploy contract with payable selfdestruct contract
|
|
||||||
const selfdestructContract = await deploySelfdestructTransferMock();
|
|
||||||
|
|
||||||
// Selfdestruct the mock, pointing to WETHGateway address
|
|
||||||
const callTx = await selfdestructContract
|
|
||||||
.connect(user.signer)
|
|
||||||
.destroyAndTransfer(wethGateway.address, { value: amount });
|
|
||||||
const { gasUsed } = await waitForTx(callTx);
|
|
||||||
const gasFees = gasUsed.mul(callTx.gasPrice);
|
|
||||||
const userBalanceAfterCall = await user.signer.getBalance();
|
|
||||||
|
|
||||||
expect(userBalanceAfterCall).to.be.eq(userBalancePriorCall.sub(amount).sub(gasFees), '');
|
|
||||||
('User should have lost the funds');
|
|
||||||
|
|
||||||
// Recover the funds from the contract and sends back to the user
|
|
||||||
await wethGateway.connect(deployer.signer).emergencyEtherTransfer(user.address, amount);
|
|
||||||
|
|
||||||
const userBalanceAfterRecovery = await user.signer.getBalance();
|
|
||||||
const wethGatewayAfterRecovery = await DRE.ethers.provider.getBalance(wethGateway.address);
|
|
||||||
|
|
||||||
expect(userBalanceAfterRecovery).to.be.eq(
|
|
||||||
userBalancePriorCall.sub(gasFees),
|
|
||||||
'User should recover the funds due emergency eth transfer.'
|
|
||||||
);
|
|
||||||
expect(wethGatewayAfterRecovery).to.be.eq('0', 'WETHGateway ether balance should be zero.');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,330 +0,0 @@
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { ProtocolErrors, RateMode } from '../helpers/types';
|
|
||||||
import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../helpers/constants';
|
|
||||||
import { convertToCurrencyDecimals } from '../helpers/contracts-helpers';
|
|
||||||
import { parseEther, parseUnits } from 'ethers/lib/utils';
|
|
||||||
import { BigNumber } from 'bignumber.js';
|
|
||||||
import { MockFlashLoanReceiver } from '../types/MockFlashLoanReceiver';
|
|
||||||
import { getMockFlashLoanReceiver } from '../helpers/contracts-getters';
|
|
||||||
|
|
||||||
const { expect } = require('chai');
|
|
||||||
|
|
||||||
makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|
||||||
let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;
|
|
||||||
|
|
||||||
const {
|
|
||||||
LP_IS_PAUSED,
|
|
||||||
INVALID_FROM_BALANCE_AFTER_TRANSFER,
|
|
||||||
INVALID_TO_BALANCE_AFTER_TRANSFER,
|
|
||||||
} = ProtocolErrors;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
_mockFlashLoanReceiver = await getMockFlashLoanReceiver();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succees', async () => {
|
|
||||||
const { users, pool, dai, aDai, configurator } = testEnv;
|
|
||||||
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await dai.connect(users[0].signer).mint(amountDAItoDeposit);
|
|
||||||
|
|
||||||
// user 0 deposits 1000 DAI
|
|
||||||
await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
await pool
|
|
||||||
.connect(users[0].signer)
|
|
||||||
.deposit(dai.address, amountDAItoDeposit, users[0].address, '0');
|
|
||||||
|
|
||||||
const user0Balance = await aDai.balanceOf(users[0].address);
|
|
||||||
const user1Balance = await aDai.balanceOf(users[1].address);
|
|
||||||
|
|
||||||
// Configurator pauses the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
// User 0 tries the transfer to User 1
|
|
||||||
await expect(
|
|
||||||
aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit)
|
|
||||||
).to.revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
const pausedFromBalance = await aDai.balanceOf(users[0].address);
|
|
||||||
const pausedToBalance = await aDai.balanceOf(users[1].address);
|
|
||||||
|
|
||||||
expect(pausedFromBalance).to.be.equal(
|
|
||||||
user0Balance.toString(),
|
|
||||||
INVALID_TO_BALANCE_AFTER_TRANSFER
|
|
||||||
);
|
|
||||||
expect(pausedToBalance.toString()).to.be.equal(
|
|
||||||
user1Balance.toString(),
|
|
||||||
INVALID_FROM_BALANCE_AFTER_TRANSFER
|
|
||||||
);
|
|
||||||
|
|
||||||
// Configurator unpauses the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
|
|
||||||
// User 0 succeeds transfer to User 1
|
|
||||||
await aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit);
|
|
||||||
|
|
||||||
const fromBalance = await aDai.balanceOf(users[0].address);
|
|
||||||
const toBalance = await aDai.balanceOf(users[1].address);
|
|
||||||
|
|
||||||
expect(fromBalance.toString()).to.be.equal(
|
|
||||||
user0Balance.sub(amountDAItoDeposit),
|
|
||||||
INVALID_FROM_BALANCE_AFTER_TRANSFER
|
|
||||||
);
|
|
||||||
expect(toBalance.toString()).to.be.equal(
|
|
||||||
user1Balance.add(amountDAItoDeposit),
|
|
||||||
INVALID_TO_BALANCE_AFTER_TRANSFER
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deposit', async () => {
|
|
||||||
const { users, pool, dai, aDai, configurator } = testEnv;
|
|
||||||
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await dai.connect(users[0].signer).mint(amountDAItoDeposit);
|
|
||||||
|
|
||||||
// user 0 deposits 1000 DAI
|
|
||||||
await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
// Configurator pauses the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
await expect(
|
|
||||||
pool.connect(users[0].signer).deposit(dai.address, amountDAItoDeposit, users[0].address, '0')
|
|
||||||
).to.revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Configurator unpauses the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Withdraw', async () => {
|
|
||||||
const { users, pool, dai, aDai, configurator } = testEnv;
|
|
||||||
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await dai.connect(users[0].signer).mint(amountDAItoDeposit);
|
|
||||||
|
|
||||||
// user 0 deposits 1000 DAI
|
|
||||||
await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
await pool
|
|
||||||
.connect(users[0].signer)
|
|
||||||
.deposit(dai.address, amountDAItoDeposit, users[0].address, '0');
|
|
||||||
|
|
||||||
// Configurator pauses the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
// user tries to burn
|
|
||||||
await expect(
|
|
||||||
pool.connect(users[0].signer).withdraw(dai.address, amountDAItoDeposit, users[0].address)
|
|
||||||
).to.revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Configurator unpauses the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Borrow', async () => {
|
|
||||||
const { pool, dai, users, configurator } = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
// Pause the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
// Try to execute liquidation
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).borrow(dai.address, '1', '1', '0', user.address)
|
|
||||||
).revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Unpause the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Repay', async () => {
|
|
||||||
const { pool, dai, users, configurator } = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
// Pause the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
// Try to execute liquidation
|
|
||||||
await expect(pool.connect(user.signer).repay(dai.address, '1', '1', user.address)).revertedWith(
|
|
||||||
LP_IS_PAUSED
|
|
||||||
);
|
|
||||||
|
|
||||||
// Unpause the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Flash loan', async () => {
|
|
||||||
const { dai, pool, weth, users, configurator } = testEnv;
|
|
||||||
|
|
||||||
const caller = users[3];
|
|
||||||
|
|
||||||
const flashAmount = parseEther('0.8');
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
||||||
|
|
||||||
// Pause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(caller.signer)
|
|
||||||
.flashLoan(
|
|
||||||
_mockFlashLoanReceiver.address,
|
|
||||||
[weth.address],
|
|
||||||
[flashAmount],
|
|
||||||
[1],
|
|
||||||
caller.address,
|
|
||||||
'0x10',
|
|
||||||
'0'
|
|
||||||
)
|
|
||||||
).revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Unpause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Liquidation call', async () => {
|
|
||||||
const { users, pool, usdc, oracle, weth, configurator, helpersContract } = testEnv;
|
|
||||||
const depositor = users[3];
|
|
||||||
const borrower = users[4];
|
|
||||||
|
|
||||||
//mints USDC to depositor
|
|
||||||
await usdc
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await usdc.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//user 3 deposits 1000 USDC
|
|
||||||
const amountUSDCtoDeposit = await convertToCurrencyDecimals(usdc.address, '1000');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.deposit(usdc.address, amountUSDCtoDeposit, depositor.address, '0');
|
|
||||||
|
|
||||||
//user 4 deposits 1 ETH
|
|
||||||
const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1');
|
|
||||||
|
|
||||||
//mints WETH to borrower
|
|
||||||
await weth.connect(borrower.signer).mint(amountETHtoDeposit);
|
|
||||||
|
|
||||||
//approve protocol to access borrower wallet
|
|
||||||
await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.deposit(weth.address, amountETHtoDeposit, borrower.address, '0');
|
|
||||||
|
|
||||||
//user 4 borrows
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
|
||||||
|
|
||||||
const amountUSDCToBorrow = await convertToCurrencyDecimals(
|
|
||||||
usdc.address,
|
|
||||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
|
||||||
.div(usdcPrice.toString())
|
|
||||||
.multipliedBy(0.9502)
|
|
||||||
.toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
|
|
||||||
|
|
||||||
// Drops HF below 1
|
|
||||||
await oracle.setAssetPrice(
|
|
||||||
usdc.address,
|
|
||||||
new BigNumber(usdcPrice.toString()).multipliedBy(1.2).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
//mints dai to the liquidator
|
|
||||||
await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
|
||||||
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
const userReserveDataBefore = await helpersContract.getUserReserveData(
|
|
||||||
usdc.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString())
|
|
||||||
.multipliedBy(0.5)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
// Pause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
// Do liquidation
|
|
||||||
await expect(
|
|
||||||
pool.liquidationCall(weth.address, usdc.address, borrower.address, amountToLiquidate, true)
|
|
||||||
).revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Unpause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('SwapBorrowRateMode', async () => {
|
|
||||||
const { pool, weth, dai, usdc, users, configurator } = testEnv;
|
|
||||||
const user = users[1];
|
|
||||||
const amountWETHToDeposit = parseEther('10');
|
|
||||||
const amountDAIToDeposit = parseEther('120');
|
|
||||||
const amountToBorrow = parseUnits('65', 6);
|
|
||||||
|
|
||||||
await weth.connect(user.signer).mint(amountWETHToDeposit);
|
|
||||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0');
|
|
||||||
|
|
||||||
await dai.connect(user.signer).mint(amountDAIToDeposit);
|
|
||||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
|
||||||
|
|
||||||
// Pause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
// Try to repay
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).swapBorrowRateMode(usdc.address, RateMode.Stable)
|
|
||||||
).revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Unpause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('RebalanceStableBorrowRate', async () => {
|
|
||||||
const { pool, dai, users, configurator } = testEnv;
|
|
||||||
const user = users[1];
|
|
||||||
// Pause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).rebalanceStableBorrowRate(dai.address, user.address)
|
|
||||||
).revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Unpause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('setUserUseReserveAsCollateral', async () => {
|
|
||||||
const { pool, weth, users, configurator } = testEnv;
|
|
||||||
const user = users[1];
|
|
||||||
|
|
||||||
const amountWETHToDeposit = parseEther('1');
|
|
||||||
await weth.connect(user.signer).mint(amountWETHToDeposit);
|
|
||||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0');
|
|
||||||
|
|
||||||
// Pause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).setUserUseReserveAsCollateral(weth.address, false)
|
|
||||||
).revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Unpause pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,241 +0,0 @@
|
||||||
// import {iATokenBase, iAssetsWithoutETH, ITestEnvWithoutInstances, RateMode} from '../utils/types';
|
|
||||||
// import {
|
|
||||||
// LendingPoolConfiguratorInstance,
|
|
||||||
// LendingPoolInstance,
|
|
||||||
// ATokenInstance,
|
|
||||||
// LendingPoolCoreInstance,
|
|
||||||
// MintableERC20Instance,
|
|
||||||
// } from '../utils/typechain-types/truffle-contracts';
|
|
||||||
// import {testEnvProviderWithoutInstances} from '../utils/truffle/dlp-tests-env';
|
|
||||||
// import {oneEther, ETHEREUM_ADDRESS} from '../utils/constants';
|
|
||||||
// import {convertToCurrencyDecimals} from '../utils/misc-utils';
|
|
||||||
|
|
||||||
// const expectRevert = require('@openzeppelin/test-helpers').expectRevert;
|
|
||||||
|
|
||||||
// contract('LendingPool: Modifiers', async ([deployer, ...users]) => {
|
|
||||||
// let _testEnvProvider: ITestEnvWithoutInstances;
|
|
||||||
// let _lendingPoolConfiguratorInstance: LendingPoolConfiguratorInstance;
|
|
||||||
// let _lendingPoolInstance: LendingPoolInstance;
|
|
||||||
// let _lendingPoolCoreInstance: LendingPoolCoreInstance;
|
|
||||||
// let _aTokenInstances: iATokenBase<ATokenInstance>;
|
|
||||||
// let _tokenInstances: iAssetsWithoutETH<MintableERC20Instance>;
|
|
||||||
|
|
||||||
// before('Initializing LendingPool test variables', async () => {
|
|
||||||
// console.time('setup-test');
|
|
||||||
// _testEnvProvider = await testEnvProviderWithoutInstances(artifacts, [deployer, ...users]);
|
|
||||||
|
|
||||||
// const {
|
|
||||||
// getAllAssetsInstances,
|
|
||||||
// getLendingPoolInstance,
|
|
||||||
// getLendingPoolCoreInstance,
|
|
||||||
// getLendingPoolConfiguratorInstance,
|
|
||||||
// getATokenInstances,
|
|
||||||
// } = _testEnvProvider;
|
|
||||||
// const instances = await Promise.all([
|
|
||||||
// getLendingPoolInstance(),
|
|
||||||
// getLendingPoolCoreInstance(),
|
|
||||||
// getLendingPoolConfiguratorInstance(),
|
|
||||||
// getATokenInstances(),
|
|
||||||
// getAllAssetsInstances(),
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// _lendingPoolInstance = instances[0];
|
|
||||||
// _lendingPoolCoreInstance = instances[1];
|
|
||||||
// _lendingPoolConfiguratorInstance = instances[2];
|
|
||||||
|
|
||||||
// _aTokenInstances = instances[3];
|
|
||||||
// _tokenInstances = instances[4];
|
|
||||||
// console.timeEnd('setup-test');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to deposit in an inactive reserve', async () => {
|
|
||||||
// //using the deployer address as a fake reserve address
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.deposit(deployer, '1', '0'),
|
|
||||||
// 'Action requires an active reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to invoke redeemUnderlying on an reserve, from a non-aToken address', async () => {
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.redeemUnderlying(ETHEREUM_ADDRESS, deployer, '1', '0'),
|
|
||||||
// 'The caller of this function can only be the aToken contract of this reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to borrow from an inactive reserve', async () => {
|
|
||||||
// //using the deployer address as a fake reserve address
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.borrow(deployer, '1', '0', RateMode.Stable),
|
|
||||||
// 'Action requires an active reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to repay in an inactive reserve', async () => {
|
|
||||||
// //using the deployer address as a fake reserve address
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.repay(deployer, '1', deployer),
|
|
||||||
// 'Action requires an active reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to swapBorrowRateMode on an inactive reserve', async () => {
|
|
||||||
// //using the deployer address as a fake reserve address
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.swapBorrowRateMode(deployer),
|
|
||||||
// 'Action requires an active reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to rebalanceStableBorrowRate on an inactive reserve', async () => {
|
|
||||||
// //using the deployer address as a fake reserve address
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.rebalanceStableBorrowRate(deployer, deployer),
|
|
||||||
// 'Action requires an active reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to setUserUseReserveAsCollateral on an inactive reserve', async () => {
|
|
||||||
// //using the deployer address as a fake reserve address
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.setUserUseReserveAsCollateral(deployer, true),
|
|
||||||
// 'Action requires an active reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to invoke liquidationCall on an inactive reserve', async () => {
|
|
||||||
// //using the deployer address as a fake reserve address
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.liquidationCall(ETHEREUM_ADDRESS, deployer, deployer, '1', false),
|
|
||||||
// 'Action requires an active reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Tries to invoke liquidationCall on an inactive collateral', async () => {
|
|
||||||
// //using the deployer address as a fake reserve address
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.liquidationCall(deployer, ETHEREUM_ADDRESS, deployer, '1', false),
|
|
||||||
// 'Action requires an active reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Freezes the ETH reserve', async () => {
|
|
||||||
// await _lendingPoolConfiguratorInstance.freezeReserve(ETHEREUM_ADDRESS);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('tries to deposit in a freezed reserve', async () => {
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.deposit(ETHEREUM_ADDRESS, '1', '0'),
|
|
||||||
// 'Action requires an unfreezed reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('tries to borrow from a freezed reserve', async () => {
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.borrow(ETHEREUM_ADDRESS, '1', '0', '0'),
|
|
||||||
// 'Action requires an unfreezed reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('tries to swap interest rate mode in a freezed reserve', async () => {
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.swapBorrowRateMode(ETHEREUM_ADDRESS),
|
|
||||||
// 'Action requires an unfreezed reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('tries to disable as collateral a freezed reserve', async () => {
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.setUserUseReserveAsCollateral(ETHEREUM_ADDRESS, false),
|
|
||||||
// 'Action requires an unfreezed reserve'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('unfreezes the reserve, user deposits 1 ETH, freezes the reserve, check that the user can redeem', async () => {
|
|
||||||
// const {aWETH} = _aTokenInstances;
|
|
||||||
|
|
||||||
// //unfreezes the reserve
|
|
||||||
// await _lendingPoolConfiguratorInstance.unfreezeReserve(ETHEREUM_ADDRESS);
|
|
||||||
|
|
||||||
// //deposit 1 ETH
|
|
||||||
// await _lendingPoolInstance.deposit(ETHEREUM_ADDRESS, oneEther, '0', {
|
|
||||||
// value: oneEther.toString(),
|
|
||||||
// });
|
|
||||||
|
|
||||||
// //freezes the reserve
|
|
||||||
// await _lendingPoolConfiguratorInstance.freezeReserve(ETHEREUM_ADDRESS);
|
|
||||||
|
|
||||||
// const balance = await aWETH.balanceOf(deployer);
|
|
||||||
|
|
||||||
// await aWETH.redeem(balance);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('unfreezes the reserve, user 0 deposits 100 DAI, user 1 deposits 1 ETH and borrows 50 DAI, freezes the reserve, checks that the user 1 can repay', async () => {
|
|
||||||
// const {aWETH, aDAI} = _aTokenInstances;
|
|
||||||
// const {DAI} = _tokenInstances;
|
|
||||||
|
|
||||||
// //unfreezes the reserve
|
|
||||||
// await _lendingPoolConfiguratorInstance.unfreezeReserve(ETHEREUM_ADDRESS);
|
|
||||||
|
|
||||||
// const amountDAI = await convertToCurrencyDecimals(DAI.address, '100');
|
|
||||||
|
|
||||||
// //user 0 deposits 100 DAI
|
|
||||||
// await DAI.mint(amountDAI, {from: users[0]});
|
|
||||||
|
|
||||||
// await DAI.approve(_lendingPoolCoreInstance.address, amountDAI, {from: users[0]});
|
|
||||||
|
|
||||||
// await _lendingPoolInstance.deposit(DAI.address, amountDAI, '0', {from: users[0]});
|
|
||||||
|
|
||||||
// //user 1 deposits 1 ETH
|
|
||||||
// await _lendingPoolInstance.deposit(ETHEREUM_ADDRESS, oneEther, '0', {
|
|
||||||
// from: users[1],
|
|
||||||
// value: oneEther.toString(),
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const amountDAIToBorrow = await convertToCurrencyDecimals(DAI.address, '10');
|
|
||||||
|
|
||||||
// //user 1 borrows 10 DAI
|
|
||||||
// await _lendingPoolInstance.borrow(DAI.address, amountDAIToBorrow, RateMode.Stable, '0', {
|
|
||||||
// from: users[1],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// //freezes the reserve
|
|
||||||
// await _lendingPoolConfiguratorInstance.freezeReserve(ETHEREUM_ADDRESS);
|
|
||||||
|
|
||||||
// //user 1 repays 1 DAI
|
|
||||||
// await DAI.approve(_lendingPoolCoreInstance.address, amountDAIToBorrow, {from: users[1]});
|
|
||||||
|
|
||||||
// await _lendingPoolInstance.repay(DAI.address, oneEther, users[1], {from: users[1]});
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Check that liquidationCall can be executed on a freezed reserve', async () => {
|
|
||||||
// const {aWETH, aDAI} = _aTokenInstances;
|
|
||||||
// const {DAI} = _tokenInstances;
|
|
||||||
|
|
||||||
// //user 2 tries to liquidate
|
|
||||||
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.liquidationCall(
|
|
||||||
// ETHEREUM_ADDRESS,
|
|
||||||
// DAI.address,
|
|
||||||
// users[1],
|
|
||||||
// oneEther,
|
|
||||||
// true,
|
|
||||||
// {from: users[2]}
|
|
||||||
// ),
|
|
||||||
// 'Health factor is not below the threshold'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('Check that rebalanceStableBorrowRate can be executed on a freezed reserve', async () => {
|
|
||||||
// const {aWETH, aDAI} = _aTokenInstances;
|
|
||||||
// const {DAI} = _tokenInstances;
|
|
||||||
|
|
||||||
// //user 2 tries to liquidate
|
|
||||||
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.rebalanceStableBorrowRate(DAI.address, users[1]),
|
|
||||||
// 'Interest rate rebalance conditions were not met'
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
// });
|
|
|
@ -1,165 +0,0 @@
|
||||||
// import {
|
|
||||||
// IReserveParams,
|
|
||||||
// iAavePoolAssets,
|
|
||||||
// iAssetsWithoutETH,
|
|
||||||
// ITestEnvWithoutInstances,
|
|
||||||
// } from "../utils/types"
|
|
||||||
// import {
|
|
||||||
|
|
||||||
// LendingPoolAddressesProviderInstance,
|
|
||||||
|
|
||||||
// DefaultReserveInterestRateStrategyInstance,
|
|
||||||
// MintableERC20Instance,
|
|
||||||
// } from "../utils/typechain-types/truffle-contracts"
|
|
||||||
// import { testEnvProviderWithoutInstances} from "../utils/truffle/dlp-tests-env"
|
|
||||||
// import {RAY} from "../utils/constants"
|
|
||||||
// import BigNumber from "bignumber.js"
|
|
||||||
|
|
||||||
// const {expect} = require("chai")
|
|
||||||
|
|
||||||
// contract("Interest rate strategy", async ([deployer, ...users]) => {
|
|
||||||
// let _testEnvProvider: ITestEnvWithoutInstances
|
|
||||||
// let _strategyInstance: DefaultReserveInterestRateStrategyInstance
|
|
||||||
// let _tokenInstances: iAssetsWithoutETH<MintableERC20Instance>
|
|
||||||
// let _addressesProviderInstance: LendingPoolAddressesProviderInstance
|
|
||||||
// let _reservesParams: iAavePoolAssets<IReserveParams>
|
|
||||||
|
|
||||||
// before("Initializing test variables", async () => {
|
|
||||||
// console.time('setup-test');
|
|
||||||
// _testEnvProvider = await testEnvProviderWithoutInstances(
|
|
||||||
// artifacts,
|
|
||||||
// [deployer, ...users],
|
|
||||||
// )
|
|
||||||
|
|
||||||
// const {
|
|
||||||
// getAllAssetsInstances,
|
|
||||||
// getLendingPoolAddressesProviderInstance,
|
|
||||||
// getAavePoolReservesParams,
|
|
||||||
// } = _testEnvProvider
|
|
||||||
|
|
||||||
// const instances = await Promise.all([
|
|
||||||
// getAllAssetsInstances(),
|
|
||||||
// getLendingPoolAddressesProviderInstance()
|
|
||||||
// ])
|
|
||||||
|
|
||||||
// _tokenInstances = instances[0]
|
|
||||||
// _addressesProviderInstance = instances[1]
|
|
||||||
// _reservesParams = await getAavePoolReservesParams()
|
|
||||||
// console.timeEnd('setup-test');
|
|
||||||
// })
|
|
||||||
|
|
||||||
// it("Deploys a new instance of a DefaultReserveInterestRateStrategy contract", async () => {
|
|
||||||
// const {DAI: daiInstance} = _tokenInstances
|
|
||||||
|
|
||||||
// const {DAI: daiConfiguration} = _reservesParams
|
|
||||||
|
|
||||||
// const contract: any = await artifacts.require("DefaultReserveInterestRateStrategy")
|
|
||||||
// const mathLibrary = await artifacts.require("WadRayMath")
|
|
||||||
// const mathLibraryInstance = await mathLibrary.new()
|
|
||||||
|
|
||||||
// await contract.link("WadRayMath", mathLibraryInstance.address)
|
|
||||||
|
|
||||||
// _strategyInstance = await contract.new(
|
|
||||||
// daiInstance.address,
|
|
||||||
// _addressesProviderInstance.address,
|
|
||||||
// daiConfiguration.baseVariableBorrowRate,
|
|
||||||
// daiConfiguration.variableRateSlope1,
|
|
||||||
// daiConfiguration.variableRateSlope2,
|
|
||||||
// daiConfiguration.stableRateSlope1,
|
|
||||||
// daiConfiguration.stableRateSlope2,
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
|
|
||||||
// it("Checks rates at 0% utilization rate", async () => {
|
|
||||||
// const {DAI: daiInstance} = _tokenInstances
|
|
||||||
// const {DAI: daiConfiguration} = _reservesParams
|
|
||||||
// const data: any = await _strategyInstance.calculateInterestRates(
|
|
||||||
// daiInstance.address,
|
|
||||||
// "1000000000000000000",
|
|
||||||
// "0",
|
|
||||||
// "0",
|
|
||||||
// "0",
|
|
||||||
// )
|
|
||||||
|
|
||||||
// expect(data.currentLiquidityRate.toString()).to.be.equal("0", "Invalid liquidity rate")
|
|
||||||
// expect(data.currentStableBorrowRate.toString()).to.be.equal(
|
|
||||||
// new BigNumber(0.039).times(RAY).toFixed(0),
|
|
||||||
// "Invalid stable rate",
|
|
||||||
// )
|
|
||||||
// expect(data.currentVariableBorrowRate.toString()).to.be.equal(
|
|
||||||
// daiConfiguration.baseVariableBorrowRate,
|
|
||||||
// "Invalid variable rate",
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
|
|
||||||
// it("Checks rates at 80% utilization rate", async () => {
|
|
||||||
// const {DAI: daiInstance} = _tokenInstances
|
|
||||||
// const {DAI: daiConfiguration} = _reservesParams
|
|
||||||
// const data: any = await _strategyInstance.calculateInterestRates(
|
|
||||||
// daiInstance.address,
|
|
||||||
// "200000000000000000",
|
|
||||||
// "0",
|
|
||||||
// "800000000000000000",
|
|
||||||
// "0",
|
|
||||||
// )
|
|
||||||
|
|
||||||
// const expectedVariableRate = new BigNumber(daiConfiguration.baseVariableBorrowRate)
|
|
||||||
// .plus(daiConfiguration.variableRateSlope1)
|
|
||||||
|
|
||||||
// expect(data.currentLiquidityRate.toString()).to.be.equal(
|
|
||||||
// expectedVariableRate.times(0.8).toFixed(0),
|
|
||||||
// "Invalid liquidity rate",
|
|
||||||
// )
|
|
||||||
|
|
||||||
// expect(data.currentVariableBorrowRate.toString()).to.be.equal(
|
|
||||||
// new BigNumber(daiConfiguration.baseVariableBorrowRate)
|
|
||||||
// .plus(daiConfiguration.variableRateSlope1)
|
|
||||||
// .toFixed(0),
|
|
||||||
// "Invalid variable rate",
|
|
||||||
// )
|
|
||||||
|
|
||||||
// expect(data.currentStableBorrowRate.toString()).to.be.equal(
|
|
||||||
// new BigNumber(0.039)
|
|
||||||
// .times(RAY)
|
|
||||||
// .plus(daiConfiguration.stableRateSlope1)
|
|
||||||
// .toFixed(0),
|
|
||||||
// "Invalid stable rate",
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
|
|
||||||
// it("Checks rates at 100% utilization rate", async () => {
|
|
||||||
// const {DAI: daiInstance} = _tokenInstances
|
|
||||||
// const {DAI: daiConfiguration} = _reservesParams
|
|
||||||
// const data: any = await _strategyInstance.calculateInterestRates(
|
|
||||||
// daiInstance.address,
|
|
||||||
// "0",
|
|
||||||
// "0",
|
|
||||||
// "1000000000000000000",
|
|
||||||
// "0",
|
|
||||||
// )
|
|
||||||
|
|
||||||
// const expectedVariableRate = new BigNumber(daiConfiguration.baseVariableBorrowRate)
|
|
||||||
// .plus(daiConfiguration.variableRateSlope1)
|
|
||||||
// .plus(daiConfiguration.variableRateSlope2)
|
|
||||||
// .toFixed(0)
|
|
||||||
|
|
||||||
// expect(data.currentLiquidityRate.toString()).to.be.equal(
|
|
||||||
// expectedVariableRate,
|
|
||||||
// "Invalid liquidity rate",
|
|
||||||
// )
|
|
||||||
|
|
||||||
// expect(data.currentVariableBorrowRate.toString()).to.be.equal(
|
|
||||||
// expectedVariableRate,
|
|
||||||
// "Invalid variable rate",
|
|
||||||
// )
|
|
||||||
|
|
||||||
// expect(data.currentStableBorrowRate.toString()).to.be.equal(
|
|
||||||
// new BigNumber(0.039)
|
|
||||||
// .times(RAY)
|
|
||||||
// .plus(daiConfiguration.stableRateSlope1)
|
|
||||||
// .plus(daiConfiguration.stableRateSlope2)
|
|
||||||
// .toFixed(0),
|
|
||||||
// "Invalid stable rate",
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
// })
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { configuration as actionsConfiguration } from './helpers/actions';
|
|
||||||
import { configuration as calculationsConfiguration } from './helpers/utils/calculations';
|
|
||||||
|
|
||||||
import fs from 'fs';
|
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
import { makeSuite } from './helpers/make-suite';
|
|
||||||
import { getReservesConfigByPool } from '../helpers/configuration';
|
|
||||||
import { AavePools, iAavePoolAssets, IReserveParams } from '../helpers/types';
|
|
||||||
import { executeStory } from './helpers/scenario-engine';
|
|
||||||
|
|
||||||
const scenarioFolder = './test/helpers/scenarios/';
|
|
||||||
|
|
||||||
const selectedScenarios: string[] = [];
|
|
||||||
|
|
||||||
fs.readdirSync(scenarioFolder).forEach((file) => {
|
|
||||||
if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return;
|
|
||||||
|
|
||||||
const scenario = require(`./helpers/scenarios/${file}`);
|
|
||||||
|
|
||||||
makeSuite(scenario.title, async (testEnv) => {
|
|
||||||
before('Initializing configuration', async () => {
|
|
||||||
// Sets BigNumber for this suite, instead of globally
|
|
||||||
BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN });
|
|
||||||
|
|
||||||
actionsConfiguration.skipIntegrityCheck = false; //set this to true to execute solidity-coverage
|
|
||||||
|
|
||||||
calculationsConfiguration.reservesParams = <iAavePoolAssets<IReserveParams>>(
|
|
||||||
getReservesConfigByPool(AavePools.proto)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
after('Reset', () => {
|
|
||||||
// Reset BigNumber
|
|
||||||
BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP });
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const story of scenario.stories) {
|
|
||||||
it(story.description, async function () {
|
|
||||||
// Retry the test scenarios up to 4 times if an error happens, due erratic HEVM network errors
|
|
||||||
this.retries(4);
|
|
||||||
await executeStory(story, testEnv);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,199 +0,0 @@
|
||||||
// import {
|
|
||||||
// LendingPoolInstance,
|
|
||||||
// LendingPoolCoreInstance,
|
|
||||||
// MintableERC20Instance,
|
|
||||||
// ATokenInstance,
|
|
||||||
// } from "../utils/typechain-types/truffle-contracts"
|
|
||||||
// import {
|
|
||||||
// iATokenBase,
|
|
||||||
// iAssetsWithoutETH,
|
|
||||||
// ITestEnvWithoutInstances,
|
|
||||||
// RateMode,
|
|
||||||
// } from "../utils/types"
|
|
||||||
// import {
|
|
||||||
// APPROVAL_AMOUNT_LENDING_POOL_CORE,
|
|
||||||
// ETHEREUM_ADDRESS,
|
|
||||||
// } from "../utils/constants"
|
|
||||||
// import { testEnvProviderWithoutInstances} from "../utils/truffle/dlp-tests-env"
|
|
||||||
// import {convertToCurrencyDecimals} from "../utils/misc-utils"
|
|
||||||
|
|
||||||
// const expectRevert = require("@openzeppelin/test-helpers").expectRevert
|
|
||||||
|
|
||||||
// contract("LendingPool - stable rate economy tests", async ([deployer, ...users]) => {
|
|
||||||
// let _testEnvProvider: ITestEnvWithoutInstances
|
|
||||||
// let _lendingPoolInstance: LendingPoolInstance
|
|
||||||
// let _lendingPoolCoreInstance: LendingPoolCoreInstance
|
|
||||||
// let _aTokenInstances: iATokenBase<ATokenInstance>
|
|
||||||
// let _tokenInstances: iAssetsWithoutETH<MintableERC20Instance>
|
|
||||||
|
|
||||||
// let _daiAddress: string
|
|
||||||
|
|
||||||
// let _depositorAddress: string
|
|
||||||
// let _borrowerAddress: string
|
|
||||||
|
|
||||||
// let _web3: Web3
|
|
||||||
|
|
||||||
// before("Initializing LendingPool test variables", async () => {
|
|
||||||
// console.time('setup-test');
|
|
||||||
// _testEnvProvider = await testEnvProviderWithoutInstances(
|
|
||||||
// artifacts,
|
|
||||||
// [deployer, ...users]
|
|
||||||
// )
|
|
||||||
|
|
||||||
// const {
|
|
||||||
// getWeb3,
|
|
||||||
// getAllAssetsInstances,
|
|
||||||
// getFirstBorrowerAddressOnTests,
|
|
||||||
// getFirstDepositorAddressOnTests,
|
|
||||||
// getLendingPoolInstance,
|
|
||||||
// getLendingPoolCoreInstance,
|
|
||||||
// getATokenInstances
|
|
||||||
// } = _testEnvProvider
|
|
||||||
|
|
||||||
// const instances = await Promise.all([
|
|
||||||
// getLendingPoolInstance(),
|
|
||||||
// getLendingPoolCoreInstance(),
|
|
||||||
// getATokenInstances(),
|
|
||||||
// getAllAssetsInstances()
|
|
||||||
// ])
|
|
||||||
// _lendingPoolInstance = instances[0]
|
|
||||||
// _lendingPoolCoreInstance = instances[1]
|
|
||||||
// _aTokenInstances = instances[2]
|
|
||||||
// _tokenInstances = instances[3]
|
|
||||||
// _daiAddress = _tokenInstances.DAI.address
|
|
||||||
// _depositorAddress = await getFirstDepositorAddressOnTests()
|
|
||||||
// _borrowerAddress = await getFirstBorrowerAddressOnTests()
|
|
||||||
|
|
||||||
// _web3 = await getWeb3()
|
|
||||||
// console.timeEnd('setup-test');
|
|
||||||
// })
|
|
||||||
|
|
||||||
// it("BORROW - Test user cannot borrow using the same currency as collateral", async () => {
|
|
||||||
// const {aDAI: aDaiInstance} = _aTokenInstances
|
|
||||||
// const {DAI: daiInstance} = _tokenInstances
|
|
||||||
|
|
||||||
// //mints DAI to depositor
|
|
||||||
// await daiInstance.mint(await convertToCurrencyDecimals(daiInstance.address, "1000"), {
|
|
||||||
// from: _depositorAddress,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// //mints DAI to borrower
|
|
||||||
// await daiInstance.mint(await convertToCurrencyDecimals(daiInstance.address, "1000"), {
|
|
||||||
// from: _borrowerAddress,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// //approve protocol to access depositor wallet
|
|
||||||
// await daiInstance.approve(_lendingPoolCoreInstance.address, APPROVAL_AMOUNT_LENDING_POOL_CORE, {
|
|
||||||
// from: _depositorAddress,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// //approve protocol to access borrower wallet
|
|
||||||
// await daiInstance.approve(_lendingPoolCoreInstance.address, APPROVAL_AMOUNT_LENDING_POOL_CORE, {
|
|
||||||
// from: _borrowerAddress,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const amountDAItoDeposit = await convertToCurrencyDecimals(_daiAddress, "1000")
|
|
||||||
|
|
||||||
// //user 1 deposits 1000 DAI
|
|
||||||
// const txResult = await _lendingPoolInstance.deposit(_daiAddress, amountDAItoDeposit, "0", {
|
|
||||||
// from: _depositorAddress,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// //user 2 deposits 1000 DAI, tries to borrow. Needs to be reverted as you can't borrow at a stable rate with the same collateral as the currency.
|
|
||||||
// const amountDAIToDepositBorrower = await convertToCurrencyDecimals(_daiAddress, "1000")
|
|
||||||
// await _lendingPoolInstance.deposit(_daiAddress, amountDAIToDepositBorrower, "0", {
|
|
||||||
// from: _borrowerAddress,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const data: any = await _lendingPoolInstance.getReserveData(_daiAddress)
|
|
||||||
|
|
||||||
// //user 2 tries to borrow
|
|
||||||
// const amountDAIToBorrow = await convertToCurrencyDecimals(_daiAddress, "250")
|
|
||||||
|
|
||||||
// //user 2 tries to borrow
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.borrow(_daiAddress, amountDAIToBorrow, RateMode.Stable, "0", {
|
|
||||||
// from: _borrowerAddress,
|
|
||||||
// }),
|
|
||||||
// "User cannot borrow the selected amount with a stable rate",
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
|
|
||||||
// it("BORROW - Test user cannot borrow more than 25% of the liquidity available", async () => {
|
|
||||||
// const {aDAI: aDaiInstance} = _aTokenInstances
|
|
||||||
// const {DAI: daiInstance} = _tokenInstances
|
|
||||||
|
|
||||||
// //redeem the DAI previously deposited
|
|
||||||
// const amountADAIToRedeem = await convertToCurrencyDecimals(aDaiInstance.address, "1000")
|
|
||||||
// await aDaiInstance.redeem(amountADAIToRedeem, {
|
|
||||||
// from: _borrowerAddress,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// //user 2 deposits 5 ETH tries to borrow. needs to be reverted as you can't borrow more than 25% of the available reserve (250 DAI)
|
|
||||||
// const amountETHToDeposit = await convertToCurrencyDecimals(ETHEREUM_ADDRESS, "5")
|
|
||||||
// await _lendingPoolInstance.deposit(ETHEREUM_ADDRESS, amountETHToDeposit, "0", {
|
|
||||||
// from: _borrowerAddress,
|
|
||||||
// value: amountETHToDeposit,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const data: any = await _lendingPoolInstance.getReserveData(_daiAddress)
|
|
||||||
|
|
||||||
// const amountDAIToBorrow = await convertToCurrencyDecimals(_daiAddress, "500")
|
|
||||||
|
|
||||||
// //user 2 tries to borrow
|
|
||||||
// await expectRevert(
|
|
||||||
// _lendingPoolInstance.borrow(_daiAddress, amountDAIToBorrow, RateMode.Stable, "0", {
|
|
||||||
// from: _borrowerAddress,
|
|
||||||
// }),
|
|
||||||
// "User is trying to borrow too much liquidity at a stable rate",
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
|
|
||||||
// it("BORROW - Test user can still borrow a currency that he previously deposited as a collateral but he transferred/redeemed", async () => {
|
|
||||||
// const {aDAI: aDaiInstance} = _aTokenInstances
|
|
||||||
// const {DAI: daiInstance} = _tokenInstances
|
|
||||||
|
|
||||||
// const user = users[2]
|
|
||||||
|
|
||||||
// //user deposits 1000 DAI
|
|
||||||
// await daiInstance.mint(await convertToCurrencyDecimals(daiInstance.address, "1000"), {
|
|
||||||
// from: user,
|
|
||||||
// })
|
|
||||||
// await daiInstance.approve(_lendingPoolCoreInstance.address, APPROVAL_AMOUNT_LENDING_POOL_CORE, {
|
|
||||||
// from: user,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const amountDAIToDeposit = await convertToCurrencyDecimals(daiInstance.address, "1000")
|
|
||||||
// await _lendingPoolInstance.deposit(daiInstance.address, amountDAIToDeposit, "0", {
|
|
||||||
// from: user,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// //user deposits 5 ETH as collateral
|
|
||||||
// const amountETHToDeposit = await convertToCurrencyDecimals(ETHEREUM_ADDRESS, "5")
|
|
||||||
// await _lendingPoolInstance.deposit(ETHEREUM_ADDRESS, amountETHToDeposit, "0", {
|
|
||||||
// from: user,
|
|
||||||
// value: amountETHToDeposit,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// //user transfers to another address all the overlying aDAI
|
|
||||||
|
|
||||||
// const aDAIBalance = await aDaiInstance.balanceOf(user)
|
|
||||||
|
|
||||||
// await aDaiInstance.transfer(users[3], aDAIBalance, {
|
|
||||||
// from: user,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// //check the underlying balance is 0
|
|
||||||
// const userData: any = await _lendingPoolInstance.getUserReserveData(daiInstance.address, user)
|
|
||||||
|
|
||||||
// expect(userData.currentATokenBalance.toString()).to.be.equal("0")
|
|
||||||
|
|
||||||
// //user tries to borrow the DAI at a stable rate using the ETH as collateral
|
|
||||||
// const amountDAIToBorrow = await convertToCurrencyDecimals(_daiAddress, "100")
|
|
||||||
|
|
||||||
// //user tries to borrow. No revert expected
|
|
||||||
// await _lendingPoolInstance.borrow(_daiAddress, amountDAIToBorrow, RateMode.Stable, "0", {
|
|
||||||
// from: user,
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
// })
|
|
|
@ -1,37 +0,0 @@
|
||||||
import { expect } from 'chai';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { ProtocolErrors } from '../helpers/types';
|
|
||||||
import { getStableDebtToken } from '../helpers/contracts-getters';
|
|
||||||
|
|
||||||
makeSuite('Stable debt token tests', (testEnv: TestEnv) => {
|
|
||||||
const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors;
|
|
||||||
|
|
||||||
it('Tries to invoke mint not being the LendingPool', async () => {
|
|
||||||
const { deployer, pool, dai, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
|
|
||||||
.stableDebtTokenAddress;
|
|
||||||
|
|
||||||
const stableDebtContract = await getStableDebtToken(daiStableDebtTokenAddress);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
stableDebtContract.mint(deployer.address, deployer.address, '1', '1')
|
|
||||||
).to.be.revertedWith(CT_CALLER_MUST_BE_LENDING_POOL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to invoke burn not being the LendingPool', async () => {
|
|
||||||
const { deployer, dai, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address))
|
|
||||||
.stableDebtTokenAddress;
|
|
||||||
|
|
||||||
const stableDebtContract = await getStableDebtToken(daiStableDebtTokenAddress);
|
|
||||||
|
|
||||||
const name = await stableDebtContract.name();
|
|
||||||
|
|
||||||
expect(name).to.be.equal('Aave stable debt bearing DAI');
|
|
||||||
await expect(stableDebtContract.burn(deployer.address, '1')).to.be.revertedWith(
|
|
||||||
CT_CALLER_MUST_BE_LENDING_POOL
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,32 +0,0 @@
|
||||||
import { configuration as actionsConfiguration } from './helpers/actions';
|
|
||||||
import { configuration as calculationsConfiguration } from './helpers/utils/calculations';
|
|
||||||
|
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
import { makeSuite } from './helpers/make-suite';
|
|
||||||
import { getReservesConfigByPool } from '../helpers/configuration';
|
|
||||||
import { AavePools, iAavePoolAssets, IReserveParams } from '../helpers/types';
|
|
||||||
import { executeStory } from './helpers/scenario-engine';
|
|
||||||
|
|
||||||
makeSuite('Subgraph scenario tests', async (testEnv) => {
|
|
||||||
let story: any;
|
|
||||||
let scenario;
|
|
||||||
before('Initializing configuration', async () => {
|
|
||||||
const scenario = require(`./helpers/scenarios/borrow-repay-stable`);
|
|
||||||
story = scenario.stories[0];
|
|
||||||
// Sets BigNumber for this suite, instead of globally
|
|
||||||
BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN });
|
|
||||||
|
|
||||||
actionsConfiguration.skipIntegrityCheck = false; //set this to true to execute solidity-coverage
|
|
||||||
|
|
||||||
calculationsConfiguration.reservesParams = <iAavePoolAssets<IReserveParams>>(
|
|
||||||
getReservesConfigByPool(AavePools.proto)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
after('Reset', () => {
|
|
||||||
// Reset BigNumber
|
|
||||||
BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP });
|
|
||||||
});
|
|
||||||
it('deposit-borrow', async () => {
|
|
||||||
await executeStory(story, testEnv);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,227 +0,0 @@
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,850 +0,0 @@
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import {
|
|
||||||
convertToCurrencyDecimals,
|
|
||||||
buildFlashLiquidationAdapterParams,
|
|
||||||
} from '../helpers/contracts-helpers';
|
|
||||||
import { getMockUniswapRouter } from '../helpers/contracts-getters';
|
|
||||||
import { deployFlashLiquidationAdapter } from '../helpers/contracts-deployments';
|
|
||||||
import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02';
|
|
||||||
import BigNumber from 'bignumber.js';
|
|
||||||
import { DRE, evmRevert, evmSnapshot, increaseTime, waitForTx } from '../helpers/misc-utils';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import { ProtocolErrors, RateMode } from '../helpers/types';
|
|
||||||
import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, oneEther } from '../helpers/constants';
|
|
||||||
import { getUserData } from './helpers/utils/helpers';
|
|
||||||
import { calcExpectedStableDebtTokenBalance } from './helpers/utils/calculations';
|
|
||||||
const { expect } = require('chai');
|
|
||||||
|
|
||||||
makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|
||||||
let mockUniswapRouter: MockUniswapV2Router02;
|
|
||||||
let evmSnapshotId: string;
|
|
||||||
const { INVALID_HF, LP_LIQUIDATION_CALL_FAILED } = ProtocolErrors;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
mockUniswapRouter = await getMockUniswapRouter();
|
|
||||||
});
|
|
||||||
|
|
||||||
const depositAndHFBelowOne = async () => {
|
|
||||||
const { dai, weth, users, pool, oracle } = testEnv;
|
|
||||||
const depositor = users[0];
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
//mints DAI to depositor
|
|
||||||
await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//user 1 deposits 1000 DAI
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.deposit(dai.address, amountDAItoDeposit, depositor.address, '0');
|
|
||||||
//user 2 deposits 1 ETH
|
|
||||||
const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1');
|
|
||||||
|
|
||||||
//mints WETH to borrower
|
|
||||||
await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access the borrower wallet
|
|
||||||
await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.deposit(weth.address, amountETHtoDeposit, borrower.address, '0');
|
|
||||||
|
|
||||||
//user 2 borrows
|
|
||||||
|
|
||||||
const userGlobalDataBefore = await pool.getUserAccountData(borrower.address);
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
|
|
||||||
const amountDAIToBorrow = await convertToCurrencyDecimals(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(userGlobalDataBefore.availableBorrowsETH.toString())
|
|
||||||
.div(daiPrice.toString())
|
|
||||||
.multipliedBy(0.95)
|
|
||||||
.toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address);
|
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.equal(
|
|
||||||
'8250',
|
|
||||||
INVALID_HF
|
|
||||||
);
|
|
||||||
|
|
||||||
await oracle.setAssetPrice(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(daiPrice.toString()).multipliedBy(1.18).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(
|
|
||||||
oneEther.toFixed(0),
|
|
||||||
INVALID_HF
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const depositSameAssetAndHFBelowOne = async () => {
|
|
||||||
const { dai, weth, users, pool, oracle } = testEnv;
|
|
||||||
const depositor = users[0];
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
//mints DAI to depositor
|
|
||||||
await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access depositor wallet
|
|
||||||
await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
//user 1 deposits 1000 DAI
|
|
||||||
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.deposit(dai.address, amountDAItoDeposit, depositor.address, '0');
|
|
||||||
//user 2 deposits 1 ETH
|
|
||||||
const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1');
|
|
||||||
|
|
||||||
//mints WETH to borrower
|
|
||||||
await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000'));
|
|
||||||
|
|
||||||
//approve protocol to access the borrower wallet
|
|
||||||
await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.deposit(weth.address, amountETHtoDeposit, borrower.address, '0');
|
|
||||||
|
|
||||||
//user 2 borrows
|
|
||||||
|
|
||||||
const userGlobalDataBefore = await pool.getUserAccountData(borrower.address);
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
|
|
||||||
const amountDAIToBorrow = await convertToCurrencyDecimals(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(userGlobalDataBefore.availableBorrowsETH.toString())
|
|
||||||
.div(daiPrice.toString())
|
|
||||||
.multipliedBy(0.8)
|
|
||||||
.toFixed(0)
|
|
||||||
);
|
|
||||||
await waitForTx(
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalDataBefore2 = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
const amountWETHToBorrow = new BigNumber(userGlobalDataBefore2.availableBorrowsETH.toString())
|
|
||||||
.multipliedBy(0.8)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
await pool
|
|
||||||
.connect(borrower.signer)
|
|
||||||
.borrow(weth.address, amountWETHToBorrow, RateMode.Variable, '0', borrower.address);
|
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.equal(
|
|
||||||
'8250',
|
|
||||||
INVALID_HF
|
|
||||||
);
|
|
||||||
|
|
||||||
await oracle.setAssetPrice(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(daiPrice.toString()).multipliedBy(1.18).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
const userGlobalData = await pool.getUserAccountData(borrower.address);
|
|
||||||
|
|
||||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(
|
|
||||||
oneEther.toFixed(0),
|
|
||||||
INVALID_HF
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
evmSnapshotId = await evmSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await evmRevert(evmSnapshotId);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Flash Liquidation Adapter', () => {
|
|
||||||
before('Before LendingPool liquidation: set config', () => {
|
|
||||||
BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN });
|
|
||||||
});
|
|
||||||
|
|
||||||
after('After LendingPool liquidation: reset config', () => {
|
|
||||||
BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP });
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('constructor', () => {
|
|
||||||
it('should deploy with correct parameters', async () => {
|
|
||||||
const { addressesProvider, weth } = testEnv;
|
|
||||||
await deployFlashLiquidationAdapter([
|
|
||||||
addressesProvider.address,
|
|
||||||
mockUniswapRouter.address,
|
|
||||||
weth.address,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should revert if not valid addresses provider', async () => {
|
|
||||||
const { weth } = testEnv;
|
|
||||||
expect(
|
|
||||||
deployFlashLiquidationAdapter([
|
|
||||||
mockUniswapRouter.address,
|
|
||||||
mockUniswapRouter.address,
|
|
||||||
weth.address,
|
|
||||||
])
|
|
||||||
).to.be.reverted;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('executeOperation: succesfully liquidateCall and swap via Flash Loan with profits', () => {
|
|
||||||
it('Liquidates the borrow with profit', async () => {
|
|
||||||
await depositAndHFBelowOne();
|
|
||||||
await increaseTime(100);
|
|
||||||
|
|
||||||
const {
|
|
||||||
dai,
|
|
||||||
weth,
|
|
||||||
users,
|
|
||||||
pool,
|
|
||||||
oracle,
|
|
||||||
helpersContract,
|
|
||||||
flashLiquidationAdapter,
|
|
||||||
} = testEnv;
|
|
||||||
|
|
||||||
const liquidator = users[3];
|
|
||||||
const borrower = users[1];
|
|
||||||
const expectedSwap = ethers.utils.parseEther('0.4');
|
|
||||||
|
|
||||||
const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address);
|
|
||||||
|
|
||||||
// Set how much ETH will be sold and swapped for DAI at Uniswap mock
|
|
||||||
await (await mockUniswapRouter.setAmountToSwap(weth.address, expectedSwap)).wait();
|
|
||||||
|
|
||||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
|
||||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
const daiReserveDataBefore = await helpersContract.getReserveData(dai.address);
|
|
||||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const collateralDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(weth.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const principalDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(dai.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0);
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
|
||||||
.times(new BigNumber(amountToLiquidate).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 flashLoanDebt = new BigNumber(amountToLiquidate.toString())
|
|
||||||
.multipliedBy(1.0009)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
const expectedProfit = ethers.BigNumber.from(expectedCollateralLiquidated.toString()).sub(
|
|
||||||
expectedSwap
|
|
||||||
);
|
|
||||||
|
|
||||||
const params = buildFlashLiquidationAdapterParams(
|
|
||||||
weth.address,
|
|
||||||
dai.address,
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
const tx = await pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.flashLoan(
|
|
||||||
flashLiquidationAdapter.address,
|
|
||||||
[dai.address],
|
|
||||||
[amountToLiquidate],
|
|
||||||
[0],
|
|
||||||
borrower.address,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Expect Swapped event
|
|
||||||
await expect(Promise.resolve(tx)).to.emit(flashLiquidationAdapter, 'Swapped');
|
|
||||||
|
|
||||||
// Expect LiquidationCall event
|
|
||||||
await expect(Promise.resolve(tx)).to.emit(pool, 'LiquidationCall');
|
|
||||||
|
|
||||||
const userReserveDataAfter = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
const liquidatorWethBalanceAfter = await weth.balanceOf(liquidator.address);
|
|
||||||
|
|
||||||
const daiReserveDataAfter = await helpersContract.getReserveData(dai.address);
|
|
||||||
const ethReserveDataAfter = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
if (!tx.blockNumber) {
|
|
||||||
expect(false, 'Invalid block number');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const txTimestamp = new BigNumber(
|
|
||||||
(await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const stableDebtBeforeTx = calcExpectedStableDebtTokenBalance(
|
|
||||||
userReserveDataBefore.principalStableDebt,
|
|
||||||
userReserveDataBefore.stableBorrowRate,
|
|
||||||
userReserveDataBefore.stableRateLastUpdated,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const collateralAssetContractBalance = await weth.balanceOf(
|
|
||||||
flashLiquidationAdapter.address
|
|
||||||
);
|
|
||||||
const borrowAssetContractBalance = await dai.balanceOf(flashLiquidationAdapter.address);
|
|
||||||
|
|
||||||
expect(collateralAssetContractBalance).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'Contract address should not keep any balance.'
|
|
||||||
);
|
|
||||||
expect(borrowAssetContractBalance).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'Contract address should not keep any balance.'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
|
||||||
stableDebtBeforeTx.minus(amountToLiquidate).toFixed(0),
|
|
||||||
'Invalid user debt after liquidation'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the liquidity index of the principal reserve needs to be bigger than the index before
|
|
||||||
expect(daiReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte(
|
|
||||||
daiReserveDataBefore.liquidityIndex.toString(),
|
|
||||||
'Invalid liquidity index'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the principal APY after a liquidation needs to be lower than the APY before
|
|
||||||
expect(daiReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt(
|
|
||||||
daiReserveDataBefore.liquidityRate.toString(),
|
|
||||||
'Invalid liquidity APY'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(daiReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.plus(flashLoanDebt)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid principal available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(ethReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.minus(expectedCollateralLiquidated)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid collateral available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Profit after flash loan liquidation
|
|
||||||
expect(liquidatorWethBalanceAfter).to.be.equal(
|
|
||||||
liquidatorWethBalanceBefore.add(expectedProfit),
|
|
||||||
'Invalid expected WETH profit'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('executeOperation: succesfully liquidateCall with same asset via Flash Loan, but no swap needed', () => {
|
|
||||||
it('Liquidates the borrow with profit', async () => {
|
|
||||||
await depositSameAssetAndHFBelowOne();
|
|
||||||
await increaseTime(100);
|
|
||||||
|
|
||||||
const { weth, users, pool, oracle, helpersContract, flashLiquidationAdapter } = testEnv;
|
|
||||||
|
|
||||||
const liquidator = users[3];
|
|
||||||
const borrower = users[1];
|
|
||||||
|
|
||||||
const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address);
|
|
||||||
|
|
||||||
const assetPrice = await oracle.getAssetPrice(weth.address);
|
|
||||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
weth.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const assetDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(weth.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const amountToLiquidate = userReserveDataBefore.currentVariableDebt.div(2).toFixed(0);
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(assetPrice.toString())
|
|
||||||
.times(new BigNumber(amountToLiquidate).times(105))
|
|
||||||
.times(new BigNumber(10).pow(assetDecimals))
|
|
||||||
.div(new BigNumber(assetPrice.toString()).times(new BigNumber(10).pow(assetDecimals)))
|
|
||||||
.div(100)
|
|
||||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
||||||
|
|
||||||
const flashLoanDebt = new BigNumber(amountToLiquidate.toString())
|
|
||||||
.multipliedBy(1.0009)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
const params = buildFlashLiquidationAdapterParams(
|
|
||||||
weth.address,
|
|
||||||
weth.address,
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
const tx = await pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.flashLoan(
|
|
||||||
flashLiquidationAdapter.address,
|
|
||||||
[weth.address],
|
|
||||||
[amountToLiquidate],
|
|
||||||
[0],
|
|
||||||
borrower.address,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dont expect Swapped event due is same asset
|
|
||||||
await expect(Promise.resolve(tx)).to.not.emit(flashLiquidationAdapter, 'Swapped');
|
|
||||||
|
|
||||||
// Expect LiquidationCall event
|
|
||||||
await expect(Promise.resolve(tx))
|
|
||||||
.to.emit(pool, 'LiquidationCall')
|
|
||||||
.withArgs(
|
|
||||||
weth.address,
|
|
||||||
weth.address,
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate.toString(),
|
|
||||||
expectedCollateralLiquidated.toString(),
|
|
||||||
flashLiquidationAdapter.address,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
const borrowAssetContractBalance = await weth.balanceOf(flashLiquidationAdapter.address);
|
|
||||||
|
|
||||||
expect(borrowAssetContractBalance).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'Contract address should not keep any balance.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('executeOperation: succesfully liquidateCall and swap via Flash Loan without profits', () => {
|
|
||||||
it('Liquidates the borrow', async () => {
|
|
||||||
await depositAndHFBelowOne();
|
|
||||||
await increaseTime(100);
|
|
||||||
|
|
||||||
const {
|
|
||||||
dai,
|
|
||||||
weth,
|
|
||||||
users,
|
|
||||||
pool,
|
|
||||||
oracle,
|
|
||||||
helpersContract,
|
|
||||||
flashLiquidationAdapter,
|
|
||||||
} = testEnv;
|
|
||||||
|
|
||||||
const liquidator = users[3];
|
|
||||||
const borrower = users[1];
|
|
||||||
const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address);
|
|
||||||
|
|
||||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
|
||||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
const daiReserveDataBefore = await helpersContract.getReserveData(dai.address);
|
|
||||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const collateralDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(weth.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const principalDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(dai.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0);
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
|
||||||
.times(new BigNumber(amountToLiquidate).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 flashLoanDebt = new BigNumber(amountToLiquidate.toString())
|
|
||||||
.multipliedBy(1.0009)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
// Set how much ETH will be sold and swapped for DAI at Uniswap mock
|
|
||||||
await (
|
|
||||||
await mockUniswapRouter.setAmountToSwap(
|
|
||||||
weth.address,
|
|
||||||
expectedCollateralLiquidated.toString()
|
|
||||||
)
|
|
||||||
).wait();
|
|
||||||
|
|
||||||
const params = buildFlashLiquidationAdapterParams(
|
|
||||||
weth.address,
|
|
||||||
dai.address,
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
const tx = await pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.flashLoan(
|
|
||||||
flashLiquidationAdapter.address,
|
|
||||||
[dai.address],
|
|
||||||
[flashLoanDebt],
|
|
||||||
[0],
|
|
||||||
borrower.address,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Expect Swapped event
|
|
||||||
await expect(Promise.resolve(tx)).to.emit(flashLiquidationAdapter, 'Swapped');
|
|
||||||
|
|
||||||
// Expect LiquidationCall event
|
|
||||||
await expect(Promise.resolve(tx)).to.emit(pool, 'LiquidationCall');
|
|
||||||
|
|
||||||
const userReserveDataAfter = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
const liquidatorWethBalanceAfter = await weth.balanceOf(liquidator.address);
|
|
||||||
|
|
||||||
const daiReserveDataAfter = await helpersContract.getReserveData(dai.address);
|
|
||||||
const ethReserveDataAfter = await helpersContract.getReserveData(weth.address);
|
|
||||||
|
|
||||||
if (!tx.blockNumber) {
|
|
||||||
expect(false, 'Invalid block number');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const txTimestamp = new BigNumber(
|
|
||||||
(await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const stableDebtBeforeTx = calcExpectedStableDebtTokenBalance(
|
|
||||||
userReserveDataBefore.principalStableDebt,
|
|
||||||
userReserveDataBefore.stableBorrowRate,
|
|
||||||
userReserveDataBefore.stableRateLastUpdated,
|
|
||||||
txTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const collateralAssetContractBalance = await dai.balanceOf(flashLiquidationAdapter.address);
|
|
||||||
const borrowAssetContractBalance = await weth.balanceOf(flashLiquidationAdapter.address);
|
|
||||||
|
|
||||||
expect(collateralAssetContractBalance).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'Contract address should not keep any balance.'
|
|
||||||
);
|
|
||||||
expect(borrowAssetContractBalance).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'Contract address should not keep any balance.'
|
|
||||||
);
|
|
||||||
expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
|
|
||||||
stableDebtBeforeTx.minus(amountToLiquidate).toFixed(0),
|
|
||||||
'Invalid user debt after liquidation'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the liquidity index of the principal reserve needs to be bigger than the index before
|
|
||||||
expect(daiReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte(
|
|
||||||
daiReserveDataBefore.liquidityIndex.toString(),
|
|
||||||
'Invalid liquidity index'
|
|
||||||
);
|
|
||||||
|
|
||||||
//the principal APY after a liquidation needs to be lower than the APY before
|
|
||||||
expect(daiReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt(
|
|
||||||
daiReserveDataBefore.liquidityRate.toString(),
|
|
||||||
'Invalid liquidity APY'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
|
|
||||||
new BigNumber(ethReserveDataBefore.availableLiquidity.toString())
|
|
||||||
.minus(expectedCollateralLiquidated)
|
|
||||||
.toFixed(0),
|
|
||||||
'Invalid collateral available liquidity'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Net Profit == 0 after flash loan liquidation
|
|
||||||
expect(liquidatorWethBalanceAfter).to.be.equal(
|
|
||||||
liquidatorWethBalanceBefore,
|
|
||||||
'Invalid expected WETH profit'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('executeOperation: succesfully liquidateCall all available debt and swap via Flash Loan ', () => {
|
|
||||||
it('Liquidates the borrow', async () => {
|
|
||||||
await depositAndHFBelowOne();
|
|
||||||
await increaseTime(100);
|
|
||||||
|
|
||||||
const {
|
|
||||||
dai,
|
|
||||||
weth,
|
|
||||||
users,
|
|
||||||
pool,
|
|
||||||
oracle,
|
|
||||||
helpersContract,
|
|
||||||
flashLiquidationAdapter,
|
|
||||||
} = testEnv;
|
|
||||||
|
|
||||||
const liquidator = users[3];
|
|
||||||
const borrower = users[1];
|
|
||||||
const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address);
|
|
||||||
|
|
||||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
|
||||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
const daiReserveDataBefore = await helpersContract.getReserveData(dai.address);
|
|
||||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const collateralDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(weth.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const principalDecimals = (
|
|
||||||
await helpersContract.getReserveConfigurationData(dai.address)
|
|
||||||
).decimals.toString();
|
|
||||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0);
|
|
||||||
const extraAmount = new BigNumber(amountToLiquidate).times('1.15').toFixed(0);
|
|
||||||
|
|
||||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
|
||||||
.times(new BigNumber(amountToLiquidate).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 flashLoanDebt = new BigNumber(amountToLiquidate.toString())
|
|
||||||
.multipliedBy(1.0009)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
// Set how much ETH will be sold and swapped for DAI at Uniswap mock
|
|
||||||
await (
|
|
||||||
await mockUniswapRouter.setAmountToSwap(
|
|
||||||
weth.address,
|
|
||||||
expectedCollateralLiquidated.toString()
|
|
||||||
)
|
|
||||||
).wait();
|
|
||||||
|
|
||||||
const params = buildFlashLiquidationAdapterParams(
|
|
||||||
weth.address,
|
|
||||||
dai.address,
|
|
||||||
borrower.address,
|
|
||||||
MAX_UINT_AMOUNT,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
const tx = await pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.flashLoan(
|
|
||||||
flashLiquidationAdapter.address,
|
|
||||||
[dai.address],
|
|
||||||
[extraAmount],
|
|
||||||
[0],
|
|
||||||
borrower.address,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Expect Swapped event
|
|
||||||
await expect(Promise.resolve(tx)).to.emit(flashLiquidationAdapter, 'Swapped');
|
|
||||||
|
|
||||||
// Expect LiquidationCall event
|
|
||||||
await expect(Promise.resolve(tx)).to.emit(pool, 'LiquidationCall');
|
|
||||||
|
|
||||||
const collateralAssetContractBalance = await dai.balanceOf(flashLiquidationAdapter.address);
|
|
||||||
const borrowAssetContractBalance = await dai.balanceOf(flashLiquidationAdapter.address);
|
|
||||||
|
|
||||||
expect(collateralAssetContractBalance).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'Contract address should not keep any balance.'
|
|
||||||
);
|
|
||||||
expect(borrowAssetContractBalance).to.be.equal(
|
|
||||||
'0',
|
|
||||||
'Contract address should not keep any balance.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('executeOperation: invalid params', async () => {
|
|
||||||
it('Revert if debt asset is different than requested flash loan token', async () => {
|
|
||||||
await depositAndHFBelowOne();
|
|
||||||
|
|
||||||
const { dai, weth, users, pool, helpersContract, flashLiquidationAdapter } = testEnv;
|
|
||||||
|
|
||||||
const liquidator = users[3];
|
|
||||||
const borrower = users[1];
|
|
||||||
const expectedSwap = ethers.utils.parseEther('0.4');
|
|
||||||
|
|
||||||
// Set how much ETH will be sold and swapped for DAI at Uniswap mock
|
|
||||||
await (await mockUniswapRouter.setAmountToSwap(weth.address, expectedSwap)).wait();
|
|
||||||
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0);
|
|
||||||
|
|
||||||
// Wrong debt asset
|
|
||||||
const params = buildFlashLiquidationAdapterParams(
|
|
||||||
weth.address,
|
|
||||||
weth.address, // intentionally bad
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.flashLoan(
|
|
||||||
flashLiquidationAdapter.address,
|
|
||||||
[dai.address],
|
|
||||||
[amountToLiquidate],
|
|
||||||
[0],
|
|
||||||
borrower.address,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Revert if debt asset amount to liquidate is greater than requested flash loan', async () => {
|
|
||||||
await depositAndHFBelowOne();
|
|
||||||
|
|
||||||
const { dai, weth, users, pool, helpersContract, flashLiquidationAdapter } = testEnv;
|
|
||||||
|
|
||||||
const liquidator = users[3];
|
|
||||||
const borrower = users[1];
|
|
||||||
const expectedSwap = ethers.utils.parseEther('0.4');
|
|
||||||
|
|
||||||
// Set how much ETH will be sold and swapped for DAI at Uniswap mock
|
|
||||||
await (await mockUniswapRouter.setAmountToSwap(weth.address, expectedSwap)).wait();
|
|
||||||
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2);
|
|
||||||
|
|
||||||
// Correct params
|
|
||||||
const params = buildFlashLiquidationAdapterParams(
|
|
||||||
weth.address,
|
|
||||||
dai.address,
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate.toString(),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
// Bad flash loan params: requested DAI amount below amountToLiquidate
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.flashLoan(
|
|
||||||
flashLiquidationAdapter.address,
|
|
||||||
[dai.address],
|
|
||||||
[amountToLiquidate.div(2).toString()],
|
|
||||||
[0],
|
|
||||||
borrower.address,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
).to.be.revertedWith(LP_LIQUIDATION_CALL_FAILED);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Revert if requested multiple assets', async () => {
|
|
||||||
await depositAndHFBelowOne();
|
|
||||||
|
|
||||||
const { dai, weth, users, pool, helpersContract, flashLiquidationAdapter } = testEnv;
|
|
||||||
|
|
||||||
const liquidator = users[3];
|
|
||||||
const borrower = users[1];
|
|
||||||
const expectedSwap = ethers.utils.parseEther('0.4');
|
|
||||||
|
|
||||||
// Set how much ETH will be sold and swapped for DAI at Uniswap mock
|
|
||||||
await (await mockUniswapRouter.setAmountToSwap(weth.address, expectedSwap)).wait();
|
|
||||||
|
|
||||||
const userReserveDataBefore = await getUserData(
|
|
||||||
pool,
|
|
||||||
helpersContract,
|
|
||||||
dai.address,
|
|
||||||
borrower.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2);
|
|
||||||
|
|
||||||
// Correct params
|
|
||||||
const params = buildFlashLiquidationAdapterParams(
|
|
||||||
weth.address,
|
|
||||||
dai.address,
|
|
||||||
borrower.address,
|
|
||||||
amountToLiquidate.toString(),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
// Bad flash loan params: requested multiple assets
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(liquidator.signer)
|
|
||||||
.flashLoan(
|
|
||||||
flashLiquidationAdapter.address,
|
|
||||||
[dai.address, weth.address],
|
|
||||||
[10, 10],
|
|
||||||
[0],
|
|
||||||
borrower.address,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,234 +0,0 @@
|
||||||
import { expect } from 'chai';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { ProtocolErrors, eContractid } from '../helpers/types';
|
|
||||||
import { deployContract, getContract } from '../helpers/contracts-helpers';
|
|
||||||
import { MockAToken } from '../types/MockAToken';
|
|
||||||
import { MockStableDebtToken } from '../types/MockStableDebtToken';
|
|
||||||
import { MockVariableDebtToken } from '../types/MockVariableDebtToken';
|
|
||||||
import { ZERO_ADDRESS } from '../helpers/constants';
|
|
||||||
import {
|
|
||||||
getAToken,
|
|
||||||
getMockStableDebtToken,
|
|
||||||
getMockVariableDebtToken,
|
|
||||||
getStableDebtToken,
|
|
||||||
getVariableDebtToken,
|
|
||||||
} from '../helpers/contracts-getters';
|
|
||||||
import {
|
|
||||||
deployMockAToken,
|
|
||||||
deployMockStableDebtToken,
|
|
||||||
deployMockVariableDebtToken,
|
|
||||||
} from '../helpers/contracts-deployments';
|
|
||||||
|
|
||||||
makeSuite('Upgradeability', (testEnv: TestEnv) => {
|
|
||||||
const { CALLER_NOT_POOL_ADMIN } = ProtocolErrors;
|
|
||||||
let newATokenAddress: string;
|
|
||||||
let newStableTokenAddress: string;
|
|
||||||
let newVariableTokenAddress: string;
|
|
||||||
|
|
||||||
before('deploying instances', async () => {
|
|
||||||
const { dai, pool } = testEnv;
|
|
||||||
const aTokenInstance = await deployMockAToken([
|
|
||||||
pool.address,
|
|
||||||
dai.address,
|
|
||||||
ZERO_ADDRESS,
|
|
||||||
ZERO_ADDRESS,
|
|
||||||
'Aave Interest bearing DAI updated',
|
|
||||||
'aDAI',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const stableDebtTokenInstance = await deployMockStableDebtToken([
|
|
||||||
pool.address,
|
|
||||||
dai.address,
|
|
||||||
ZERO_ADDRESS,
|
|
||||||
'Aave stable debt bearing DAI updated',
|
|
||||||
'stableDebtDAI',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const variableDebtTokenInstance = await deployMockVariableDebtToken([
|
|
||||||
pool.address,
|
|
||||||
dai.address,
|
|
||||||
ZERO_ADDRESS,
|
|
||||||
'Aave variable debt bearing DAI updated',
|
|
||||||
'variableDebtDAI',
|
|
||||||
]);
|
|
||||||
|
|
||||||
newATokenAddress = aTokenInstance.address;
|
|
||||||
newVariableTokenAddress = variableDebtTokenInstance.address;
|
|
||||||
newStableTokenAddress = stableDebtTokenInstance.address;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to update the DAI Atoken implementation with a different address than the lendingPoolManager', async () => {
|
|
||||||
const { dai, configurator, users } = testEnv;
|
|
||||||
|
|
||||||
const name = await (await getAToken(newATokenAddress)).name();
|
|
||||||
const symbol = await (await getAToken(newATokenAddress)).symbol();
|
|
||||||
|
|
||||||
const updateATokenInputParams: {
|
|
||||||
asset: string;
|
|
||||||
treasury: string;
|
|
||||||
incentivesController: string;
|
|
||||||
name: string;
|
|
||||||
symbol: string;
|
|
||||||
implementation: string;
|
|
||||||
} = {
|
|
||||||
asset: dai.address,
|
|
||||||
treasury: ZERO_ADDRESS,
|
|
||||||
incentivesController: ZERO_ADDRESS,
|
|
||||||
name: name,
|
|
||||||
symbol: symbol,
|
|
||||||
implementation: newATokenAddress,
|
|
||||||
};
|
|
||||||
await expect(
|
|
||||||
configurator.connect(users[1].signer).updateAToken(updateATokenInputParams)
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Upgrades the DAI Atoken implementation ', async () => {
|
|
||||||
const { dai, configurator, aDai } = testEnv;
|
|
||||||
|
|
||||||
const name = await (await getAToken(newATokenAddress)).name();
|
|
||||||
const symbol = await (await getAToken(newATokenAddress)).symbol();
|
|
||||||
|
|
||||||
const updateATokenInputParams: {
|
|
||||||
asset: string;
|
|
||||||
treasury: string;
|
|
||||||
incentivesController: string;
|
|
||||||
name: string;
|
|
||||||
symbol: string;
|
|
||||||
implementation: string;
|
|
||||||
} = {
|
|
||||||
asset: dai.address,
|
|
||||||
treasury: ZERO_ADDRESS,
|
|
||||||
incentivesController: ZERO_ADDRESS,
|
|
||||||
name: name,
|
|
||||||
symbol: symbol,
|
|
||||||
implementation: newATokenAddress,
|
|
||||||
};
|
|
||||||
await configurator.updateAToken(updateATokenInputParams);
|
|
||||||
|
|
||||||
const tokenName = await aDai.name();
|
|
||||||
|
|
||||||
expect(tokenName).to.be.eq('Aave Interest bearing DAI updated', 'Invalid token name');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to update the DAI Stable debt token implementation with a different address than the lendingPoolManager', async () => {
|
|
||||||
const { dai, configurator, users } = testEnv;
|
|
||||||
|
|
||||||
const name = await (await getStableDebtToken(newStableTokenAddress)).name();
|
|
||||||
const symbol = await (await getStableDebtToken(newStableTokenAddress)).symbol();
|
|
||||||
|
|
||||||
|
|
||||||
const updateDebtTokenInput: {
|
|
||||||
asset: string;
|
|
||||||
incentivesController: string;
|
|
||||||
name: string;
|
|
||||||
symbol: string;
|
|
||||||
implementation: string;
|
|
||||||
} = {
|
|
||||||
asset: dai.address,
|
|
||||||
incentivesController: ZERO_ADDRESS,
|
|
||||||
name: name,
|
|
||||||
symbol: symbol,
|
|
||||||
implementation: newStableTokenAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
configurator
|
|
||||||
.connect(users[1].signer)
|
|
||||||
.updateStableDebtToken(updateDebtTokenInput)
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Upgrades the DAI stable debt token implementation ', async () => {
|
|
||||||
const { dai, configurator, pool, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const name = await (await getStableDebtToken(newStableTokenAddress)).name();
|
|
||||||
const symbol = await (await getStableDebtToken(newStableTokenAddress)).symbol();
|
|
||||||
|
|
||||||
|
|
||||||
const updateDebtTokenInput: {
|
|
||||||
asset: string;
|
|
||||||
incentivesController: string;
|
|
||||||
name: string;
|
|
||||||
symbol: string;
|
|
||||||
implementation: string;
|
|
||||||
} = {
|
|
||||||
asset: dai.address,
|
|
||||||
incentivesController: ZERO_ADDRESS,
|
|
||||||
name: name,
|
|
||||||
symbol: symbol,
|
|
||||||
implementation: newStableTokenAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
await configurator.updateStableDebtToken(updateDebtTokenInput);
|
|
||||||
|
|
||||||
const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(dai.address);
|
|
||||||
|
|
||||||
const debtToken = await getMockStableDebtToken(stableDebtTokenAddress);
|
|
||||||
|
|
||||||
const tokenName = await debtToken.name();
|
|
||||||
|
|
||||||
expect(tokenName).to.be.eq('Aave stable debt bearing DAI updated', 'Invalid token name');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to update the DAI variable debt token implementation with a different address than the lendingPoolManager', async () => {
|
|
||||||
const {dai, configurator, users} = testEnv;
|
|
||||||
|
|
||||||
const name = await (await getVariableDebtToken(newVariableTokenAddress)).name();
|
|
||||||
const symbol = await (await getVariableDebtToken(newVariableTokenAddress)).symbol();
|
|
||||||
|
|
||||||
const updateDebtTokenInput: {
|
|
||||||
asset: string;
|
|
||||||
incentivesController: string;
|
|
||||||
name: string;
|
|
||||||
symbol: string;
|
|
||||||
implementation: string;
|
|
||||||
} = {
|
|
||||||
asset: dai.address,
|
|
||||||
incentivesController: ZERO_ADDRESS,
|
|
||||||
name: name,
|
|
||||||
symbol: symbol,
|
|
||||||
implementation: newVariableTokenAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
configurator
|
|
||||||
.connect(users[1].signer)
|
|
||||||
.updateVariableDebtToken(updateDebtTokenInput)
|
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Upgrades the DAI variable debt token implementation ', async () => {
|
|
||||||
const {dai, configurator, pool, helpersContract} = testEnv;
|
|
||||||
|
|
||||||
const name = await (await getVariableDebtToken(newVariableTokenAddress)).name();
|
|
||||||
const symbol = await (await getVariableDebtToken(newVariableTokenAddress)).symbol();
|
|
||||||
|
|
||||||
const updateDebtTokenInput: {
|
|
||||||
asset: string;
|
|
||||||
incentivesController: string;
|
|
||||||
name: string;
|
|
||||||
symbol: string;
|
|
||||||
implementation: string;
|
|
||||||
} = {
|
|
||||||
asset: dai.address,
|
|
||||||
incentivesController: ZERO_ADDRESS,
|
|
||||||
name: name,
|
|
||||||
symbol: symbol,
|
|
||||||
implementation: newVariableTokenAddress,
|
|
||||||
}
|
|
||||||
//const name = await (await getAToken(newATokenAddress)).name();
|
|
||||||
|
|
||||||
await configurator.updateVariableDebtToken(updateDebtTokenInput);
|
|
||||||
|
|
||||||
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
dai.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtToken = await getMockVariableDebtToken(variableDebtTokenAddress);
|
|
||||||
|
|
||||||
const tokenName = await debtToken.name();
|
|
||||||
|
|
||||||
expect(tokenName).to.be.eq('Aave variable debt bearing DAI updated', 'Invalid token name');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,36 +0,0 @@
|
||||||
import { expect } from 'chai';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { ProtocolErrors, TokenContractId, eContractid } from '../helpers/types';
|
|
||||||
import { getVariableDebtToken } from '../helpers/contracts-getters';
|
|
||||||
|
|
||||||
makeSuite('Variable debt token tests', (testEnv: TestEnv) => {
|
|
||||||
const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors;
|
|
||||||
|
|
||||||
it('Tries to invoke mint not being the LendingPool', async () => {
|
|
||||||
const { deployer, pool, dai, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const daiVariableDebtTokenAddress = (
|
|
||||||
await helpersContract.getReserveTokensAddresses(dai.address)
|
|
||||||
).variableDebtTokenAddress;
|
|
||||||
|
|
||||||
const variableDebtContract = await getVariableDebtToken(daiVariableDebtTokenAddress);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
variableDebtContract.mint(deployer.address, deployer.address, '1', '1')
|
|
||||||
).to.be.revertedWith(CT_CALLER_MUST_BE_LENDING_POOL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Tries to invoke burn not being the LendingPool', async () => {
|
|
||||||
const { deployer, pool, dai, helpersContract } = testEnv;
|
|
||||||
|
|
||||||
const daiVariableDebtTokenAddress = (
|
|
||||||
await helpersContract.getReserveTokensAddresses(dai.address)
|
|
||||||
).variableDebtTokenAddress;
|
|
||||||
|
|
||||||
const variableDebtContract = await getVariableDebtToken(daiVariableDebtTokenAddress);
|
|
||||||
|
|
||||||
await expect(variableDebtContract.burn(deployer.address, '1', '1')).to.be.revertedWith(
|
|
||||||
CT_CALLER_MUST_BE_LENDING_POOL
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,370 +0,0 @@
|
||||||
import { MAX_UINT_AMOUNT } from '../helpers/constants';
|
|
||||||
import { convertToCurrencyDecimals } from '../helpers/contracts-helpers';
|
|
||||||
import { makeSuite, TestEnv } from './helpers/make-suite';
|
|
||||||
import { parseEther } from 'ethers/lib/utils';
|
|
||||||
import { DRE, waitForTx } from '../helpers/misc-utils';
|
|
||||||
import { BigNumber } from 'ethers';
|
|
||||||
import { getStableDebtToken, getVariableDebtToken } from '../helpers/contracts-getters';
|
|
||||||
import { deploySelfdestructTransferMock } from '../helpers/contracts-deployments';
|
|
||||||
|
|
||||||
const { expect } = require('chai');
|
|
||||||
|
|
||||||
makeSuite('Use native ETH at LendingPool via WETHGateway', (testEnv: TestEnv) => {
|
|
||||||
const zero = BigNumber.from('0');
|
|
||||||
const depositSize = parseEther('5');
|
|
||||||
const daiSize = parseEther('10000');
|
|
||||||
it('Deposit WETH via WethGateway and DAI', async () => {
|
|
||||||
const { users, wethGateway, aWETH } = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
const depositor = users[0];
|
|
||||||
|
|
||||||
// Deposit liquidity with native ETH
|
|
||||||
await wethGateway
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.depositETH(depositor.address, '0', { value: depositSize });
|
|
||||||
|
|
||||||
// Deposit with native ETH
|
|
||||||
await wethGateway.connect(user.signer).depositETH(user.address, '0', { value: depositSize });
|
|
||||||
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero);
|
|
||||||
expect(aTokensBalance).to.be.gte(depositSize);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Withdraw WETH - Partial', async () => {
|
|
||||||
const { users, wethGateway, aWETH, pool } = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
const priorEthersBalance = await user.signer.getBalance();
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero, 'User should have aTokens.');
|
|
||||||
|
|
||||||
// Partially withdraw native ETH
|
|
||||||
const partialWithdraw = await convertToCurrencyDecimals(aWETH.address, '2');
|
|
||||||
|
|
||||||
// Approve the aTokens to Gateway so Gateway can withdraw and convert to Ether
|
|
||||||
const approveTx = await aWETH
|
|
||||||
.connect(user.signer)
|
|
||||||
.approve(wethGateway.address, MAX_UINT_AMOUNT);
|
|
||||||
const { gasUsed: approveGas } = await waitForTx(approveTx);
|
|
||||||
|
|
||||||
// Partial Withdraw and send native Ether to user
|
|
||||||
const { gasUsed: withdrawGas } = await waitForTx(
|
|
||||||
await wethGateway.connect(user.signer).withdrawETH(partialWithdraw, user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const afterPartialEtherBalance = await user.signer.getBalance();
|
|
||||||
const afterPartialATokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
const gasCosts = approveGas.add(withdrawGas).mul(approveTx.gasPrice);
|
|
||||||
|
|
||||||
expect(afterPartialEtherBalance).to.be.equal(
|
|
||||||
priorEthersBalance.add(partialWithdraw).sub(gasCosts),
|
|
||||||
'User ETHER balance should contain the partial withdraw'
|
|
||||||
);
|
|
||||||
expect(afterPartialATokensBalance).to.be.equal(
|
|
||||||
aTokensBalance.sub(partialWithdraw),
|
|
||||||
'User aWETH balance should be substracted'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Withdraw WETH - Full', async () => {
|
|
||||||
const { users, aWETH, wethGateway, pool } = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
const priorEthersBalance = await user.signer.getBalance();
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero, 'User should have aTokens.');
|
|
||||||
|
|
||||||
// Approve the aTokens to Gateway so Gateway can withdraw and convert to Ether
|
|
||||||
const approveTx = await aWETH
|
|
||||||
.connect(user.signer)
|
|
||||||
.approve(wethGateway.address, MAX_UINT_AMOUNT);
|
|
||||||
const { gasUsed: approveGas } = await waitForTx(approveTx);
|
|
||||||
|
|
||||||
// Full withdraw
|
|
||||||
const { gasUsed: withdrawGas } = await waitForTx(
|
|
||||||
await wethGateway.connect(user.signer).withdrawETH(MAX_UINT_AMOUNT, user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const afterFullEtherBalance = await user.signer.getBalance();
|
|
||||||
const afterFullATokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
const gasCosts = approveGas.add(withdrawGas).mul(approveTx.gasPrice);
|
|
||||||
|
|
||||||
expect(afterFullEtherBalance).to.be.eq(
|
|
||||||
priorEthersBalance.add(aTokensBalance).sub(gasCosts),
|
|
||||||
'User ETHER balance should contain the full withdraw'
|
|
||||||
);
|
|
||||||
expect(afterFullATokensBalance).to.be.eq(0, 'User aWETH balance should be zero');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Borrow stable WETH and Full Repay with ETH', async () => {
|
|
||||||
const { users, wethGateway, aDai, weth, dai, pool, helpersContract } = testEnv;
|
|
||||||
const borrowSize = parseEther('1');
|
|
||||||
const repaySize = borrowSize.add(borrowSize.mul(5).div(100));
|
|
||||||
const user = users[1];
|
|
||||||
const depositor = users[0];
|
|
||||||
|
|
||||||
// Deposit with native ETH
|
|
||||||
await wethGateway
|
|
||||||
.connect(depositor.signer)
|
|
||||||
.depositETH(depositor.address, '0', { value: depositSize });
|
|
||||||
|
|
||||||
const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const stableDebtToken = await getStableDebtToken(stableDebtTokenAddress);
|
|
||||||
|
|
||||||
// Deposit 10000 DAI
|
|
||||||
await dai.connect(user.signer).mint(daiSize);
|
|
||||||
await dai.connect(user.signer).approve(pool.address, daiSize);
|
|
||||||
await pool.connect(user.signer).deposit(dai.address, daiSize, user.address, '0');
|
|
||||||
|
|
||||||
const aTokensBalance = await aDai.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero);
|
|
||||||
expect(aTokensBalance).to.be.gte(daiSize);
|
|
||||||
|
|
||||||
// Borrow WETH with WETH as collateral
|
|
||||||
await waitForTx(
|
|
||||||
await pool.connect(user.signer).borrow(weth.address, borrowSize, '1', '0', user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtBalance = await stableDebtToken.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(debtBalance).to.be.gt(zero);
|
|
||||||
|
|
||||||
// Full Repay WETH with native ETH
|
|
||||||
await waitForTx(
|
|
||||||
await wethGateway
|
|
||||||
.connect(user.signer)
|
|
||||||
.repayETH(MAX_UINT_AMOUNT, '1', user.address, { value: repaySize })
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtBalanceAfterRepay = await stableDebtToken.balanceOf(user.address);
|
|
||||||
expect(debtBalanceAfterRepay).to.be.eq(zero);
|
|
||||||
|
|
||||||
// Withdraw DAI
|
|
||||||
await aDai.connect(user.signer).approve(pool.address, MAX_UINT_AMOUNT);
|
|
||||||
await pool.connect(user.signer).withdraw(dai.address, MAX_UINT_AMOUNT, user.address);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Borrow variable WETH and Full Repay with ETH', async () => {
|
|
||||||
const { users, wethGateway, aWETH, weth, pool, helpersContract } = testEnv;
|
|
||||||
const borrowSize = parseEther('1');
|
|
||||||
const repaySize = borrowSize.add(borrowSize.mul(5).div(100));
|
|
||||||
const user = users[1];
|
|
||||||
|
|
||||||
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
|
|
||||||
const varDebtToken = await getVariableDebtToken(variableDebtTokenAddress);
|
|
||||||
|
|
||||||
// Deposit with native ETH
|
|
||||||
await wethGateway.connect(user.signer).depositETH(user.address, '0', { value: depositSize });
|
|
||||||
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero);
|
|
||||||
expect(aTokensBalance).to.be.gte(depositSize);
|
|
||||||
|
|
||||||
// Borrow WETH with WETH as collateral
|
|
||||||
await waitForTx(
|
|
||||||
await pool.connect(user.signer).borrow(weth.address, borrowSize, '2', '0', user.address)
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtBalance = await varDebtToken.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(debtBalance).to.be.gt(zero);
|
|
||||||
|
|
||||||
// Partial Repay WETH loan with native ETH
|
|
||||||
const partialPayment = repaySize.div(2);
|
|
||||||
await waitForTx(
|
|
||||||
await wethGateway
|
|
||||||
.connect(user.signer)
|
|
||||||
.repayETH(partialPayment, '2', user.address, { value: partialPayment })
|
|
||||||
);
|
|
||||||
|
|
||||||
const debtBalanceAfterPartialRepay = await varDebtToken.balanceOf(user.address);
|
|
||||||
expect(debtBalanceAfterPartialRepay).to.be.lt(debtBalance);
|
|
||||||
|
|
||||||
// Full Repay WETH loan with native ETH
|
|
||||||
await waitForTx(
|
|
||||||
await wethGateway
|
|
||||||
.connect(user.signer)
|
|
||||||
.repayETH(MAX_UINT_AMOUNT, '2', user.address, { value: repaySize })
|
|
||||||
);
|
|
||||||
const debtBalanceAfterFullRepay = await varDebtToken.balanceOf(user.address);
|
|
||||||
expect(debtBalanceAfterFullRepay).to.be.eq(zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Borrow ETH via delegateApprove ETH and repays back', async () => {
|
|
||||||
const { users, wethGateway, aWETH, weth, helpersContract } = testEnv;
|
|
||||||
const borrowSize = parseEther('1');
|
|
||||||
const user = users[2];
|
|
||||||
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
|
||||||
weth.address
|
|
||||||
);
|
|
||||||
const varDebtToken = await getVariableDebtToken(variableDebtTokenAddress);
|
|
||||||
|
|
||||||
const priorDebtBalance = await varDebtToken.balanceOf(user.address);
|
|
||||||
expect(priorDebtBalance).to.be.eq(zero);
|
|
||||||
|
|
||||||
// Deposit WETH with native ETH
|
|
||||||
await wethGateway.connect(user.signer).depositETH(user.address, '0', { value: depositSize });
|
|
||||||
|
|
||||||
const aTokensBalance = await aWETH.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(aTokensBalance).to.be.gt(zero);
|
|
||||||
expect(aTokensBalance).to.be.gte(depositSize);
|
|
||||||
|
|
||||||
// Delegates borrowing power of WETH to WETHGateway
|
|
||||||
await waitForTx(
|
|
||||||
await varDebtToken.connect(user.signer).approveDelegation(wethGateway.address, borrowSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Borrows ETH with WETH as collateral
|
|
||||||
await waitForTx(await wethGateway.connect(user.signer).borrowETH(borrowSize, '2', '0'));
|
|
||||||
|
|
||||||
const debtBalance = await varDebtToken.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(debtBalance).to.be.gt(zero);
|
|
||||||
|
|
||||||
// Full Repay WETH loan with native ETH
|
|
||||||
await waitForTx(
|
|
||||||
await wethGateway
|
|
||||||
.connect(user.signer)
|
|
||||||
.repayETH(MAX_UINT_AMOUNT, '2', user.address, { value: borrowSize.mul(2) })
|
|
||||||
);
|
|
||||||
const debtBalanceAfterFullRepay = await varDebtToken.balanceOf(user.address);
|
|
||||||
expect(debtBalanceAfterFullRepay).to.be.eq(zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should revert if receiver function receives Ether if not WETH', async () => {
|
|
||||||
const { users, wethGateway } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
const amount = parseEther('1');
|
|
||||||
|
|
||||||
// Call receiver function (empty data + value)
|
|
||||||
await expect(
|
|
||||||
user.signer.sendTransaction({
|
|
||||||
to: wethGateway.address,
|
|
||||||
value: amount,
|
|
||||||
gasLimit: DRE.network.config.gas,
|
|
||||||
})
|
|
||||||
).to.be.revertedWith('Receive not allowed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should revert if fallback functions is called with Ether', async () => {
|
|
||||||
const { users, wethGateway } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
const amount = parseEther('1');
|
|
||||||
const fakeABI = ['function wantToCallFallback()'];
|
|
||||||
const abiCoder = new DRE.ethers.utils.Interface(fakeABI);
|
|
||||||
const fakeMethodEncoded = abiCoder.encodeFunctionData('wantToCallFallback', []);
|
|
||||||
|
|
||||||
// Call fallback function with value
|
|
||||||
await expect(
|
|
||||||
user.signer.sendTransaction({
|
|
||||||
to: wethGateway.address,
|
|
||||||
data: fakeMethodEncoded,
|
|
||||||
value: amount,
|
|
||||||
gasLimit: DRE.network.config.gas,
|
|
||||||
})
|
|
||||||
).to.be.revertedWith('Fallback not allowed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should revert if fallback functions is called', async () => {
|
|
||||||
const { users, wethGateway } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
|
|
||||||
const fakeABI = ['function wantToCallFallback()'];
|
|
||||||
const abiCoder = new DRE.ethers.utils.Interface(fakeABI);
|
|
||||||
const fakeMethodEncoded = abiCoder.encodeFunctionData('wantToCallFallback', []);
|
|
||||||
|
|
||||||
// Call fallback function without value
|
|
||||||
await expect(
|
|
||||||
user.signer.sendTransaction({
|
|
||||||
to: wethGateway.address,
|
|
||||||
data: fakeMethodEncoded,
|
|
||||||
gasLimit: DRE.network.config.gas,
|
|
||||||
})
|
|
||||||
).to.be.revertedWith('Fallback not allowed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Getters should retrieve correct state', async () => {
|
|
||||||
const { aWETH, weth, pool, wethGateway } = testEnv;
|
|
||||||
|
|
||||||
const WETHAddress = await wethGateway.getWETHAddress();
|
|
||||||
const aWETHAddress = await wethGateway.getAWETHAddress();
|
|
||||||
const poolAddress = await wethGateway.getLendingPoolAddress();
|
|
||||||
|
|
||||||
expect(WETHAddress).to.be.equal(weth.address);
|
|
||||||
expect(aWETHAddress).to.be.equal(aWETH.address);
|
|
||||||
expect(poolAddress).to.be.equal(pool.address);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Owner can do emergency token recovery', async () => {
|
|
||||||
const { users, dai, wethGateway, deployer } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
const amount = parseEther('1');
|
|
||||||
|
|
||||||
await dai.connect(user.signer).mint(amount);
|
|
||||||
const daiBalanceAfterMint = await dai.balanceOf(user.address);
|
|
||||||
|
|
||||||
await dai.connect(user.signer).transfer(wethGateway.address, amount);
|
|
||||||
const daiBalanceAfterBadTransfer = await dai.balanceOf(user.address);
|
|
||||||
expect(daiBalanceAfterBadTransfer).to.be.eq(
|
|
||||||
daiBalanceAfterMint.sub(amount),
|
|
||||||
'User should have lost the funds here.'
|
|
||||||
);
|
|
||||||
|
|
||||||
await wethGateway
|
|
||||||
.connect(deployer.signer)
|
|
||||||
.emergencyTokenTransfer(dai.address, user.address, amount);
|
|
||||||
const daiBalanceAfterRecovery = await dai.balanceOf(user.address);
|
|
||||||
|
|
||||||
expect(daiBalanceAfterRecovery).to.be.eq(
|
|
||||||
daiBalanceAfterMint,
|
|
||||||
'User should recover the funds due emergency token transfer'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Owner can do emergency native ETH recovery', async () => {
|
|
||||||
const { users, wethGateway, deployer } = testEnv;
|
|
||||||
const user = users[0];
|
|
||||||
const amount = parseEther('1');
|
|
||||||
const userBalancePriorCall = await user.signer.getBalance();
|
|
||||||
|
|
||||||
// Deploy contract with payable selfdestruct contract
|
|
||||||
const selfdestructContract = await deploySelfdestructTransferMock();
|
|
||||||
|
|
||||||
// Selfdestruct the mock, pointing to WETHGateway address
|
|
||||||
const callTx = await selfdestructContract
|
|
||||||
.connect(user.signer)
|
|
||||||
.destroyAndTransfer(wethGateway.address, { value: amount });
|
|
||||||
const { gasUsed } = await waitForTx(callTx);
|
|
||||||
const gasFees = gasUsed.mul(callTx.gasPrice);
|
|
||||||
const userBalanceAfterCall = await user.signer.getBalance();
|
|
||||||
|
|
||||||
expect(userBalanceAfterCall).to.be.eq(userBalancePriorCall.sub(amount).sub(gasFees), '');
|
|
||||||
('User should have lost the funds');
|
|
||||||
|
|
||||||
// Recover the funds from the contract and sends back to the user
|
|
||||||
await wethGateway.connect(deployer.signer).emergencyEtherTransfer(user.address, amount);
|
|
||||||
|
|
||||||
const userBalanceAfterRecovery = await user.signer.getBalance();
|
|
||||||
const wethGatewayAfterRecovery = await DRE.ethers.provider.getBalance(wethGateway.address);
|
|
||||||
|
|
||||||
expect(userBalanceAfterRecovery).to.be.eq(
|
|
||||||
userBalancePriorCall.sub(gasFees),
|
|
||||||
'User should recover the funds due emergency eth transfer.'
|
|
||||||
);
|
|
||||||
expect(wethGatewayAfterRecovery).to.be.eq('0', 'WETHGateway ether balance should be zero.');
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user