Removed deprecated /test/ test directory

This commit is contained in:
Zer0dot 2021-02-10 18:52:06 -05:00
parent 0a3da08c9c
commit 4080bb5983
44 changed files with 0 additions and 15362 deletions

View File

@ -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');
});

View File

@ -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');
});
});

View File

@ -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
);
});
});

View File

@ -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');
});
});

View File

@ -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());
});
});

View File

@ -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);
});
});

View File

@ -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);
});
});

View File

@ -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'
);
});
});

View File

@ -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),
};
};

View File

@ -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);
}
};
});
};
}

View File

@ -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();
});
});
}

View File

@ -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}`;
}
};

View File

@ -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"
}
]
}
]
}

View File

@ -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"
}
]
}
]
}

View File

@ -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"
}
]
}
]
}

View File

@ -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"
}
]
}
]
}

View File

@ -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"
}
]
}
]
}

View File

@ -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"
}
]
}
]
}

View File

@ -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"
}
]
}
]
}

View File

@ -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"
}
]
}
]
}

View File

@ -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."
}
]
}
]
}

View File

@ -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

View File

@ -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();
};

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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);
});
});

View File

@ -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'
);
});
});

View File

@ -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'
);
});
});

View File

@ -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.');
});
});

View File

@ -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);
});
});

View File

@ -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'
// );
// });
// });

View File

@ -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",
// )
// })
// })

View File

@ -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);
});
}
});
});

View File

@ -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,
// })
// })
// })

View File

@ -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
);
});
});

View File

@ -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);
});
});

View File

@ -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);
});
});
});
});

View File

@ -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

View File

@ -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');
});
});

View File

@ -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
);
});
});

View File

@ -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.');
});
});