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