mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Added mainnet check list. Remove deployed contracts. Update README.md
This commit is contained in:
parent
0d40682054
commit
27365697ac
25
README.md
25
README.md
|
@ -83,6 +83,8 @@ npm run aave:kovan:full:migration
|
|||
|
||||
### Mainnet fork deployment
|
||||
|
||||
You can deploy Aave Protocol v2 in a forked Mainnet chain using Hardhat built-in feature:
|
||||
|
||||
```
|
||||
# In one terminal, run a hardhat note with mainnet fork enabled
|
||||
MAINNET_FORK=true npx hardhat node
|
||||
|
@ -95,4 +97,27 @@ docker-compose exec contracts-env bash
|
|||
|
||||
# A new Bash terminal is prompted, connected to the container
|
||||
npm run aave:fork:main
|
||||
|
||||
# Contracts are now deployed at Hardhat node with Mainnet fork.
|
||||
|
||||
# You can interact with them via Hardhat console
|
||||
MAINNET_FORK=true npx hardhat console
|
||||
# Or your custom Hardhat task
|
||||
MAINNET_FORK=true npx hardhat your-custom-task
|
||||
|
||||
```
|
||||
|
||||
### Mainnet fork - Run the check list
|
||||
|
||||
For testing the deployment scripts for Mainnet release, you can run the check-list tests in a Mainnet fork using Hardhat built-in feature:
|
||||
|
||||
```
|
||||
# In another terminal, run docker-compose
|
||||
docker-compose up
|
||||
|
||||
# Open another tab or terminal
|
||||
docker-compose exec contracts-env bash
|
||||
|
||||
# A new Bash terminal is prompted, connected to the container
|
||||
npm run test:main:check-list
|
||||
```
|
||||
|
|
|
@ -10,11 +10,15 @@ import {ReserveConfiguration} from '../libraries/configuration/ReserveConfigurat
|
|||
import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
|
||||
import {IStableDebtToken} from '../tokenization/interfaces/IStableDebtToken.sol';
|
||||
import {IVariableDebtToken} from '../tokenization/interfaces/IVariableDebtToken.sol';
|
||||
import 'hardhat/console.sol';
|
||||
|
||||
contract AaveProtocolDataProvider {
|
||||
using ReserveConfiguration for ReserveConfiguration.Map;
|
||||
using UserConfiguration for UserConfiguration.Map;
|
||||
|
||||
address constant MKR = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2;
|
||||
address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
|
||||
struct TokenData {
|
||||
string symbol;
|
||||
address tokenAddress;
|
||||
|
@ -31,10 +35,16 @@ contract AaveProtocolDataProvider {
|
|||
address[] memory reserves = pool.getReservesList();
|
||||
TokenData[] memory reservesTokens = new TokenData[](reserves.length);
|
||||
for (uint256 i = 0; i < reserves.length; i++) {
|
||||
if (reserves[i] == MKR) {
|
||||
reservesTokens[i] = TokenData({symbol: 'MKR', tokenAddress: reserves[i]});
|
||||
continue;
|
||||
}
|
||||
if (reserves[i] == ETH) {
|
||||
reservesTokens[i] = TokenData({symbol: 'ETH', tokenAddress: reserves[i]});
|
||||
continue;
|
||||
}
|
||||
reservesTokens[i] = TokenData({
|
||||
symbol: (reserves[i] == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
? 'ETH'
|
||||
: IERC20Detailed(reserves[i]).symbol(),
|
||||
symbol: IERC20Detailed(reserves[i]).symbol(),
|
||||
tokenAddress: reserves[i]
|
||||
});
|
||||
}
|
||||
|
|
160
contracts/misc/interfaces/IUniswapV2Router01.sol
Normal file
160
contracts/misc/interfaces/IUniswapV2Router01.sol
Normal file
|
@ -0,0 +1,160 @@
|
|||
pragma solidity >=0.6.2;
|
||||
|
||||
interface IUniswapV2Router01 {
|
||||
function factory() external pure returns (address);
|
||||
|
||||
function WETH() external pure returns (address);
|
||||
|
||||
function addLiquidity(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 amountADesired,
|
||||
uint256 amountBDesired,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
)
|
||||
external
|
||||
returns (
|
||||
uint256 amountA,
|
||||
uint256 amountB,
|
||||
uint256 liquidity
|
||||
);
|
||||
|
||||
function addLiquidityETH(
|
||||
address token,
|
||||
uint256 amountTokenDesired,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (
|
||||
uint256 amountToken,
|
||||
uint256 amountETH,
|
||||
uint256 liquidity
|
||||
);
|
||||
|
||||
function removeLiquidity(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 liquidity,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256 amountA, uint256 amountB);
|
||||
|
||||
function removeLiquidityETH(
|
||||
address token,
|
||||
uint256 liquidity,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256 amountToken, uint256 amountETH);
|
||||
|
||||
function removeLiquidityWithPermit(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 liquidity,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin,
|
||||
address to,
|
||||
uint256 deadline,
|
||||
bool approveMax,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external returns (uint256 amountA, uint256 amountB);
|
||||
|
||||
function removeLiquidityETHWithPermit(
|
||||
address token,
|
||||
uint256 liquidity,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline,
|
||||
bool approveMax,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external returns (uint256 amountToken, uint256 amountETH);
|
||||
|
||||
function swapExactTokensForTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapTokensForExactTokens(
|
||||
uint256 amountOut,
|
||||
uint256 amountInMax,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapExactETHForTokens(
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external payable returns (uint256[] memory amounts);
|
||||
|
||||
function swapTokensForExactETH(
|
||||
uint256 amountOut,
|
||||
uint256 amountInMax,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapExactTokensForETH(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapETHForExactTokens(
|
||||
uint256 amountOut,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external payable returns (uint256[] memory amounts);
|
||||
|
||||
function quote(
|
||||
uint256 amountA,
|
||||
uint256 reserveA,
|
||||
uint256 reserveB
|
||||
) external pure returns (uint256 amountB);
|
||||
|
||||
function getAmountOut(
|
||||
uint256 amountIn,
|
||||
uint256 reserveIn,
|
||||
uint256 reserveOut
|
||||
) external pure returns (uint256 amountOut);
|
||||
|
||||
function getAmountIn(
|
||||
uint256 amountOut,
|
||||
uint256 reserveIn,
|
||||
uint256 reserveOut
|
||||
) external pure returns (uint256 amountIn);
|
||||
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
|
||||
function getAmountsIn(uint256 amountOut, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
}
|
50
contracts/misc/interfaces/IUniswapV2Router02.sol
Normal file
50
contracts/misc/interfaces/IUniswapV2Router02.sol
Normal file
|
@ -0,0 +1,50 @@
|
|||
pragma solidity >=0.6.2;
|
||||
|
||||
import './IUniswapV2Router01.sol';
|
||||
|
||||
interface IUniswapV2Router02 is IUniswapV2Router01 {
|
||||
function removeLiquidityETHSupportingFeeOnTransferTokens(
|
||||
address token,
|
||||
uint256 liquidity,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256 amountETH);
|
||||
|
||||
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
|
||||
address token,
|
||||
uint256 liquidity,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline,
|
||||
bool approveMax,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external returns (uint256 amountETH);
|
||||
|
||||
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external;
|
||||
|
||||
function swapExactETHForTokensSupportingFeeOnTransferTokens(
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external payable;
|
||||
|
||||
function swapExactTokensForETHSupportingFeeOnTransferTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -109,7 +109,7 @@ export const getLendingRateOracles = (poolConfig: ICommonConfiguration) => {
|
|||
ReserveAssets,
|
||||
} = poolConfig;
|
||||
|
||||
const MAINNET_FORK = process.env.MAINNET_FORK;
|
||||
const MAINNET_FORK = process.env.MAINNET_FORK === 'true';
|
||||
const network = MAINNET_FORK ? 'main' : DRE.network.name;
|
||||
return filterMapBy(LendingRateOracleRatesCommon, (key) =>
|
||||
Object.keys(ReserveAssets[network]).includes(key)
|
||||
|
|
|
@ -22,7 +22,7 @@ export type MockTokenMap = {[symbol: string]: MintableERC20};
|
|||
|
||||
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
|
||||
const currentNetwork = DRE.network.name;
|
||||
const MAINNET_FORK = process.env.MAINNET_FORK;
|
||||
const MAINNET_FORK = process.env.MAINNET_FORK === 'true';
|
||||
if (MAINNET_FORK || (currentNetwork !== 'hardhat' && !currentNetwork.includes('coverage'))) {
|
||||
console.log(`*** ${contractId} ***\n`);
|
||||
console.log(`Network: ${currentNetwork}`);
|
||||
|
@ -134,7 +134,7 @@ export const getParamPerNetwork = <T>(
|
|||
{kovan, ropsten, main, buidlerevm, coverage, tenderlyMain}: iParamsPerNetwork<T>,
|
||||
network: eEthereumNetwork
|
||||
) => {
|
||||
const MAINNET_FORK = process.env.MAINNET_FORK;
|
||||
const MAINNET_FORK = process.env.MAINNET_FORK === 'true';
|
||||
if (MAINNET_FORK) {
|
||||
return main;
|
||||
}
|
||||
|
|
14
package.json
14
package.json
|
@ -11,21 +11,17 @@
|
|||
"hardhat:main": "hardhat --network main",
|
||||
"hardhat:docker": "hardhat --network hardhatevm_docker",
|
||||
"compile": "SKIP_LOAD=true hardhat compile",
|
||||
"test": "SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 hardhat test",
|
||||
"test": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test/*.spec.ts",
|
||||
"test-scenarios": "npm run test -- test/__setup.spec.ts test/scenario.spec.ts",
|
||||
"aave:evm:dev:migration": "hardhat aave:dev",
|
||||
"aave:evm:full:migration": "hardhat aave:full",
|
||||
"aave:docker:dev:migration": "npm run hardhat:docker -- aave:dev",
|
||||
"aave:docker:full:migration": "npm run hardhat:docker -- aave:full",
|
||||
"aave:kovan:dev:migration": "npm run hardhat:kovan -- aave:dev --verify",
|
||||
"aave:kovan:full:migration": "npm run hardhat:kovan -- aave:full --verify",
|
||||
"aave:kovan:full:initialize": "npm run hardhat:kovan -- full:initialize-lending-pool --verify --pool Aave",
|
||||
"aave:ropsten:dev:migration": "npm run hardhat:ropsten -- aave:dev --verify",
|
||||
"aave:ropsten:full:migration": "npm run hardhat:ropsten -- aave:full --verify",
|
||||
"aave:fork:main:tenderly": "npm run hardhat:tenderly-main -- aave:full:fork",
|
||||
"aave:fork:main": "MAINNET_FORK=true hardhat aave:full:fork",
|
||||
"aave:main:dev:migration": "npm run hardhat:main -- aave:dev --verify",
|
||||
"aave:main:full:migration": "npm run hardhat:main -- aave:full --verify",
|
||||
"aave:fork:main:tenderly": "npm run hardhat:tenderly-main -- aave:mainnet",
|
||||
"aave:fork:main": "MAINNET_FORK=true hardhat aave:mainnet",
|
||||
"test:main:check-list": "MAINNET_FORK=true TS_NODE_TRANSPILE_ONLY=1 hardhat test test/__setup.spec.ts test/mainnet/check-list.spec.ts",
|
||||
"aave:main:full:migration": "npm run hardhat:main -- aave:mainnet --verify",
|
||||
"uniswap:evm:dev:migration": "hardhat uniswap:dev",
|
||||
"uniswap:evm:full:migration": "hardhat uniswap:full --verify",
|
||||
"uniswap:kovan:dev:migration": "npm run hardhat:kovan -- uniswap:dev --verify",
|
||||
|
|
|
@ -20,10 +20,10 @@ task(
|
|||
)
|
||||
.addFlag('verify', 'Verify contracts at Etherscan')
|
||||
.addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`)
|
||||
.setAction(async ({verify, pool}, localBRE) => {
|
||||
await localBRE.run('set-DRE');
|
||||
console.log('addresses', await getEthersSignersAddresses());
|
||||
const network = <eEthereumNetwork>localBRE.network.name;
|
||||
.setAction(async ({verify, pool}, DRE) => {
|
||||
await DRE.run('set-DRE');
|
||||
|
||||
const network = <eEthereumNetwork>DRE.network.name;
|
||||
const poolConfig = loadPoolConfig(pool);
|
||||
const {ProviderId} = poolConfig;
|
||||
|
||||
|
@ -31,7 +31,6 @@ task(
|
|||
// Deploy address provider and set genesis manager
|
||||
const addressesProvider = await deployLendingPoolAddressesProvider(verify);
|
||||
|
||||
console.log('prox', addressesProvider.address);
|
||||
await waitForTx(await addressesProvider.setPoolAdmin(await getGenesisPoolAdmin(poolConfig)));
|
||||
await waitForTx(await addressesProvider.setEmergencyAdmin(await getEmergencyAdmin(poolConfig)));
|
||||
|
||||
|
|
|
@ -23,16 +23,13 @@ task('full:deploy-lending-pool', 'Deploy lending pool for dev enviroment')
|
|||
try {
|
||||
await DRE.run('set-DRE');
|
||||
|
||||
console.log('addresses', await getEthersSignersAddresses());
|
||||
const addressesProvider = await getLendingPoolAddressesProvider();
|
||||
|
||||
// Deploy lending pool
|
||||
const lendingPoolImpl = await deployLendingPool(verify);
|
||||
|
||||
console.log('set lend poool', addressesProvider.address);
|
||||
// Set lending pool impl to address provider
|
||||
await waitForTx(await addressesProvider.setLendingPoolImpl(lendingPoolImpl.address));
|
||||
console.log('setted');
|
||||
|
||||
const address = await addressesProvider.getLendingPool();
|
||||
const lendingPoolProxy = await getLendingPool(address);
|
||||
|
|
|
@ -27,7 +27,6 @@ task('full:deploy-oracles', 'Deploy oracles for dev enviroment')
|
|||
.addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`)
|
||||
.setAction(async ({verify, pool}, DRE) => {
|
||||
try {
|
||||
console.log('addresses', await getEthersSignersAddresses());
|
||||
await DRE.run('set-DRE');
|
||||
const network = <eEthereumNetwork>DRE.network.name;
|
||||
const poolConfig = loadPoolConfig(pool);
|
||||
|
@ -64,12 +63,6 @@ task('full:deploy-oracles', 'Deploy oracles for dev enviroment')
|
|||
const {USD, ...tokensAddressesWithoutUsd} = tokensToWatch;
|
||||
|
||||
if (!lendingRateOracleAddress) {
|
||||
console.log(
|
||||
lendingRateOracles,
|
||||
tokensAddressesWithoutUsd,
|
||||
lendingRateOracle.address,
|
||||
aggregators
|
||||
);
|
||||
await setInitialMarketRatesInRatesOracleByHelper(
|
||||
lendingRateOracles,
|
||||
tokensAddressesWithoutUsd,
|
||||
|
|
|
@ -25,7 +25,10 @@ task('aave:full', 'Deploy development enviroment')
|
|||
console.log('2. Deploy lending pool');
|
||||
await localBRE.run('full:deploy-lending-pool');
|
||||
|
||||
console.log('3. Initialize lending pool');
|
||||
console.log('3. Deploy data provider');
|
||||
await localBRE.run('full:data-provider');
|
||||
|
||||
console.log('4. Initialize lending pool');
|
||||
await localBRE.run('full:initialize-lending-pool', {pool: POOL_NAME});
|
||||
|
||||
if (verify) {
|
||||
|
|
|
@ -5,7 +5,7 @@ import {ConfigNames} from '../../helpers/configuration';
|
|||
import {EthereumNetworkNames} from '../../helpers/types';
|
||||
import {printContracts} from '../../helpers/misc-utils';
|
||||
|
||||
task('aave:full:fork', 'Deploy development enviroment')
|
||||
task('aave:mainnet', 'Deploy development enviroment')
|
||||
.addFlag('verify', 'Verify contracts at Etherscan')
|
||||
.setAction(async ({verify}, DRE) => {
|
||||
const POOL_NAME = ConfigNames.Aave;
|
||||
|
@ -17,7 +17,6 @@ task('aave:full:fork', 'Deploy development enviroment')
|
|||
checkVerification();
|
||||
}
|
||||
|
||||
// Set the ethers provider to the one we initialized so it targets the correct backend
|
||||
if (network.includes('tenderly')) {
|
||||
console.log('- Setting up Tenderly provider');
|
||||
await DRE.tenderlyRPC.initializeFork();
|
||||
|
@ -36,7 +35,10 @@ task('aave:full:fork', 'Deploy development enviroment')
|
|||
console.log('3. Deploy oracles');
|
||||
await DRE.run('full:deploy-oracles', {pool: POOL_NAME});
|
||||
|
||||
console.log('4. Initialize lending pool');
|
||||
console.log('4. Deploy Data Provider');
|
||||
await DRE.run('full:data-provider', {pool: POOL_NAME});
|
||||
|
||||
console.log('5. Initialize lending pool');
|
||||
await DRE.run('full:initialize-lending-pool', {pool: POOL_NAME});
|
||||
|
||||
if (verify) {
|
||||
|
|
|
@ -273,8 +273,15 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
|
|||
before(async () => {
|
||||
await rawBRE.run('set-DRE');
|
||||
const [deployer, secondaryWallet] = await getEthersSigners();
|
||||
console.log('-> Deploying test environment...');
|
||||
await buildTestEnv(deployer, secondaryWallet);
|
||||
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');
|
||||
|
|
|
@ -106,14 +106,17 @@ export async function initializeMakeSuite() {
|
|||
|
||||
testEnv.helpersContract = await getAaveProtocolDataProvider();
|
||||
|
||||
console.log('het cpmtra');
|
||||
const allTokens = await testEnv.helpersContract.getAllATokens();
|
||||
|
||||
console.log('tokann');
|
||||
const aDaiAddress = allTokens.find((aToken) => aToken.symbol === 'aDAI')?.tokenAddress;
|
||||
|
||||
const aWEthAddress = allTokens.find((aToken) => aToken.symbol === 'aWETH')?.tokenAddress;
|
||||
|
||||
console.log('priah');
|
||||
const reservesTokens = await testEnv.helpersContract.getAllReservesTokens();
|
||||
|
||||
console.log('all tokan');
|
||||
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;
|
||||
|
@ -136,6 +139,7 @@ export async function initializeMakeSuite() {
|
|||
testEnv.aave = await getMintableErc20(aaveAddress);
|
||||
testEnv.weth = await getWETHMocked(wethAddress);
|
||||
testEnv.wethGateway = await getWETHGateway();
|
||||
console.log('laa');
|
||||
}
|
||||
|
||||
export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
|
||||
|
|
362
test/mainnet/check-list.spec.ts
Normal file
362
test/mainnet/check-list.spec.ts
Normal file
|
@ -0,0 +1,362 @@
|
|||
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.');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user