import { Contract } from 'ethers'; import { DRE, notFalsyOrZeroAddress } from './misc-utils'; import { tEthereumAddress, eContractid, tStringTokenSmallUnits, AavePools, TokenContractId, iMultiPoolsAssets, IReserveParams, PoolConfiguration, eEthereumNetwork, } from './types'; import { MintableERC20 } from '../types/MintableERC20'; import { MockContract } from 'ethereum-waffle'; import { ConfigNames, getReservesConfigByPool, loadPoolConfig } from './configuration'; import { getFirstSigner } from './contracts-getters'; import { AaveProtocolDataProviderFactory, ATokenFactory, ATokensAndRatesHelperFactory, AaveOracleFactory, DefaultReserveInterestRateStrategyFactory, DelegationAwareATokenFactory, InitializableAdminUpgradeabilityProxyFactory, LendingPoolAddressesProviderFactory, LendingPoolAddressesProviderRegistryFactory, LendingPoolCollateralManagerFactory, LendingPoolConfiguratorFactory, LendingPoolFactory, LendingRateOracleFactory, MintableDelegationERC20Factory, MintableERC20Factory, MockAggregatorFactory, MockATokenFactory, MockFlashLoanReceiverFactory, MockParaSwapAugustusFactory, MockParaSwapAugustusRegistryFactory, MockStableDebtTokenFactory, MockVariableDebtTokenFactory, MockUniswapV2Router02Factory, ParaSwapLiquiditySwapAdapterFactory, PriceOracleFactory, ReserveLogicFactory, SelfdestructTransferFactory, StableDebtTokenFactory, UniswapLiquiditySwapAdapterFactory, UniswapRepayAdapterFactory, VariableDebtTokenFactory, WalletBalanceProviderFactory, WETH9MockedFactory, WETHGatewayFactory, FlashLiquidationAdapterFactory, UiPoolDataProviderV2Factory, UiPoolDataProviderV2V3Factory, UiIncentiveDataProviderV2V3, UiIncentiveDataProviderV2Factory, } from '../types'; import { withSaveAndVerify, registerContractInJsonDb, linkBytecode, insertContractAddressInDb, deployContract, verifyContract, getOptionalParamAddressPerNetwork, } from './contracts-helpers'; import { StableAndVariableTokensHelperFactory } from '../types/StableAndVariableTokensHelperFactory'; import { MintableDelegationERC20 } from '../types/MintableDelegationERC20'; import { readArtifact as buidlerReadArtifact } from '@nomiclabs/buidler/plugins'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { LendingPoolLibraryAddresses } from '../types/LendingPoolFactory'; import { UiPoolDataProvider } from '../types'; import { eNetwork } from './types'; export const deployUiIncentiveDataProviderV2 = async (verify?: boolean) => withSaveAndVerify( await new UiIncentiveDataProviderV2Factory(await getFirstSigner()).deploy(), eContractid.UiIncentiveDataProviderV2, [], verify ); export const deployUiIncentiveDataProviderV2V3 = async (verify?: boolean) => { const id = eContractid.UiIncentiveDataProviderV2V3; const instance = await deployContract(id, []); if (verify) { await verifyContract(id, instance, []); } return instance; }; export const deployUiPoolDataProviderV2 = async ( chainlinkAggregatorProxy: string, chainlinkEthUsdAggregatorProxy: string, verify?: boolean ) => withSaveAndVerify( await new UiPoolDataProviderV2Factory(await getFirstSigner()).deploy( chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy ), eContractid.UiPoolDataProvider, [chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy], verify ); export const deployUiPoolDataProviderV2V3 = async ( chainlinkAggregatorProxy: string, chainlinkEthUsdAggregatorProxy: string, verify?: boolean ) => withSaveAndVerify( await new UiPoolDataProviderV2V3Factory(await getFirstSigner()).deploy( chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy ), eContractid.UiPoolDataProvider, [chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy], verify ); export const deployUiPoolDataProvider = async ( [incentivesController, aaveOracle]: [tEthereumAddress, tEthereumAddress], verify?: boolean ) => { const id = eContractid.UiPoolDataProvider; const args: string[] = [incentivesController, aaveOracle]; const instance = await deployContract(id, args); if (verify) { await verifyContract(id, instance, args); } return instance; }; const readArtifact = async (id: string) => { if (DRE.network.name === eEthereumNetwork.buidlerevm) { return buidlerReadArtifact(DRE.config.paths.artifacts, id); } return (DRE as HardhatRuntimeEnvironment).artifacts.readArtifact(id); }; export const deployLendingPoolAddressesProvider = async (marketId: string, verify?: boolean) => withSaveAndVerify( await new LendingPoolAddressesProviderFactory(await getFirstSigner()).deploy(marketId), eContractid.LendingPoolAddressesProvider, [marketId], verify ); export const deployLendingPoolAddressesProviderRegistry = async (verify?: boolean) => withSaveAndVerify( await new LendingPoolAddressesProviderRegistryFactory(await getFirstSigner()).deploy(), eContractid.LendingPoolAddressesProviderRegistry, [], verify ); export const deployLendingPoolConfigurator = async (verify?: boolean) => { const lendingPoolConfiguratorImpl = await new LendingPoolConfiguratorFactory( await getFirstSigner() ).deploy(); await insertContractAddressInDb( eContractid.LendingPoolConfiguratorImpl, lendingPoolConfiguratorImpl.address ); return withSaveAndVerify( lendingPoolConfiguratorImpl, eContractid.LendingPoolConfigurator, [], verify ); }; export const deployReserveLogicLibrary = async (verify?: boolean) => withSaveAndVerify( await new ReserveLogicFactory(await getFirstSigner()).deploy(), eContractid.ReserveLogic, [], verify ); export const deployGenericLogic = async (reserveLogic: Contract, verify?: boolean) => { const genericLogicArtifact = await readArtifact(eContractid.GenericLogic); const linkedGenericLogicByteCode = linkBytecode(genericLogicArtifact, { [eContractid.ReserveLogic]: reserveLogic.address, }); const genericLogicFactory = await DRE.ethers.getContractFactory( genericLogicArtifact.abi, linkedGenericLogicByteCode ); const genericLogic = await ( await genericLogicFactory.connect(await getFirstSigner()).deploy() ).deployed(); return withSaveAndVerify(genericLogic, eContractid.GenericLogic, [], verify); }; export const deployValidationLogic = async ( reserveLogic: Contract, genericLogic: Contract, verify?: boolean ) => { const validationLogicArtifact = await readArtifact(eContractid.ValidationLogic); const linkedValidationLogicByteCode = linkBytecode(validationLogicArtifact, { [eContractid.ReserveLogic]: reserveLogic.address, [eContractid.GenericLogic]: genericLogic.address, }); const validationLogicFactory = await DRE.ethers.getContractFactory( validationLogicArtifact.abi, linkedValidationLogicByteCode ); const validationLogic = await ( await validationLogicFactory.connect(await getFirstSigner()).deploy() ).deployed(); return withSaveAndVerify(validationLogic, eContractid.ValidationLogic, [], verify); }; export const deployAaveLibraries = async ( verify?: boolean ): Promise => { const reserveLogic = await deployReserveLogicLibrary(verify); const genericLogic = await deployGenericLogic(reserveLogic, verify); const validationLogic = await deployValidationLogic(reserveLogic, genericLogic, verify); // Hardcoded solidity placeholders, if any library changes path this will fail. // The '__$PLACEHOLDER$__ can be calculated via solidity keccak, but the LendingPoolLibraryAddresses Type seems to // require a hardcoded string. // // how-to: // 1. PLACEHOLDER = solidityKeccak256(['string'], `${libPath}:${libName}`).slice(2, 36) // 2. LIB_PLACEHOLDER = `__$${PLACEHOLDER}$__` // or grab placeholdes from LendingPoolLibraryAddresses at Typechain generation. // // libPath example: contracts/libraries/logic/GenericLogic.sol // libName example: GenericLogic return { ['__$de8c0cf1a7d7c36c802af9a64fb9d86036$__']: validationLogic.address, ['__$22cd43a9dda9ce44e9b92ba393b88fb9ac$__']: reserveLogic.address, }; }; export const deployLendingPool = async (verify?: boolean) => { const libraries = await deployAaveLibraries(verify); const lendingPoolImpl = await new LendingPoolFactory(libraries, await getFirstSigner()).deploy(); await insertContractAddressInDb(eContractid.LendingPoolImpl, lendingPoolImpl.address); return withSaveAndVerify(lendingPoolImpl, eContractid.LendingPool, [], verify); }; export const deployPriceOracle = async (verify?: boolean) => withSaveAndVerify( await new PriceOracleFactory(await getFirstSigner()).deploy(), eContractid.PriceOracle, [], verify ); export const deployLendingRateOracle = async (verify?: boolean) => withSaveAndVerify( await new LendingRateOracleFactory(await getFirstSigner()).deploy(), eContractid.LendingRateOracle, [], verify ); export const deployMockAggregator = async (price: tStringTokenSmallUnits, verify?: boolean) => withSaveAndVerify( await new MockAggregatorFactory(await getFirstSigner()).deploy(price), eContractid.MockAggregator, [price], verify ); export const deployAaveOracle = async ( args: [tEthereumAddress[], tEthereumAddress[], tEthereumAddress, tEthereumAddress, string], verify?: boolean ) => withSaveAndVerify( await new AaveOracleFactory(await getFirstSigner()).deploy(...args), eContractid.AaveOracle, args, verify ); export const deployLendingPoolCollateralManager = async (verify?: boolean) => { const collateralManagerImpl = await new LendingPoolCollateralManagerFactory( await getFirstSigner() ).deploy(); await insertContractAddressInDb( eContractid.LendingPoolCollateralManagerImpl, collateralManagerImpl.address ); return withSaveAndVerify( collateralManagerImpl, eContractid.LendingPoolCollateralManager, [], verify ); }; export const deployInitializableAdminUpgradeabilityProxy = async (verify?: boolean) => withSaveAndVerify( await new InitializableAdminUpgradeabilityProxyFactory(await getFirstSigner()).deploy(), eContractid.InitializableAdminUpgradeabilityProxy, [], verify ); export const deployMockFlashLoanReceiver = async ( addressesProvider: tEthereumAddress, verify?: boolean ) => withSaveAndVerify( await new MockFlashLoanReceiverFactory(await getFirstSigner()).deploy(addressesProvider), eContractid.MockFlashLoanReceiver, [addressesProvider], verify ); export const deployWalletBalancerProvider = async (verify?: boolean) => withSaveAndVerify( await new WalletBalanceProviderFactory(await getFirstSigner()).deploy(), eContractid.WalletBalanceProvider, [], verify ); export const deployAaveProtocolDataProvider = async ( addressesProvider: tEthereumAddress, verify?: boolean ) => withSaveAndVerify( await new AaveProtocolDataProviderFactory(await getFirstSigner()).deploy(addressesProvider), eContractid.AaveProtocolDataProvider, [addressesProvider], verify ); export const deployMintableERC20 = async ( args: [string, string, string], verify?: boolean ): Promise => withSaveAndVerify( await new MintableERC20Factory(await getFirstSigner()).deploy(...args), eContractid.MintableERC20, args, verify ); export const deployMintableDelegationERC20 = async ( args: [string, string, string], verify?: boolean ): Promise => withSaveAndVerify( await new MintableDelegationERC20Factory(await getFirstSigner()).deploy(...args), eContractid.MintableDelegationERC20, args, verify ); export const deployDefaultReserveInterestRateStrategy = async ( args: [tEthereumAddress, string, string, string, string, string, string], verify: boolean ) => withSaveAndVerify( await new DefaultReserveInterestRateStrategyFactory(await getFirstSigner()).deploy(...args), eContractid.DefaultReserveInterestRateStrategy, args, verify ); export const deployStableDebtToken = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string], verify: boolean ) => { const instance = await withSaveAndVerify( await new StableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.StableDebtToken, [], verify ); await instance.initialize(args[0], args[1], args[2], '18', args[3], args[4], '0x10'); return instance; }; export const deployVariableDebtToken = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string], verify: boolean ) => { const instance = await withSaveAndVerify( await new VariableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.VariableDebtToken, [], verify ); await instance.initialize(args[0], args[1], args[2], '18', args[3], args[4], '0x10'); return instance; }; export const deployGenericStableDebtToken = async (verify?: boolean) => withSaveAndVerify( await new StableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.StableDebtToken, [], verify ); export const deployGenericVariableDebtToken = async (verify?: boolean) => withSaveAndVerify( await new VariableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.VariableDebtToken, [], verify ); export const deployGenericAToken = async ( [poolAddress, underlyingAssetAddress, treasuryAddress, incentivesController, name, symbol]: [ tEthereumAddress, tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string ], verify: boolean ) => { const instance = await withSaveAndVerify( await new ATokenFactory(await getFirstSigner()).deploy(), eContractid.AToken, [], verify ); await instance.initialize( poolAddress, treasuryAddress, underlyingAssetAddress, incentivesController, '18', name, symbol, '0x10' ); return instance; }; export const deployGenericATokenImpl = async (verify: boolean) => withSaveAndVerify( await new ATokenFactory(await getFirstSigner()).deploy(), eContractid.AToken, [], verify ); export const deployDelegationAwareAToken = async ( [pool, underlyingAssetAddress, treasuryAddress, incentivesController, name, symbol]: [ tEthereumAddress, tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string ], verify: boolean ) => { const instance = await withSaveAndVerify( await new DelegationAwareATokenFactory(await getFirstSigner()).deploy(), eContractid.DelegationAwareAToken, [], verify ); await instance.initialize( pool, treasuryAddress, underlyingAssetAddress, incentivesController, '18', name, symbol, '0x10' ); return instance; }; export const deployDelegationAwareATokenImpl = async (verify: boolean) => withSaveAndVerify( await new DelegationAwareATokenFactory(await getFirstSigner()).deploy(), eContractid.DelegationAwareAToken, [], verify ); export const deployAllMockTokens = async (verify?: boolean) => { const tokens: { [symbol: string]: MockContract | MintableERC20 } = {}; const protoConfigData = getReservesConfigByPool(AavePools.proto); for (const tokenSymbol of Object.keys(TokenContractId)) { let decimals = '18'; let configData = (protoConfigData)[tokenSymbol]; tokens[tokenSymbol] = await deployMintableERC20( [tokenSymbol, tokenSymbol, configData ? configData.reserveDecimals : decimals], verify ); await registerContractInJsonDb(tokenSymbol.toUpperCase(), tokens[tokenSymbol]); } return tokens; }; export const deployMockTokens = async (config: PoolConfiguration, verify?: boolean) => { const tokens: { [symbol: string]: MockContract | MintableERC20 } = {}; const defaultDecimals = 18; const configData = config.ReservesConfig; for (const tokenSymbol of Object.keys(configData)) { tokens[tokenSymbol] = await deployMintableERC20( [ tokenSymbol, tokenSymbol, configData[tokenSymbol as keyof iMultiPoolsAssets].reserveDecimals || defaultDecimals.toString(), ], verify ); await registerContractInJsonDb(tokenSymbol.toUpperCase(), tokens[tokenSymbol]); } return tokens; }; export const deployStableAndVariableTokensHelper = async ( args: [tEthereumAddress, tEthereumAddress], verify?: boolean ) => withSaveAndVerify( await new StableAndVariableTokensHelperFactory(await getFirstSigner()).deploy(...args), eContractid.StableAndVariableTokensHelper, args, verify ); export const deployATokensAndRatesHelper = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress], verify?: boolean ) => withSaveAndVerify( await new ATokensAndRatesHelperFactory(await getFirstSigner()).deploy(...args), eContractid.ATokensAndRatesHelper, args, verify ); export const deployWETHGateway = async (args: [tEthereumAddress], verify?: boolean) => withSaveAndVerify( await new WETHGatewayFactory(await getFirstSigner()).deploy(...args), eContractid.WETHGateway, args, verify ); export const authorizeWETHGateway = async ( wethGateWay: tEthereumAddress, lendingPool: tEthereumAddress ) => await new WETHGatewayFactory(await getFirstSigner()) .attach(wethGateWay) .authorizeLendingPool(lendingPool); export const deployMockStableDebtToken = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string, string], verify?: boolean ) => { const instance = await withSaveAndVerify( await new MockStableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.MockStableDebtToken, [], verify ); await instance.initialize(args[0], args[1], args[2], '18', args[3], args[4], args[5]); return instance; }; export const deployWETHMocked = async (verify?: boolean) => withSaveAndVerify( await new WETH9MockedFactory(await getFirstSigner()).deploy(), eContractid.WETHMocked, [], verify ); export const deployMockVariableDebtToken = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string, string], verify?: boolean ) => { const instance = await withSaveAndVerify( await new MockVariableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.MockVariableDebtToken, [], verify ); await instance.initialize(args[0], args[1], args[2], '18', args[3], args[4], args[5]); return instance; }; export const deployMockAToken = async ( args: [ tEthereumAddress, tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string, string ], verify?: boolean ) => { const instance = await withSaveAndVerify( await new MockATokenFactory(await getFirstSigner()).deploy(), eContractid.MockAToken, [], verify ); await instance.initialize(args[0], args[2], args[1], args[3], '18', args[4], args[5], args[6]); return instance; }; export const deploySelfdestructTransferMock = async (verify?: boolean) => withSaveAndVerify( await new SelfdestructTransferFactory(await getFirstSigner()).deploy(), eContractid.SelfdestructTransferMock, [], verify ); export const deployMockUniswapRouter = async (verify?: boolean) => withSaveAndVerify( await new MockUniswapV2Router02Factory(await getFirstSigner()).deploy(), eContractid.MockUniswapV2Router02, [], verify ); export const deployUniswapLiquiditySwapAdapter = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress], verify?: boolean ) => withSaveAndVerify( await new UniswapLiquiditySwapAdapterFactory(await getFirstSigner()).deploy(...args), eContractid.UniswapLiquiditySwapAdapter, args, verify ); export const deployUniswapRepayAdapter = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress], verify?: boolean ) => withSaveAndVerify( await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy(...args), eContractid.UniswapRepayAdapter, args, verify ); export const deployFlashLiquidationAdapter = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress], verify?: boolean ) => withSaveAndVerify( await new FlashLiquidationAdapterFactory(await getFirstSigner()).deploy(...args), eContractid.FlashLiquidationAdapter, args, verify ); export const chooseATokenDeployment = (id: eContractid) => { switch (id) { case eContractid.AToken: return deployGenericATokenImpl; case eContractid.DelegationAwareAToken: return deployDelegationAwareATokenImpl; default: throw Error(`Missing aToken implementation deployment script for: ${id}`); } }; export const deployATokenImplementations = async ( pool: ConfigNames, reservesConfig: { [key: string]: IReserveParams }, verify = false ) => { const poolConfig = loadPoolConfig(pool); const network = DRE.network.name; // Obtain the different AToken implementations of all reserves inside the Market config const aTokenImplementations = [ ...Object.entries(reservesConfig).reduce>((acc, [, entry]) => { acc.add(entry.aTokenImpl); return acc; }, new Set()), ]; for (let x = 0; x < aTokenImplementations.length; x++) { const aTokenAddress = getOptionalParamAddressPerNetwork( poolConfig[aTokenImplementations[x].toString()], network ); if (!notFalsyOrZeroAddress(aTokenAddress)) { const deployImplementationMethod = chooseATokenDeployment(aTokenImplementations[x]); console.log(`Deploying implementation`, aTokenImplementations[x]); await deployImplementationMethod(verify); } } // Debt tokens, for now all Market configs follows same implementations const genericStableDebtTokenAddress = getOptionalParamAddressPerNetwork( poolConfig.StableDebtTokenImplementation, network ); const geneticVariableDebtTokenAddress = getOptionalParamAddressPerNetwork( poolConfig.VariableDebtTokenImplementation, network ); if (!notFalsyOrZeroAddress(genericStableDebtTokenAddress)) { await deployGenericStableDebtToken(verify); } if (!notFalsyOrZeroAddress(geneticVariableDebtTokenAddress)) { await deployGenericVariableDebtToken(verify); } }; export const deployRateStrategy = async ( strategyName: string, args: [tEthereumAddress, string, string, string, string, string, string], verify: boolean ): Promise => { switch (strategyName) { default: return await ( await deployDefaultReserveInterestRateStrategy(args, verify) ).address; } }; export const deployMockParaSwapAugustus = async (verify?: boolean) => withSaveAndVerify( await new MockParaSwapAugustusFactory(await getFirstSigner()).deploy(), eContractid.MockParaSwapAugustus, [], verify ); export const deployMockParaSwapAugustusRegistry = async ( args: [tEthereumAddress], verify?: boolean ) => withSaveAndVerify( await new MockParaSwapAugustusRegistryFactory(await getFirstSigner()).deploy(...args), eContractid.MockParaSwapAugustusRegistry, args, verify ); export const deployParaSwapLiquiditySwapAdapter = async ( args: [tEthereumAddress, tEthereumAddress], verify?: boolean ) => withSaveAndVerify( await new ParaSwapLiquiditySwapAdapterFactory(await getFirstSigner()).deploy(...args), eContractid.ParaSwapLiquiditySwapAdapter, args, verify );