From 383f7fc3bb13b92aa979c623321dda9036d41a95 Mon Sep 17 00:00:00 2001 From: David Racero Date: Thu, 20 Aug 2020 17:35:05 +0200 Subject: [PATCH] WIP: Added dev-deployment scripts. Moved some helpers and mocks outside of test setup. --- helpers/etherscan-verification.ts | 96 +++++++++++++++ helpers/oracles-helpers.ts | 62 ++++++++++ tasks/dev-deployment/1_mock_tokens.ts | 9 ++ .../2_address_provider_registry.ts | 35 ++++++ tasks/dev-deployment/3_lending_pool.ts | 44 +++++++ tasks/dev-deployment/4_oracles.ts | 111 ++++++++++++++++++ tasks/dev-deployment/5_initialize.wip | 102 ++++++++++++++++ tasks/migrations/dev-migration.ts | 34 ++++++ tasks/misc/verify-sc.ts | 35 ++++++ 9 files changed, 528 insertions(+) create mode 100644 helpers/etherscan-verification.ts create mode 100644 helpers/oracles-helpers.ts create mode 100644 tasks/dev-deployment/1_mock_tokens.ts create mode 100644 tasks/dev-deployment/2_address_provider_registry.ts create mode 100644 tasks/dev-deployment/3_lending_pool.ts create mode 100644 tasks/dev-deployment/4_oracles.ts create mode 100644 tasks/dev-deployment/5_initialize.wip create mode 100644 tasks/migrations/dev-migration.ts create mode 100644 tasks/misc/verify-sc.ts diff --git a/helpers/etherscan-verification.ts b/helpers/etherscan-verification.ts new file mode 100644 index 00000000..3c507a1d --- /dev/null +++ b/helpers/etherscan-verification.ts @@ -0,0 +1,96 @@ +import {BRE} from './misc-utils'; + +export const SUPPORTED_ETHERSCAN_NETWORKS = ['main', 'ropsten', 'kovan']; + +export const getEtherscanPath = async (contractName: string) => { + const compilerInput = await BRE.run('compile:get-compiler-input'); + const paths = Object.keys(compilerInput.sources); + const path = paths.find((p) => p.includes(contractName)); + if (!path) { + throw new Error( + `Contract path not found for ${contractName}. Check if smart contract file is equal to contractName input.` + ); + } + + return `${path}:${contractName}`; +}; + +function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +export const verifyContract = async ( + contractName: string, + address: string, + constructorArguments: string[], + libraries?: string +) => { + const currentNetwork = BRE.network.name; + + if (!process.env.ETHERSCAN_KEY) { + throw Error('Missing process.env.ETHERSCAN_KEY.'); + } + if (!process.env.ETHERSCAN_NETWORK) { + throw Error('Missing process.env.ETHERSCAN_NETWORK'); + } + if (!SUPPORTED_ETHERSCAN_NETWORKS.includes(currentNetwork)) { + throw Error( + `Current network ${currentNetwork} not supported. Please change to one of the next networks: ${SUPPORTED_ETHERSCAN_NETWORKS.toString()}` + ); + } + const etherscanPath = await getEtherscanPath(contractName); + + const params = { + contractName: etherscanPath, + address: address, + constructorArguments, + libraries, + }; + + try { + console.log( + '[ETHERSCAN][WARNING] Delaying Etherscan verification due their API can not find newly deployed contracts' + ); + const msDelay = 3000; + const times = 30; + await runTaskWithRetry('verify-contract', params, times, msDelay); + } catch (error) {} +}; + +export const runTaskWithRetry = async ( + task: string, + params: any, + times: number, + msDelay: number +) => { + let counter = times; + await delay(msDelay); + + try { + if (times) { + await BRE.run(task, params); + } else { + console.error('[ERROR] Errors after all the retries, check the logs for more information.'); + } + } catch (error) { + counter--; + console.info(`[INFO] Retrying attemps: ${counter}.`); + console.error('[ERROR]', error.message); + await runTaskWithRetry(task, params, counter, msDelay); + } +}; + +export const checkVerification = () => { + const currentNetwork = BRE.network.name; + if (!process.env.ETHERSCAN_KEY) { + throw Error('Missing process.env.ETHERSCAN_KEY.'); + } + if (!process.env.ETHERSCAN_NETWORK) { + throw Error('Missing process.env.ETHERSCAN_NETWORK'); + } + if (!SUPPORTED_ETHERSCAN_NETWORKS.includes(currentNetwork)) { + throw Error( + `Current network ${currentNetwork} not supported. Please change to one of the next networks: ${SUPPORTED_ETHERSCAN_NETWORKS.toString()}` + ); + } +}; diff --git a/helpers/oracles-helpers.ts b/helpers/oracles-helpers.ts new file mode 100644 index 00000000..d6ebc3fe --- /dev/null +++ b/helpers/oracles-helpers.ts @@ -0,0 +1,62 @@ +import { + tEthereumAddress, + iMultiPoolsAssets, + IMarketRates, + iAssetBase, + iAssetAggregatorBase, +} from './types'; + +import {LendingRateOracle} from '../types/LendingRateOracle'; +import {PriceOracle} from '../types/PriceOracle'; +import {MockAggregator} from '../types/MockAggregator'; +import {deployMockAggregator} from './contracts-helpers'; +import {waitForTx} from './misc-utils'; + +export const setInitialMarketRatesInRatesOracle = async ( + marketRates: iMultiPoolsAssets, + assetsAddresses: {[x: string]: tEthereumAddress}, + lendingRateOracleInstance: LendingRateOracle +) => { + for (const [assetSymbol, {borrowRate}] of Object.entries(marketRates) as [ + string, + IMarketRates + ][]) { + const assetAddressIndex = Object.keys(assetsAddresses).findIndex( + (value) => value === assetSymbol + ); + const [, assetAddress] = (Object.entries(assetsAddresses) as [string, string][])[ + assetAddressIndex + ]; + await lendingRateOracleInstance.setMarketBorrowRate(assetAddress, borrowRate); + } +}; + +export const setInitialAssetPricesInOracle = async ( + prices: iAssetBase, + assetsAddresses: iAssetBase, + priceOracleInstance: PriceOracle +) => { + for (const [assetSymbol, price] of Object.entries(prices) as [string, string][]) { + const assetAddressIndex = Object.keys(assetsAddresses).findIndex( + (value) => value === assetSymbol + ); + const [, assetAddress] = (Object.entries(assetsAddresses) as [string, string][])[ + assetAddressIndex + ]; + await waitForTx(await priceOracleInstance.setAssetPrice(assetAddress, price)); + } +}; + +export const deployAllMockAggregators = async (initialPrices: iAssetAggregatorBase) => { + const aggregators: {[tokenSymbol: string]: MockAggregator} = {}; + for (const tokenContractName of Object.keys(initialPrices)) { + if (tokenContractName !== 'ETH') { + const priceIndex = Object.keys(initialPrices).findIndex( + (value) => value === tokenContractName + ); + const [, price] = (Object.entries(initialPrices) as [string, string][])[priceIndex]; + aggregators[tokenContractName] = await deployMockAggregator(price); + } + } + return aggregators; +}; diff --git a/tasks/dev-deployment/1_mock_tokens.ts b/tasks/dev-deployment/1_mock_tokens.ts new file mode 100644 index 00000000..e69c63d8 --- /dev/null +++ b/tasks/dev-deployment/1_mock_tokens.ts @@ -0,0 +1,9 @@ +import {task} from '@nomiclabs/buidler/config'; +import {deployAllMockTokens} from '../../helpers/contracts-helpers'; + +task('deploy-mock-tokens', 'Deploy mock tokens for dev enviroment') + .addOptionalParam('verify', 'Verify contracts at Etherscan') + .setAction(async ({verify}, localBRE) => { + await localBRE.run('set-bre'); + await deployAllMockTokens(verify); + }); diff --git a/tasks/dev-deployment/2_address_provider_registry.ts b/tasks/dev-deployment/2_address_provider_registry.ts new file mode 100644 index 00000000..4527ca19 --- /dev/null +++ b/tasks/dev-deployment/2_address_provider_registry.ts @@ -0,0 +1,35 @@ +import {task} from '@nomiclabs/buidler/config'; +import { + deployLendingPoolAddressesProvider, + deployLendingPoolAddressesProviderRegistry, + deployFeeProvider, + getFeeProvider, + insertContractAddressInDb, +} from '../../helpers/contracts-helpers'; +import {eContractid} from '../../helpers/types'; +import {waitForTx} from '../../helpers/misc-utils'; + +task( + 'deploy-address-provider', + 'Deploy address provider, registry and fee provider for dev enviroment' +) + .addOptionalParam('verify', 'Verify contracts at Etherscan') + .setAction(async ({verify}, localBRE) => { + await localBRE.run('set-bre'); + + const lendingPoolManager = await (await localBRE.ethers.getSigners())[0].getAddress(); + + const addressesProvider = await deployLendingPoolAddressesProvider(verify); + await waitForTx(await addressesProvider.setLendingPoolManager(lendingPoolManager)); + + const addressesProviderRegistry = await deployLendingPoolAddressesProviderRegistry(verify); + await waitForTx( + await addressesProviderRegistry.registerAddressesProvider(addressesProvider.address, 0) + ); + + const feeProviderImpl = await deployFeeProvider(verify); + await waitForTx(await addressesProvider.setFeeProviderImpl(feeProviderImpl.address)); + + const feeProviderProxy = await getFeeProvider(await addressesProvider.getFeeProvider()); + await insertContractAddressInDb(eContractid.FeeProvider, feeProviderProxy.address); + }); diff --git a/tasks/dev-deployment/3_lending_pool.ts b/tasks/dev-deployment/3_lending_pool.ts new file mode 100644 index 00000000..2a477ee0 --- /dev/null +++ b/tasks/dev-deployment/3_lending_pool.ts @@ -0,0 +1,44 @@ +import {task} from '@nomiclabs/buidler/config'; +import { + deployLendingPool, + getLendingPoolAddressesProvider, + getLendingPool, + insertContractAddressInDb, + deployLendingPoolConfigurator, + getLendingPoolConfiguratorProxy, +} from '../../helpers/contracts-helpers'; +import {eContractid} from '../../helpers/types'; +import {waitForTx} from '../../helpers/misc-utils'; + +task('deploy-lending-pool', 'Deploy lending pool for dev enviroment') + .addOptionalParam('verify', 'Verify contracts at Etherscan') + .setAction(async ({verify}, localBRE) => { + await localBRE.run('set-bre'); + + const addressesProvider = await getLendingPoolAddressesProvider(); + + const lendingPoolImpl = await deployLendingPool(verify); + + // Set lending pool impl to Address Provider + await waitForTx(await addressesProvider.setLendingPoolImpl(lendingPoolImpl.address)); + + const address = await addressesProvider.getLendingPool(); + const lendingPoolProxy = await getLendingPool(address); + + await insertContractAddressInDb(eContractid.LendingPool, lendingPoolProxy.address); + + const lendingPoolConfiguratorImpl = await deployLendingPoolConfigurator(verify); + + // Set lending pool conf impl to Address Provider + await waitForTx( + await addressesProvider.setLendingPoolConfiguratorImpl(lendingPoolConfiguratorImpl.address) + ); + + const lendingPoolConfiguratorProxy = await getLendingPoolConfiguratorProxy( + await addressesProvider.getLendingPoolConfigurator() + ); + await insertContractAddressInDb( + eContractid.LendingPoolConfigurator, + lendingPoolConfiguratorProxy.address + ); + }); diff --git a/tasks/dev-deployment/4_oracles.ts b/tasks/dev-deployment/4_oracles.ts new file mode 100644 index 00000000..dba251dd --- /dev/null +++ b/tasks/dev-deployment/4_oracles.ts @@ -0,0 +1,111 @@ +import {task} from '@nomiclabs/buidler/config'; +import { + getLendingPoolAddressesProvider, + deployPriceOracle, + getMockedTokens, + getPairsTokenAggregator, + deployChainlinkProxyPriceProvider, + deployLendingRateOracle, +} from '../../helpers/contracts-helpers'; +import { + MOCK_USD_PRICE_IN_WEI, + ALL_ASSETS_INITIAL_PRICES, + USD_ADDRESS, + MOCK_CHAINLINK_AGGREGATORS_PRICES, + LENDING_RATE_ORACLE_RATES_COMMON, +} from '../../helpers/constants'; +import { + setInitialAssetPricesInOracle, + deployAllMockAggregators, + setInitialMarketRatesInRatesOracle, +} from '../../helpers/oracles-helpers'; +import {tEthereumAddress} from '../../helpers/types'; +import {waitForTx} from '../../helpers/misc-utils'; + +task('deploy-oracles', 'Deploy oracles for dev enviroment') + .addOptionalParam('verify', 'Verify contracts at Etherscan') + .setAction(async ({verify}, localBRE) => { + await localBRE.run('set-bre'); + + const mockTokens = await getMockedTokens(); + + const addressesProvider = await getLendingPoolAddressesProvider(); + + const fallbackOracle = await deployPriceOracle(verify); + 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, + LEND: mockTokens.LEND.address, + BAT: mockTokens.BAT.address, + REP: mockTokens.REP.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, + USD: USD_ADDRESS, + UNI_DAI_ETH: mockTokens.UNI_DAI_ETH.address, + UNI_USDC_ETH: mockTokens.UNI_USDC_ETH.address, + UNI_SETH_ETH: mockTokens.UNI_SETH_ETH.address, + UNI_LEND_ETH: mockTokens.UNI_LEND_ETH.address, + UNI_MKR_ETH: mockTokens.UNI_MKR_ETH.address, + UNI_LINK_ETH: mockTokens.UNI_LINK_ETH.address, + }, + fallbackOracle + ); + + // TODO: Missing verify + 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 + ); + + // TODO: Missing verify and getter + const chainlinkProxyPriceProvider = await deployChainlinkProxyPriceProvider([ + tokens, + aggregators, + fallbackOracle.address, + ]); + await waitForTx(await addressesProvider.setPriceOracle(fallbackOracle.address)); + + // TODO: Missing verify + const lendingRateOracle = await deployLendingRateOracle(); + await waitForTx(await addressesProvider.setLendingRateOracle(lendingRateOracle.address)); + + const {USD, ...tokensAddressesWithoutUsd} = allTokenAddresses; + const allReservesAddresses = { + ...tokensAddressesWithoutUsd, + }; + await setInitialMarketRatesInRatesOracle( + LENDING_RATE_ORACLE_RATES_COMMON, + allReservesAddresses, + lendingRateOracle + ); + }); diff --git a/tasks/dev-deployment/5_initialize.wip b/tasks/dev-deployment/5_initialize.wip new file mode 100644 index 00000000..1d0b9afe --- /dev/null +++ b/tasks/dev-deployment/5_initialize.wip @@ -0,0 +1,102 @@ +import {task} from '@nomiclabs/buidler/config'; +import { +getLendingPoolAddressesProvider, +getMockedTokens, +initReserves, +} from '../../helpers/contracts-helpers'; +import {getReservesConfigByPool} from '../../helpers/constants'; + +import {tEthereumAddress, AavePools} from '../../helpers/types'; +import {waitForTx} from '../../helpers/misc-utils'; + +task('initialize-lending-pool', 'Deploy oracles for dev enviroment') +.addOptionalParam('verify', 'Verify contracts at Etherscan') +.setAction(async ({verify}, localBRE) => { +await localBRE.run('set-bre'); + + const mockTokens = await getMockedTokens(); + + const allTokenAddresses = Object.entries(mockTokens).reduce( + (accum: {[tokenSymbol: string]: tEthereumAddress}, [tokenSymbol, tokenContract]) => ({ + ...accum, + [tokenSymbol]: tokenContract.address, + }), + {} + ); + const addressesProvider = await getLendingPoolAddressesProvider(); + + const { + UNI_DAI_ETH, + UNI_USDC_ETH, + UNI_SETH_ETH, + UNI_LINK_ETH, + UNI_MKR_ETH, + UNI_LEND_ETH, + ...protoPoolReservesAddresses + } = <{[symbol: string]: tEthereumAddress}>allTokenAddresses; + + const reservesParams = getReservesConfigByPool(AavePools.proto); + + console.log('Initialize configuration'); + await initReserves( + reservesParams, + protoPoolReservesAddresses, + addressesProvider, + lendingPoolProxy, + lendingPoolConfiguratorProxy, + AavePools.proto + ); + await enableReservesToBorrow( + reservesParams, + protoPoolReservesAddresses, + lendingPoolProxy, + lendingPoolConfiguratorProxy + ); + await enableReservesAsCollateral( + reservesParams, + protoPoolReservesAddresses, + lendingPoolProxy, + lendingPoolConfiguratorProxy + ); + + const liquidationManager = await deployLendingPoolLiquidationManager(); + await waitForTx( + await addressesProvider.setLendingPoolLiquidationManager(liquidationManager.address) + ); + + const {receivers, percentages} = getFeeDistributionParamsCommon(lendingPoolManager); + + const tokenDistributorImpl = await deployTokenDistributor(); + const tokenDistributorProxy = await deployInitializableAdminUpgradeabilityProxy(); + const implementationParams = tokenDistributorImpl.interface.encodeFunctionData('initialize', [ + ZERO_ADDRESS, + tokensAddressesWithoutUsd.LEND, + '0x0000000000000000000000000000000000000000', // TODO: finish removal + receivers, + percentages, + Object.values(tokensAddressesWithoutUsd), + ]); + await waitForTx( + await tokenDistributorProxy['initialize(address,address,bytes)']( + tokenDistributorImpl.address, + await secondaryWallet.getAddress(), + implementationParams + ) + ); + await waitForTx(await addressesProvider.setTokenDistributor(tokenDistributorProxy.address)); + + await insertContractAddressInDb(eContractid.TokenDistributor, tokenDistributorProxy.address); + + const mockFlashLoanReceiver = await deployMockFlashLoanReceiver(addressesProvider.address); + await insertContractAddressInDb( + eContractid.MockFlashLoanReceiver, + mockFlashLoanReceiver.address + ); + + await deployWalletBalancerProvider(addressesProvider.address); + + const testHelpers = await deployAaveProtocolTestHelpers(addressesProvider.address); + + await insertContractAddressInDb(eContractid.AaveProtocolTestHelpers, testHelpers.address); + +}); diff --git a/tasks/migrations/dev-migration.ts b/tasks/migrations/dev-migration.ts new file mode 100644 index 00000000..4d77fcad --- /dev/null +++ b/tasks/migrations/dev-migration.ts @@ -0,0 +1,34 @@ +import {task} from '@nomiclabs/buidler/config'; +import {checkVerification} from '../../helpers/etherscan-verification'; + +task('dev-migration', 'Deploy development enviroment') + .addOptionalParam('verify', 'Verify contracts at Etherscan') + .setAction(async ({verify}, localBRE) => { + await localBRE.run('set-bre'); + + // Prevent loss of gas verifying all the needed ENVs for Etherscan verification + if (verify) { + checkVerification(); + } + + console.log('Migration started\n'); + + console.log('1. Deploy mock tokens'); + await localBRE.run('deploy-mock-tokens'); + + console.log('2. Deploy address provider'); + await localBRE.run('deploy-address-provider'); + + console.log('3. Deploy lending pool'); + await localBRE.run('deploy-lending-pool'); + + console.log('4. Deploy oracles'); + await localBRE.run('deploy-oracles'); + + // 5. Initialize lending pool configuration + // + // console.log('5. Initialize lending pool') + // await localBRE.run('initialize-lending-pool'); + + console.log('\nFinished migration'); + }); diff --git a/tasks/misc/verify-sc.ts b/tasks/misc/verify-sc.ts new file mode 100644 index 00000000..ee1ce0e2 --- /dev/null +++ b/tasks/misc/verify-sc.ts @@ -0,0 +1,35 @@ +import {task} from '@nomiclabs/buidler/config'; +import {verifyContract, checkVerification} from '../../helpers/etherscan-verification'; + +interface VerifyParams { + contractName: string; + address: string; + constructorArguments: string[]; + libraries: string; +} + +task('verify-sc', 'Inits the BRE, to have access to all the plugins') + .addParam('contractName', 'Name of the Solidity smart contract') + .addParam('address', 'Ethereum address of the smart contract') + .addOptionalParam( + 'libraries', + 'Stringified JSON object in format of {library1: "0x2956356cd2a2bf3202f771f50d3d14a367b48071"}' + ) + .addOptionalVariadicPositionalParam( + 'constructorArguments', + 'arguments for contract constructor', + [] + ) + .setAction( + async ( + {contractName, address, constructorArguments = [], libraries}: VerifyParams, + localBRE + ) => { + await localBRE.run('set-bre'); + + checkVerification(); + + const result = await verifyContract(contractName, address, constructorArguments, libraries); + return result; + } + );