From e0025184a08da460dd96c97d5101c8f3ae6f0842 Mon Sep 17 00:00:00 2001 From: David Racero Date: Tue, 1 Dec 2020 12:39:54 +0100 Subject: [PATCH] Added resume initialization task --- hardhat.config.ts | 2 +- helpers/init-helpers.ts | 191 +++++++++++++++++++++++++++++++- package.json | 6 +- tasks/full/initialize-tokens.ts | 96 ++++++++++++++++ tasks/misc/print-config.ts | 2 +- 5 files changed, 290 insertions(+), 7 deletions(-) create mode 100644 tasks/full/initialize-tokens.ts diff --git a/hardhat.config.ts b/hardhat.config.ts index df732515..98935035 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -62,7 +62,7 @@ const getCommonNetworkConfig = (networkName: eEthereumNetwork, networkId: number const mainnetFork = MAINNET_FORK ? { - blockNumber: 11361132, + blockNumber: 11366117, url: ALCHEMY_KEY ? `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_KEY}` : `https://main.infura.io/v3/${INFURA_KEY}`, diff --git a/helpers/init-helpers.ts b/helpers/init-helpers.ts index 668124f0..923f68cc 100644 --- a/helpers/init-helpers.ts +++ b/helpers/init-helpers.ts @@ -1,13 +1,20 @@ -import { eContractid, iMultiPoolsAssets, IReserveParams, tEthereumAddress } from './types'; -import { AaveProtocolDataProvider } from '../types/AaveProtocolDataProvider'; -import { chunk, waitForTx } from './misc-utils'; import { + eContractid, + eEthereumNetwork, + iMultiPoolsAssets, + IReserveParams, + tEthereumAddress, +} from './types'; +import { AaveProtocolDataProvider } from '../types/AaveProtocolDataProvider'; +import { chunk, DRE, getDb, waitForTx } from './misc-utils'; +import { + getAaveProtocolDataProvider, getATokensAndRatesHelper, getLendingPoolAddressesProvider, getStableAndVariableTokensHelper, } from './contracts-getters'; import { rawInsertContractAddressInDb } from './contracts-helpers'; -import { BigNumber, BigNumberish } from 'ethers'; +import { BigNumber, BigNumberish, Signer } from 'ethers'; import { deployDefaultReserveInterestRateStrategy, deployDelegationAwareAToken, @@ -16,6 +23,7 @@ import { deployVariableDebtToken, } from './contracts-deployments'; import { ZERO_ADDRESS } from './constants'; +import { isZeroAddress } from 'ethereumjs-util'; const chooseATokenDeployment = (id: eContractid) => { switch (id) { @@ -374,3 +382,178 @@ export const configureReservesByHelper = async ( await waitForTx(await addressProvider.setPoolAdmin(admin)); } }; + +const getAddressById = async ( + id: string, + network: eEthereumNetwork +): Promise => + (await getDb().get(`${id}.${network}`).value())?.address || undefined; + +export const initTokenReservesByHelper = async ( + reservesParams: iMultiPoolsAssets, + tokenAddresses: { [symbol: string]: tEthereumAddress }, + admin: tEthereumAddress, + addressesProviderAddress: tEthereumAddress, + ratesHelperAddress: tEthereumAddress, + dataProviderAddress: tEthereumAddress, + signer: Signer, + treasuryAddress: tEthereumAddress, + verify: boolean +) => { + let gasUsage = BigNumber.from('0'); + const atokenAndRatesDeployer = await (await getATokensAndRatesHelper(ratesHelperAddress)).connect( + signer + ); + + const addressProvider = await ( + await getLendingPoolAddressesProvider(addressesProviderAddress) + ).connect(signer); + const protocolDataProvider = await ( + await getAaveProtocolDataProvider(dataProviderAddress) + ).connect(signer); + const poolAddress = await addressProvider.getLendingPool(); + + // Set aTokenAndRatesDeployer as temporal admin + await waitForTx(await addressProvider.setPoolAdmin(atokenAndRatesDeployer.address)); + + // CHUNK CONFIGURATION + const initChunks = 4; + + // Initialize variables for future reserves initialization + let deployedStableTokens: string[] = []; + let deployedVariableTokens: string[] = []; + let deployedATokens: string[] = []; + let deployedRates: string[] = []; + let reserveInitDecimals: string[] = []; + let reserveSymbols: string[] = []; + const network = + process.env.MAINNET_FORK === 'true' + ? eEthereumNetwork.main + : (DRE.network.name as eEthereumNetwork); + // Grab config from DB + for (const [symbol, address] of Object.entries(tokenAddresses)) { + const { aTokenAddress } = await protocolDataProvider.getReserveTokensAddresses(address); + const reserveParamIndex = Object.keys(reservesParams).findIndex((value) => value === symbol); + const [, { reserveDecimals: decimals }] = (Object.entries(reservesParams) as [ + string, + IReserveParams + ][])[reserveParamIndex]; + + if (!isZeroAddress(aTokenAddress)) { + console.log(`Skipping ${symbol} due already initialized`); + continue; + } + console.log('Getting deployment information from', symbol); + let stableTokenImpl = await getAddressById(`stableDebt${symbol}`, network); + let variableTokenImpl = await getAddressById(`variableDebt${symbol}`, network); + let aTokenImplementation = await getAddressById(`a${symbol}`, network); + let strategyImpl = await getAddressById(`strategy${symbol}`, network); + + if (!stableTokenImpl) { + const stableDebt = await deployStableDebtToken( + [ + poolAddress, + tokenAddresses[symbol], + `Aave stable debt bearing ${symbol}`, + `stableDebt${symbol}`, + ZERO_ADDRESS, + ], + verify + ); + stableTokenImpl = stableDebt.address; + } + if (!variableTokenImpl) { + const variableDebt = await deployVariableDebtToken( + [ + poolAddress, + tokenAddresses[symbol], + `Aave variable debt bearing ${symbol}`, + `variableDebt${symbol}`, + ZERO_ADDRESS, + ], + verify + ); + variableTokenImpl = variableDebt.address; + } + if (!aTokenImplementation) { + const [, { aTokenImpl }] = (Object.entries(reservesParams) as [string, IReserveParams][])[ + reserveParamIndex + ]; + const deployCustomAToken = chooseATokenDeployment(aTokenImpl); + const aToken = await deployCustomAToken( + [ + poolAddress, + tokenAddresses[symbol], + treasuryAddress, + `Aave interest bearing ${symbol}`, + `a${symbol}`, + ZERO_ADDRESS, + ], + verify + ); + aTokenImplementation = aToken.address; + } + if (!strategyImpl) { + const [ + , + { + optimalUtilizationRate, + baseVariableBorrowRate, + variableRateSlope1, + variableRateSlope2, + stableRateSlope1, + stableRateSlope2, + }, + ] = (Object.entries(reservesParams) as [string, IReserveParams][])[reserveParamIndex]; + const rates = await deployDefaultReserveInterestRateStrategy( + [ + tokenAddresses[symbol], + optimalUtilizationRate, + baseVariableBorrowRate, + variableRateSlope1, + variableRateSlope2, + stableRateSlope1, + stableRateSlope2, + ], + verify + ); + strategyImpl = rates.address; + } + + deployedStableTokens.push(stableTokenImpl); + deployedVariableTokens.push(variableTokenImpl); + deployedATokens.push(aTokenImplementation); + deployedRates.push(strategyImpl); + reserveInitDecimals.push(decimals.toString()); + reserveSymbols.push(symbol); + } + + // Deploy init reserves per chunks + const chunkedStableTokens = chunk(deployedStableTokens, initChunks); + const chunkedVariableTokens = chunk(deployedVariableTokens, initChunks); + const chunkedAtokens = chunk(deployedATokens, initChunks); + const chunkedRates = chunk(deployedRates, initChunks); + const chunkedDecimals = chunk(reserveInitDecimals, initChunks); + const chunkedSymbols = chunk(reserveSymbols, initChunks); + + console.log(`- Reserves initialization in ${chunkedStableTokens.length} txs`); + for (let chunkIndex = 0; chunkIndex < chunkedDecimals.length; chunkIndex++) { + const tx3 = await waitForTx( + await atokenAndRatesDeployer.initReserve( + chunkedStableTokens[chunkIndex], + chunkedVariableTokens[chunkIndex], + chunkedAtokens[chunkIndex], + chunkedRates[chunkIndex], + chunkedDecimals[chunkIndex] + ) + ); + + console.log(` - Reserve ready for: ${chunkedSymbols[chunkIndex].join(', ')}`); + console.log(' * gasUsed', tx3.gasUsed.toString()); + gasUsage = gasUsage.add(tx3.gasUsed); + } + + // Set deployer back as admin + await waitForTx(await addressProvider.setPoolAdmin(admin)); + return gasUsage; +}; diff --git a/package.json b/package.json index 24c24213..367b894c 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "aave:fork:main:tenderly": "npm run compile && npm run hardhat:tenderly-main -- aave:mainnet", "aave:fork:main": "npm run compile && MAINNET_FORK=true hardhat aave:mainnet", "aave:main:full:migration": "npm run compile && npm run hardhat:main -- aave:mainnet --verify", + "aave:main:full:initialize": "npm run compile && MAINNET_FORK=true full:initialize-tokens --pool Aave", "dev:prettier": "prettier --write .", "ci:test": "npm run compile && npm run test", "ci:clean": "rm -rf ./artifacts ./cache ./types", @@ -50,7 +51,10 @@ "ropsten:verify:tokens": "npm run hardhat:ropsten verify:tokens -- --pool Aave", "mainnet:verify:tokens": "npm run hardhat:main verify:tokens -- --pool Aave", "print-config:fork:mainnet": "MAINNET_FORK=true hardhat print-config:fork", - "print-config:kovan": "hardhat --network kovan print-config --pool Aave --data-provider 0xA1901785c29cBd48bfA74e46b67C736b26054fa4" + "print-config:kovan": "hardhat --network kovan print-config --pool Aave --data-provider 0xA1901785c29cBd48bfA74e46b67C736b26054fa4", + "main:fork:initialize-tokens": "npm run compile && MAINNET_FORK=true hardhat full:initialize-tokens --pool Aave", + "main:initialize-tokens": "npm run compile && hardhat --network main full:initialize-tokens --pool Aave", + "kovan:initialize-tokens": "npm run compile && hardhat --network kovan full:initialize-tokens --pool Aave" }, "devDependencies": { "@nomiclabs/buidler": "^1.4.7", diff --git a/tasks/full/initialize-tokens.ts b/tasks/full/initialize-tokens.ts new file mode 100644 index 00000000..91822f84 --- /dev/null +++ b/tasks/full/initialize-tokens.ts @@ -0,0 +1,96 @@ +import { task } from 'hardhat/config'; +import { getParamPerNetwork } from '../../helpers/contracts-helpers'; +import { loadPoolConfig, ConfigNames, getTreasuryAddress } from '../../helpers/configuration'; +import { eEthereumNetwork, ICommonConfiguration } from '../../helpers/types'; +import { waitForTx } from '../../helpers/misc-utils'; +import { initTokenReservesByHelper } from '../../helpers/init-helpers'; +import { exit } from 'process'; +import { + getFirstSigner, + getLendingPoolAddressesProvider, + getLendingPoolAddressesProviderRegistry, +} from '../../helpers/contracts-getters'; +import { Signer } from 'ethers'; +import { formatEther, parseEther } from 'ethers/lib/utils'; + +task('full:initialize-tokens', 'Initialize lending pool configuration.') + .addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`) + .addParam('ratesDeployer', `RatesHelper address `) + .addParam('dataProvider', `Data provider address`) + .addFlag('verify') + .setAction(async ({ verify, pool, dataProvider, ratesDeployer }, DRE) => { + try { + await DRE.run('set-DRE'); + let signer: Signer; + const network = + process.env.MAINNET_FORK === 'true' + ? eEthereumNetwork.main + : DRE.network.name; + const poolConfig = loadPoolConfig(pool); + const { ReserveAssets, ReservesConfig } = poolConfig as ICommonConfiguration; + + const reserveAssets = await getParamPerNetwork(ReserveAssets, network); + + const treasuryAddress = await getTreasuryAddress(poolConfig); + const providerRegistryAddress = getParamPerNetwork(poolConfig.ProviderRegistry, network); + const providerRegistryOwner = getParamPerNetwork(poolConfig.ProviderRegistryOwner, network); + + const providerRegistry = await getLendingPoolAddressesProviderRegistry( + providerRegistryAddress + ); + + const providers = await providerRegistry.getAddressesProvidersList(); + + const addressesProvider = await getLendingPoolAddressesProvider(providers[0]); // Checks first provider + + const admin = await addressesProvider.getPoolAdmin(); + if (!reserveAssets) { + throw 'Reserve assets is undefined. Check ReserveAssets configuration at config directory'; + } + + if (process.env.MAINNET_FORK === 'true') { + await DRE.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [providerRegistryOwner], + }); + signer = DRE.ethers.provider.getSigner(providerRegistryOwner); + const user = await getFirstSigner(); + await waitForTx( + await user.sendTransaction({ to: await signer.getAddress(), value: parseEther('10') }) + ); + + const balance = await signer.getBalance(); + console.log('signer balance', formatEther(balance)); + } else { + signer = await getFirstSigner(); + const deployerAddress = await signer.getAddress(); + if (providerRegistryOwner !== (await signer.getAddress())) { + throw Error( + `Current signer is not provider registry owner. \nCurrent deployer address: ${deployerAddress} \nExpected address: ${poolConfig.ProviderRegistryOwner}` + ); + } + } + + // Init unitilialized reserves + await initTokenReservesByHelper( + ReservesConfig, + reserveAssets, + admin, + addressesProvider.address, + ratesDeployer, + dataProvider, + signer, + treasuryAddress, + verify + ); + + // Show contracts state + await DRE.run('print-config', { + pool: 'Aave', + dataProvider, + }); + } catch (err) { + console.error(err); + exit(1); + } + }); diff --git a/tasks/misc/print-config.ts b/tasks/misc/print-config.ts index 24dd33e5..6a7db42a 100644 --- a/tasks/misc/print-config.ts +++ b/tasks/misc/print-config.ts @@ -68,7 +68,7 @@ task('print-config', 'Inits the DRE, to have access to all the plugins') const reserveData = await protocolDataProvider.getReserveConfigurationData(address); const tokensAddresses = await protocolDataProvider.getReserveTokensAddresses(address); fields.forEach((field, index) => { - console.log(` - ${field}:`, reserveData[index].toString()); + console.log(` - ${field}:`, reserveData[field].toString()); }); tokensFields.forEach((field, index) => { console.log(` - ${field}:`, tokensAddresses[index]);