aave-protocol-v2/test/mainnet/check-list.spec.ts

363 lines
13 KiB
TypeScript
Raw Normal View History

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