From d6ede6c87e55d413c4698ec7103630cc9b38e5a0 Mon Sep 17 00:00:00 2001 From: David Racero Date: Wed, 12 May 2021 17:09:49 +0200 Subject: [PATCH] feat: Added Rewards Aware ATokens tests and scripts. --- helpers/constants.ts | 1 + helpers/contracts-deployments.ts | 103 +++- helpers/contracts-getters.ts | 19 + helpers/contracts-helpers.ts | 37 +- helpers/init-helpers.ts | 145 ++--- helpers/types.ts | 8 +- markets/aave/reservesConfigs.ts | 49 +- tasks/dev/3_lending_pool.ts | 8 +- tasks/dev/5_initialize.ts | 8 +- tasks/full/2_lending_pool.ts | 2 + tasks/full/6-initialize.ts | 1 + tasks/helpers/deploy-new-asset.ts | 14 +- tasks/migrations/aave.dev.ts | 2 +- test-suites/test-aave/__setup.spec.ts | 35 +- test-suites/test-aave/helpers/make-suite.ts | 13 +- .../rewards-distribution/base-maths.ts | 21 + .../helpers/rewards-distribution/verify.ts | 112 ++++ .../test-aave/helpers/utils/calculations.ts | 32 +- .../helpers/utils/ray-math/bignumber.ts | 20 + .../test-aave/helpers/utils/ray-math/index.ts | 40 ++ .../helpers/utils/ray-math/ray-math.ts | 59 ++ .../test-aave/rewardAwareAToken.spec.ts | 521 ++++++++++++++++++ test-suites/test-amm/__setup.spec.ts | 27 +- 23 files changed, 1070 insertions(+), 207 deletions(-) create mode 100644 test-suites/test-aave/helpers/rewards-distribution/base-maths.ts create mode 100644 test-suites/test-aave/helpers/rewards-distribution/verify.ts create mode 100644 test-suites/test-aave/helpers/utils/ray-math/bignumber.ts create mode 100644 test-suites/test-aave/helpers/utils/ray-math/index.ts create mode 100644 test-suites/test-aave/helpers/utils/ray-math/ray-math.ts create mode 100644 test-suites/test-aave/rewardAwareAToken.spec.ts diff --git a/helpers/constants.ts b/helpers/constants.ts index 95e8922b..79c50814 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -70,4 +70,5 @@ export const MOCK_CHAINLINK_AGGREGATORS_PRICES = { STAKE: oneEther.multipliedBy('0.003620948469').toFixed(), xSUSHI: oneEther.multipliedBy('0.00913428586').toFixed(), USD: '5848466240000000', + REW: oneEther.multipliedBy('0.00137893825230').toFixed(), }; diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index 6ae58383..4aeb6d56 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -1,5 +1,5 @@ import { Contract } from 'ethers'; -import { DRE } from './misc-utils'; +import { DRE, notFalsyOrZeroAddress } from './misc-utils'; import { tEthereumAddress, eContractid, @@ -10,12 +10,12 @@ import { IReserveParams, PoolConfiguration, eEthereumNetwork, + eNetwork, } from './types'; import { MintableERC20 } from '../types/MintableERC20'; import { MockContract } from 'ethereum-waffle'; -import { getReservesConfigByPool } from './configuration'; +import { ConfigNames, getReservesConfigByPool, loadPoolConfig } from './configuration'; import { getFirstSigner } from './contracts-getters'; -import { ZERO_ADDRESS } from './constants'; import { AaveProtocolDataProviderFactory, ATokenFactory, @@ -49,6 +49,8 @@ import { WETH9MockedFactory, WETHGatewayFactory, FlashLiquidationAdapterFactory, + RewardsTokenFactory, + RewardsATokenMockFactory, } from '../types'; import { withSaveAndVerify, @@ -57,6 +59,8 @@ import { insertContractAddressInDb, deployContract, verifyContract, + getParamPerNetwork, + getOptionalParamAddressPerNetwork, } from './contracts-helpers'; import { StableAndVariableTokensHelperFactory } from '../types/StableAndVariableTokensHelperFactory'; import { MintableDelegationERC20 } from '../types/MintableDelegationERC20'; @@ -347,20 +351,20 @@ export const deployVariableDebtToken = async ( return instance; }; -export const deployGenericStableDebtToken = async () => +export const deployGenericStableDebtToken = async (verify?: boolean) => withSaveAndVerify( await new StableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.StableDebtToken, [], - false + verify ); -export const deployGenericVariableDebtToken = async () => +export const deployGenericVariableDebtToken = async (verify?: boolean) => withSaveAndVerify( await new VariableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.VariableDebtToken, [], - false + verify ); export const deployGenericAToken = async ( @@ -395,7 +399,7 @@ export const deployGenericAToken = async ( return instance; }; -export const deployGenericATokenImpl = async (verify: boolean) => +export const deployGenericATokenImpl = async (verify?: boolean) => withSaveAndVerify( await new ATokenFactory(await getFirstSigner()).deploy(), eContractid.AToken, @@ -435,7 +439,7 @@ export const deployDelegationAwareAToken = async ( return instance; }; -export const deployDelegationAwareATokenImpl = async (verify: boolean) => +export const deployDelegationAwareATokenImpl = async (verify?: boolean) => withSaveAndVerify( await new DelegationAwareATokenFactory(await getFirstSigner()).deploy(), eContractid.DelegationAwareAToken, @@ -473,8 +477,7 @@ export const deployMockTokens = async (config: PoolConfiguration, verify?: boole [ tokenSymbol, tokenSymbol, - configData[tokenSymbol as keyof iMultiPoolsAssets].reserveDecimals || - defaultDecimals.toString(), + configData[tokenSymbol]?.reserveDecimals || defaultDecimals.toString(), ], verify ); @@ -633,3 +636,81 @@ export const deployFlashLiquidationAdapter = async ( args, verify ); + +export const deployRewardsMockedToken = async (verify?: boolean) => + withSaveAndVerify( + await new RewardsTokenFactory(await getFirstSigner()).deploy(), + eContractid.RewardsToken, + [], + verify + ); + +export const deployRewardATokenMock = async (verify?: boolean) => { + return withSaveAndVerify( + await new RewardsATokenMockFactory(await getFirstSigner()).deploy(), + eContractid.RewardsATokenMock, + [], + verify + ); +}; + +export const chooseATokenDeployment = (id: eContractid) => { + switch (id) { + case eContractid.AToken: + return deployGenericATokenImpl; + case eContractid.DelegationAwareAToken: + return deployDelegationAwareATokenImpl; + case eContractid.RewardsATokenMock: + return deployRewardATokenMock; + default: + throw Error(`Missing aToken implementation deployment script for: ${id}`); + } +}; + +export const deployATokenImplementations = async ( + pool: ConfigNames, + reservesConfig: { [key: string]: IReserveParams }, + verify?: boolean +) => { + 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()), + ]; + + console.log(aTokenImplementations); + + 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); + } +}; diff --git a/helpers/contracts-getters.ts b/helpers/contracts-getters.ts index 41089e39..2ddbefbc 100644 --- a/helpers/contracts-getters.ts +++ b/helpers/contracts-getters.ts @@ -30,6 +30,8 @@ import { WETH9MockedFactory, WETHGatewayFactory, FlashLiquidationAdapterFactory, + RewardsATokenMockFactory, + RewardsTokenFactory, } from '../types'; import { IERC20DetailedFactory } from '../types/IERC20DetailedFactory'; import { MockTokenMap } from './contracts-helpers'; @@ -364,3 +366,20 @@ export const getFlashLiquidationAdapter = async (address?: tEthereumAddress) => .address, await getFirstSigner() ); + +export const getRewardsToken = async (address?: tEthereumAddress) => + await RewardsTokenFactory.connect( + address || + (await getDb().get(`${eContractid.RewardsToken}.${DRE.network.name}`).value()).address, + await getFirstSigner() + ); + +export const getRewardsATokenMock = async (address?: tEthereumAddress) => + await RewardsATokenMockFactory.connect( + address || + (await getDb().get(`${eContractid.RewardsATokenMock}.${DRE.network.name}`).value()).address, + await getFirstSigner() + ); + +export const getRewardsAToken = async (address: tEthereumAddress) => + await RewardsATokenMockFactory.connect(address, await getFirstSigner()); diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index 38894310..a7f1df03 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -2,7 +2,7 @@ import { Contract, Signer, utils, ethers, BigNumberish } from 'ethers'; import { signTypedData_v4 } from 'eth-sig-util'; import { fromRpcSig, ECDSASignature } from 'ethereumjs-util'; import BigNumber from 'bignumber.js'; -import { getDb, DRE, waitForTx } from './misc-utils'; +import { getDb, DRE, waitForTx, notFalsyOrZeroAddress } from './misc-utils'; import { tEthereumAddress, eContractid, @@ -23,9 +23,12 @@ import { MintableERC20 } from '../types/MintableERC20'; import { Artifact } from 'hardhat/types'; import { Artifact as BuidlerArtifact } from '@nomiclabs/buidler/types'; import { verifyEtherscanContract } from './etherscan-verification'; -import { getIErc20Detailed } from './contracts-getters'; +import { getFirstSigner, getIErc20Detailed } from './contracts-getters'; import { usingTenderly, verifyAtTenderly } from './tenderly-utils'; import { usingPolygon, verifyAtPolygon } from './polygon-utils'; +import { ConfigNames, loadPoolConfig } from './configuration'; +import { ZERO_ADDRESS } from './constants'; +import { RewardsTokenFactory, RewardsATokenMockFactory } from '../types'; export type MockTokenMap = { [symbol: string]: MintableERC20 }; @@ -173,6 +176,16 @@ export const getParamPerNetwork = (param: iParamsPerNetwork, network: eNet } }; +export const getOptionalParamAddressPerNetwork = ( + param: iParamsPerNetwork | undefined | null, + network: eNetwork +) => { + if (!param) { + return ZERO_ADDRESS; + } + return getParamPerNetwork(param, network); +}; + export const getParamPerPool = ({ proto, amm, matic }: iParamsPerPool, pool: AavePools) => { switch (pool) { case AavePools.proto: @@ -335,3 +348,23 @@ export const verifyContract = async ( } return instance; }; + +export const getContractAddressWithJsonFallback = async ( + id: string, + pool: ConfigNames +): Promise => { + const poolConfig = loadPoolConfig(pool); + const network = DRE.network.name; + const db = getDb(); + + const contractAtMarketConfig = getOptionalParamAddressPerNetwork(poolConfig[id], network); + if (notFalsyOrZeroAddress(contractAtMarketConfig)) { + return contractAtMarketConfig; + } + + const contractAtDb = await getDb().get(`${id}.${DRE.network.name}`).value(); + if (contractAtDb?.address) { + return contractAtDb.address as tEthereumAddress; + } + throw Error(`Missing contract address ${id} at Market config and JSON local db`); +}; diff --git a/helpers/init-helpers.ts b/helpers/init-helpers.ts index acf17c02..3cdb6796 100644 --- a/helpers/init-helpers.ts +++ b/helpers/init-helpers.ts @@ -1,48 +1,25 @@ import { eContractid, - eEthereumNetwork, eNetwork, iMultiPoolsAssets, IReserveParams, tEthereumAddress, } from './types'; import { AaveProtocolDataProvider } from '../types/AaveProtocolDataProvider'; -import { chunk, DRE, getDb, waitForTx } from './misc-utils'; +import { chunk, getDb, waitForTx } from './misc-utils'; import { - getAaveProtocolDataProvider, getAToken, getATokensAndRatesHelper, getLendingPoolAddressesProvider, getLendingPoolConfiguratorProxy, - getStableAndVariableTokensHelper, } from './contracts-getters'; -import { rawInsertContractAddressInDb } from './contracts-helpers'; -import { BigNumber, BigNumberish, Signer } from 'ethers'; import { - deployDefaultReserveInterestRateStrategy, - deployDelegationAwareAToken, - deployDelegationAwareATokenImpl, - deployGenericAToken, - deployGenericATokenImpl, - deployGenericStableDebtToken, - deployGenericVariableDebtToken, - deployStableDebtToken, - deployVariableDebtToken, -} from './contracts-deployments'; -import { ZERO_ADDRESS } from './constants'; -import { isZeroAddress } from 'ethereumjs-util'; -import { DefaultReserveInterestRateStrategy, DelegationAwareAToken } from '../types'; - -export const chooseATokenDeployment = (id: eContractid) => { - switch (id) { - case eContractid.AToken: - return deployGenericAToken; - case eContractid.DelegationAwareAToken: - return deployDelegationAwareAToken; - default: - throw Error(`Missing aToken deployment script for: ${id}`); - } -}; + getContractAddressWithJsonFallback, + rawInsertContractAddressInDb, +} from './contracts-helpers'; +import { BigNumber, BigNumberish, Signer } from 'ethers'; +import { deployDefaultReserveInterestRateStrategy } from './contracts-deployments'; +import { ConfigNames } from './configuration'; export const initReservesByHelper = async ( reservesParams: iMultiPoolsAssets, @@ -54,19 +31,16 @@ export const initReservesByHelper = async ( admin: tEthereumAddress, treasuryAddress: tEthereumAddress, incentivesController: tEthereumAddress, + poolName: ConfigNames, verify: boolean ): Promise => { let gasUsage = BigNumber.from('0'); - const stableAndVariableDeployer = await getStableAndVariableTokensHelper(); - const addressProvider = await getLendingPoolAddressesProvider(); // CHUNK CONFIGURATION const initChunks = 4; // Initialize variables for future reserves initialization - let reserveTokens: string[] = []; - let reserveInitDecimals: string[] = []; let reserveSymbols: string[] = []; let initInputParams: { @@ -99,49 +73,8 @@ export const initReservesByHelper = async ( ]; let rateStrategies: Record = {}; let strategyAddresses: Record = {}; - let strategyAddressPerAsset: Record = {}; - let aTokenType: Record = {}; - let delegationAwareATokenImplementationAddress = ''; - let aTokenImplementationAddress = ''; - let stableDebtTokenImplementationAddress = ''; - let variableDebtTokenImplementationAddress = ''; - // NOT WORKING ON MATIC, DEPLOYING INDIVIDUAL IMPLs INSTEAD - // const tx1 = await waitForTx( - // await stableAndVariableDeployer.initDeployment([ZERO_ADDRESS], ["1"]) - // ); - // console.log(tx1.events); - // tx1.events?.forEach((event, index) => { - // stableDebtTokenImplementationAddress = event?.args?.stableToken; - // variableDebtTokenImplementationAddress = event?.args?.variableToken; - // rawInsertContractAddressInDb(`stableDebtTokenImpl`, stableDebtTokenImplementationAddress); - // rawInsertContractAddressInDb(`variableDebtTokenImpl`, variableDebtTokenImplementationAddress); - // }); - //gasUsage = gasUsage.add(tx1.gasUsed); - stableDebtTokenImplementationAddress = await (await deployGenericStableDebtToken()).address; - variableDebtTokenImplementationAddress = await (await deployGenericVariableDebtToken()).address; - - const aTokenImplementation = await deployGenericATokenImpl(verify); - aTokenImplementationAddress = aTokenImplementation.address; - rawInsertContractAddressInDb(`aTokenImpl`, aTokenImplementationAddress); - - const delegatedAwareReserves = Object.entries(reservesParams).filter( - ([_, { aTokenImpl }]) => aTokenImpl === eContractid.DelegationAwareAToken - ) as [string, IReserveParams][]; - - if (delegatedAwareReserves.length > 0) { - const delegationAwareATokenImplementation = await deployDelegationAwareATokenImpl(verify); - delegationAwareATokenImplementationAddress = delegationAwareATokenImplementation.address; - rawInsertContractAddressInDb( - `delegationAwareATokenImpl`, - delegationAwareATokenImplementationAddress - ); - } - - const reserves = Object.entries(reservesParams).filter( - ([_, { aTokenImpl }]) => - aTokenImpl === eContractid.DelegationAwareAToken || aTokenImpl === eContractid.AToken - ) as [string, IReserveParams][]; + const reserves = Object.entries(reservesParams); for (let [symbol, params] of reserves) { const { strategy, aTokenImpl, reserveDecimals } = params; @@ -171,44 +104,30 @@ export const initReservesByHelper = async ( // and once under the actual `strategyASSET` key. rawInsertContractAddressInDb(strategy.name, strategyAddresses[strategy.name]); } - strategyAddressPerAsset[symbol] = strategyAddresses[strategy.name]; - console.log('Strategy address for asset %s: %s', symbol, strategyAddressPerAsset[symbol]); - - if (aTokenImpl === eContractid.AToken) { - aTokenType[symbol] = 'generic'; - } else if (aTokenImpl === eContractid.DelegationAwareAToken) { - aTokenType[symbol] = 'delegation aware'; - } - - reserveInitDecimals.push(reserveDecimals); - reserveTokens.push(tokenAddresses[symbol]); + // Prepare input parameters reserveSymbols.push(symbol); - } - - for (let i = 0; i < reserveSymbols.length; i++) { - let aTokenToUse: string; - if (aTokenType[reserveSymbols[i]] === 'generic') { - aTokenToUse = aTokenImplementationAddress; - } else { - aTokenToUse = delegationAwareATokenImplementationAddress; - } - initInputParams.push({ - aTokenImpl: aTokenToUse, - stableDebtTokenImpl: stableDebtTokenImplementationAddress, - variableDebtTokenImpl: variableDebtTokenImplementationAddress, - underlyingAssetDecimals: reserveInitDecimals[i], - interestRateStrategyAddress: strategyAddressPerAsset[reserveSymbols[i]], - underlyingAsset: reserveTokens[i], + aTokenImpl: await getContractAddressWithJsonFallback(aTokenImpl, poolName), + stableDebtTokenImpl: await getContractAddressWithJsonFallback( + eContractid.StableDebtToken, + poolName + ), + variableDebtTokenImpl: await getContractAddressWithJsonFallback( + eContractid.VariableDebtToken, + poolName + ), + underlyingAssetDecimals: reserveDecimals, + interestRateStrategyAddress: strategyAddresses[strategy.name], + underlyingAsset: tokenAddresses[symbol], treasury: treasuryAddress, - incentivesController, - underlyingAssetName: reserveSymbols[i], - aTokenName: `${aTokenNamePrefix} ${reserveSymbols[i]}`, - aTokenSymbol: `a${symbolPrefix}${reserveSymbols[i]}`, - variableDebtTokenName: `${variableDebtTokenNamePrefix} ${symbolPrefix}${reserveSymbols[i]}`, - variableDebtTokenSymbol: `variableDebt${symbolPrefix}${reserveSymbols[i]}`, - stableDebtTokenName: `${stableDebtTokenNamePrefix} ${reserveSymbols[i]}`, - stableDebtTokenSymbol: `stableDebt${symbolPrefix}${reserveSymbols[i]}`, + incentivesController: incentivesController, + underlyingAssetName: symbol, + aTokenName: `${aTokenNamePrefix} ${symbol}`, + aTokenSymbol: `a${symbolPrefix}${symbol}`, + variableDebtTokenName: `${variableDebtTokenNamePrefix} ${symbolPrefix}${symbol}`, + variableDebtTokenSymbol: `variableDebt${symbolPrefix}${symbol}`, + stableDebtTokenName: `${stableDebtTokenNamePrefix} ${symbol}`, + stableDebtTokenSymbol: `stableDebt${symbolPrefix}${symbol}`, params: '0x10', }); } @@ -218,7 +137,6 @@ export const initReservesByHelper = async ( const chunkedInitInputParams = chunk(initInputParams, initChunks); const configurator = await getLendingPoolConfiguratorProxy(); - //await waitForTx(await addressProvider.setPoolAdmin(admin)); console.log(`- Reserves initialization in ${chunkedInitInputParams.length} txs`); for (let chunkIndex = 0; chunkIndex < chunkedInitInputParams.length; chunkIndex++) { @@ -228,10 +146,9 @@ export const initReservesByHelper = async ( console.log(` - Reserve ready for: ${chunkedSymbols[chunkIndex].join(', ')}`); console.log(' * gasUsed', tx3.gasUsed.toString()); - //gasUsage = gasUsage.add(tx3.gasUsed); } - return gasUsage; // Deprecated + return gasUsage; }; export const getPairsTokenAggregator = ( diff --git a/helpers/types.ts b/helpers/types.ts index 267b5abb..8b210516 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -87,6 +87,8 @@ export enum eContractid { UniswapLiquiditySwapAdapter = 'UniswapLiquiditySwapAdapter', UniswapRepayAdapter = 'UniswapRepayAdapter', FlashLiquidationAdapter = 'FlashLiquidationAdapter', + RewardsATokenMock = 'RewardsATokenMock', + RewardsToken = 'RewardsToken', } /* @@ -237,8 +239,9 @@ export interface iAssetBase { BptWBTCWETH: T; BptBALWETH: T; WMATIC: T; - STAKE: T; xSUSHI: T; + STAKE: T; + REW: T; } export type iAssetsWithoutETH = Omit, 'ETH'>; @@ -352,6 +355,7 @@ export enum TokenContractId { WMATIC = 'WMATIC', STAKE = 'STAKE', xSUSHI = 'xSUSHI', + REW = 'REW', } export interface IReserveParams extends IReserveBorrowParams, IReserveCollateralParams { @@ -494,6 +498,8 @@ export interface ICommonConfiguration { WethGateway: iParamsPerNetwork; ReserveFactorTreasuryAddress: iParamsPerNetwork; IncentivesController: iParamsPerNetwork; + StableDebtTokenImplementation?: iParamsPerNetwork; + VariableDebtTokenImplementation?: iParamsPerNetwork; } export interface IAaveConfiguration extends ICommonConfiguration { diff --git a/markets/aave/reservesConfigs.ts b/markets/aave/reservesConfigs.ts index a29e16d7..111d841f 100644 --- a/markets/aave/reservesConfigs.ts +++ b/markets/aave/reservesConfigs.ts @@ -1,6 +1,6 @@ import { eContractid, IReserveParams } from '../../helpers/types'; -import { +import { rateStrategyStableOne, rateStrategyStableTwo, rateStrategyStableThree, @@ -21,7 +21,7 @@ export const strategyBUSD: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', }; export const strategyDAI: IReserveParams = { @@ -33,7 +33,7 @@ export const strategyDAI: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', }; export const strategySUSD: IReserveParams = { @@ -45,7 +45,7 @@ export const strategySUSD: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyTUSD: IReserveParams = { @@ -57,7 +57,7 @@ export const strategyTUSD: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', }; export const strategyUSDC: IReserveParams = { @@ -69,7 +69,7 @@ export const strategyUSDC: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '6', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', }; export const strategyUSDT: IReserveParams = { @@ -81,7 +81,7 @@ export const strategyUSDT: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '6', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', }; export const strategyAAVE: IReserveParams = { @@ -93,7 +93,7 @@ export const strategyAAVE: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '0' + reserveFactor: '0', }; export const strategyBAT: IReserveParams = { @@ -105,7 +105,7 @@ export const strategyBAT: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyENJ: IReserveParams = { @@ -117,7 +117,7 @@ export const strategyENJ: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyWETH: IReserveParams = { @@ -129,7 +129,7 @@ export const strategyWETH: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', }; export const strategyKNC: IReserveParams = { @@ -141,7 +141,7 @@ export const strategyKNC: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyLINK: IReserveParams = { @@ -153,7 +153,7 @@ export const strategyLINK: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyMANA: IReserveParams = { @@ -165,7 +165,7 @@ export const strategyMANA: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '3500' + reserveFactor: '3500', }; export const strategyMKR: IReserveParams = { @@ -177,7 +177,7 @@ export const strategyMKR: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyREN: IReserveParams = { @@ -189,7 +189,7 @@ export const strategyREN: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategySNX: IReserveParams = { @@ -201,7 +201,7 @@ export const strategySNX: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '3500' + reserveFactor: '3500', }; // Invalid borrow rates in params currently, replaced with snx params @@ -214,7 +214,7 @@ export const strategyUNI: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.DelegationAwareAToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyWBTC: IReserveParams = { @@ -226,7 +226,7 @@ export const strategyWBTC: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '8', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyYFI: IReserveParams = { @@ -238,7 +238,7 @@ export const strategyYFI: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyZRX: IReserveParams = { @@ -250,7 +250,7 @@ export const strategyZRX: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', }; export const strategyXSUSHI: IReserveParams = { @@ -263,4 +263,9 @@ export const strategyXSUSHI: IReserveParams = { reserveDecimals: '18', aTokenImpl: eContractid.AToken, reserveFactor: '3500', -}; \ No newline at end of file +}; + +export const strategyMockedRewardAwareToken: IReserveParams = { + ...strategyBAT, + aTokenImpl: eContractid.RewardsATokenMock, +}; diff --git a/tasks/dev/3_lending_pool.ts b/tasks/dev/3_lending_pool.ts index 9936abfa..5f4336c6 100644 --- a/tasks/dev/3_lending_pool.ts +++ b/tasks/dev/3_lending_pool.ts @@ -1,5 +1,6 @@ import { task } from 'hardhat/config'; import { + deployATokenImplementations, deployATokensAndRatesHelper, deployLendingPool, deployLendingPoolConfigurator, @@ -13,13 +14,15 @@ import { getLendingPoolConfiguratorProxy, } from '../../helpers/contracts-getters'; import { insertContractAddressInDb } from '../../helpers/contracts-helpers'; +import { ConfigNames, loadPoolConfig } from '../../helpers/configuration'; task('dev:deploy-lending-pool', 'Deploy lending pool for dev enviroment') .addFlag('verify', 'Verify contracts at Etherscan') - .setAction(async ({ verify }, localBRE) => { + .addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`) + .setAction(async ({ verify, pool }, localBRE) => { await localBRE.run('set-DRE'); - const addressesProvider = await getLendingPoolAddressesProvider(); + const poolConfig = loadPoolConfig(pool); const lendingPoolImpl = await deployLendingPool(verify); @@ -55,4 +58,5 @@ task('dev:deploy-lending-pool', 'Deploy lending pool for dev enviroment') [lendingPoolProxy.address, addressesProvider.address, lendingPoolConfiguratorProxy.address], verify ); + await deployATokenImplementations(pool, poolConfig.ReservesConfig, verify); }); diff --git a/tasks/dev/5_initialize.ts b/tasks/dev/5_initialize.ts index a1ad9970..b6c2ee01 100644 --- a/tasks/dev/5_initialize.ts +++ b/tasks/dev/5_initialize.ts @@ -41,6 +41,7 @@ task('dev:initialize-lending-pool', 'Initialize lending pool configuration.') VariableDebtTokenNamePrefix, SymbolPrefix, WethGateway, + ReservesConfig, } = poolConfig; const mockTokens = await getAllMockedTokens(); const allTokenAddresses = getAllTokenAddresses(mockTokens); @@ -53,14 +54,12 @@ task('dev:initialize-lending-pool', 'Initialize lending pool configuration.') const testHelpers = await deployAaveProtocolDataProvider(addressesProvider.address, verify); - const reservesParams = getReservesConfigByPool(AavePools.proto); - const admin = await addressesProvider.getPoolAdmin(); const treasuryAddress = await getTreasuryAddress(poolConfig); await initReservesByHelper( - reservesParams, + ReservesConfig, protoPoolReservesAddresses, ATokenNamePrefix, StableDebtTokenNamePrefix, @@ -69,9 +68,10 @@ task('dev:initialize-lending-pool', 'Initialize lending pool configuration.') admin, treasuryAddress, ZERO_ADDRESS, + pool, verify ); - await configureReservesByHelper(reservesParams, protoPoolReservesAddresses, testHelpers, admin); + await configureReservesByHelper(ReservesConfig, protoPoolReservesAddresses, testHelpers, admin); const collateralManager = await deployLendingPoolCollateralManager(verify); await waitForTx( diff --git a/tasks/full/2_lending_pool.ts b/tasks/full/2_lending_pool.ts index 1d7a9b4a..27c9251a 100644 --- a/tasks/full/2_lending_pool.ts +++ b/tasks/full/2_lending_pool.ts @@ -1,6 +1,7 @@ import { task } from 'hardhat/config'; import { getParamPerNetwork, insertContractAddressInDb } from '../../helpers/contracts-helpers'; import { + deployATokenImplementations, deployATokensAndRatesHelper, deployLendingPool, deployLendingPoolConfigurator, @@ -78,6 +79,7 @@ task('full:deploy-lending-pool', 'Deploy lending pool for dev enviroment') [lendingPoolProxy.address, addressesProvider.address, lendingPoolConfiguratorProxy.address], verify ); + await deployATokenImplementations(pool, poolConfig.ReservesConfig, verify); } catch (error) { if (DRE.network.name.includes('tenderly')) { const transactionLink = `https://dashboard.tenderly.co/${DRE.config.tenderly.username}/${ diff --git a/tasks/full/6-initialize.ts b/tasks/full/6-initialize.ts index bfe2925e..f315f36b 100644 --- a/tasks/full/6-initialize.ts +++ b/tasks/full/6-initialize.ts @@ -66,6 +66,7 @@ task('full:initialize-lending-pool', 'Initialize lending pool configuration.') admin, treasuryAddress, incentivesController, + pool, verify ); await configureReservesByHelper(ReservesConfig, reserveAssets, testHelpers, admin); diff --git a/tasks/helpers/deploy-new-asset.ts b/tasks/helpers/deploy-new-asset.ts index 718b6372..69066ac3 100644 --- a/tasks/helpers/deploy-new-asset.ts +++ b/tasks/helpers/deploy-new-asset.ts @@ -3,9 +3,9 @@ import { eEthereumNetwork } from '../../helpers/types'; import { getTreasuryAddress } from '../../helpers/configuration'; import * as marketConfigs from '../../markets/aave'; import * as reserveConfigs from '../../markets/aave/reservesConfigs'; -import { chooseATokenDeployment } from '../../helpers/init-helpers'; import { getLendingPoolAddressesProvider } from './../../helpers/contracts-getters'; import { + chooseATokenDeployment, deployDefaultReserveInterestRateStrategy, deployStableDebtToken, deployVariableDebtToken, @@ -48,17 +48,7 @@ WRONG RESERVE ASSET SETUP: ); const poolAddress = await addressProvider.getLendingPool(); const treasuryAddress = await getTreasuryAddress(marketConfigs.AaveConfig); - const aToken = await deployCustomAToken( - [ - poolAddress, - reserveAssetAddress, - treasuryAddress, - ZERO_ADDRESS, // Incentives Controller - `Aave interest bearing ${symbol}`, - `a${symbol}`, - ], - verify - ); + const aToken = await deployCustomAToken(verify); const stableDebt = await deployStableDebtToken( [ poolAddress, diff --git a/tasks/migrations/aave.dev.ts b/tasks/migrations/aave.dev.ts index 16ff3a6a..2f09154e 100644 --- a/tasks/migrations/aave.dev.ts +++ b/tasks/migrations/aave.dev.ts @@ -24,7 +24,7 @@ task('aave:dev', 'Deploy development enviroment') await localBRE.run('dev:deploy-address-provider', { verify }); console.log('3. Deploy lending pool'); - await localBRE.run('dev:deploy-lending-pool', { verify }); + await localBRE.run('dev:deploy-lending-pool', { verify, pool: POOL_NAME }); console.log('4. Deploy oracles'); await localBRE.run('dev:deploy-oracles', { verify, pool: POOL_NAME }); diff --git a/test-suites/test-aave/__setup.spec.ts b/test-suites/test-aave/__setup.spec.ts index 831b5231..a223c3a5 100644 --- a/test-suites/test-aave/__setup.spec.ts +++ b/test-suites/test-aave/__setup.spec.ts @@ -27,8 +27,9 @@ import { deployUniswapRepayAdapter, deployFlashLiquidationAdapter, authorizeWETHGateway, + deployATokenImplementations, + deployRewardsMockedToken, } from '../../helpers/contracts-deployments'; -import { eEthereumNetwork } from '../../helpers/types'; import { Signer } from 'ethers'; import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../../helpers/types'; import { MintableERC20 } from '../../types/MintableERC20'; @@ -55,11 +56,12 @@ import { getPairsTokenAggregator, } from '../../helpers/contracts-getters'; import { WETH9Mocked } from '../../types/WETH9Mocked'; +import { strategyMockedRewardAwareToken } from '../../markets/aave/reservesConfigs'; +import { RewardsToken } from '../../types'; const MOCK_USD_PRICE_IN_WEI = AaveConfig.ProtocolGlobalParams.MockUsdPriceInWei; const ALL_ASSETS_INITIAL_PRICES = AaveConfig.Mocks.AllAssetsInitialPrices; const USD_ADDRESS = AaveConfig.ProtocolGlobalParams.UsdAddress; -const MOCK_CHAINLINK_AGGREGATORS_PRICES = AaveConfig.Mocks.AllAssetsInitialPrices; const LENDING_RATE_ORACLE_RATES_COMMON = AaveConfig.LendingRateOracleRatesCommon; const deployAllMockTokens = async (deployer: Signer) => { @@ -95,9 +97,14 @@ const deployAllMockTokens = async (deployer: Signer) => { const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { console.time('setup'); const aaveAdmin = await deployer.getAddress(); + const config = loadPoolConfig(ConfigNames.Aave); - const mockTokens = await deployAllMockTokens(deployer); - console.log('Deployed mocks'); + const mockTokens: { + [symbol: string]: MockContract | MintableERC20 | WETH9Mocked | RewardsToken; + } = { + ...(await deployAllMockTokens(deployer)), + REW: await deployRewardsMockedToken(), + }; const addressesProvider = await deployLendingPoolAddressesProvider(AaveConfig.MarketId); await waitForTx(await addressesProvider.setPoolAdmin(aaveAdmin)); @@ -191,13 +198,13 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { WMATIC: mockTokens.WMATIC.address, USD: USD_ADDRESS, STAKE: mockTokens.STAKE.address, - xSUSHI: mockTokens.xSUSHI.address + xSUSHI: mockTokens.xSUSHI.address, + REW: mockTokens.REW.address, }, fallbackOracle ); - const mockAggregators = await deployAllMockAggregators(MOCK_CHAINLINK_AGGREGATORS_PRICES); - console.log('Mock aggs deployed'); + const mockAggregators = await deployAllMockAggregators(ALL_ASSETS_INITIAL_PRICES); const allTokenAddresses = Object.entries(mockTokens).reduce( (accum: { [tokenSymbol: string]: tEthereumAddress }, [tokenSymbol, tokenContract]) => ({ ...accum, @@ -232,17 +239,18 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { aaveAdmin ); - const reservesParams = getReservesConfigByPool(AavePools.proto); + // Reserve params from AAVE pool + mocked tokens + const reservesParams = { + ...config.ReservesConfig, + REW: strategyMockedRewardAwareToken, + }; const testHelpers = await deployAaveProtocolDataProvider(addressesProvider.address); - await insertContractAddressInDb(eContractid.AaveProtocolDataProvider, testHelpers.address); + await deployATokenImplementations(ConfigNames.Aave, reservesParams, false); + const admin = await deployer.getAddress(); - console.log('Initialize configuration'); - - const config = loadPoolConfig(ConfigNames.Aave); - const { ATokenNamePrefix, StableDebtTokenNamePrefix, @@ -261,6 +269,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { admin, treasuryAddress, ZERO_ADDRESS, + ConfigNames.Aave, false ); diff --git a/test-suites/test-aave/helpers/make-suite.ts b/test-suites/test-aave/helpers/make-suite.ts index e56358be..99898108 100644 --- a/test-suites/test-aave/helpers/make-suite.ts +++ b/test-suites/test-aave/helpers/make-suite.ts @@ -14,6 +14,8 @@ import { getUniswapLiquiditySwapAdapter, getUniswapRepayAdapter, getFlashLiquidationAdapter, + getRewardsToken, + getRewardsATokenMock, } from '../../../helpers/contracts-getters'; import { eEthereumNetwork, tEthereumAddress } from '../../../helpers/types'; import { LendingPool } from '../../../types/LendingPool'; @@ -37,7 +39,7 @@ import { WETH9Mocked } from '../../../types/WETH9Mocked'; import { WETHGateway } from '../../../types/WETHGateway'; import { solidity } from 'ethereum-waffle'; import { AaveConfig } from '../../../markets/aave'; -import { FlashLiquidationAdapter } from '../../../types'; +import { FlashLiquidationAdapter, RewardsATokenMock, RewardsToken } from '../../../types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { usingTenderly } from '../../../helpers/tenderly-utils'; @@ -62,6 +64,8 @@ export interface TestEnv { aDai: AToken; usdc: MintableERC20; aave: MintableERC20; + rew: RewardsToken; + aRew: RewardsATokenMock; addressesProvider: LendingPoolAddressesProvider; uniswapLiquiditySwapAdapter: UniswapLiquiditySwapAdapter; uniswapRepayAdapter: UniswapRepayAdapter; @@ -88,6 +92,8 @@ const testEnv: TestEnv = { aDai: {} as AToken, usdc: {} as MintableERC20, aave: {} as MintableERC20, + rew: {} as RewardsToken, + aRew: {} as RewardsATokenMock, addressesProvider: {} as LendingPoolAddressesProvider, uniswapLiquiditySwapAdapter: {} as UniswapLiquiditySwapAdapter, uniswapRepayAdapter: {} as UniswapRepayAdapter, @@ -129,7 +135,7 @@ export async function initializeMakeSuite() { const allTokens = await testEnv.helpersContract.getAllATokens(); const aDaiAddress = allTokens.find((aToken) => aToken.symbol === 'aDAI')?.tokenAddress; - + const aRewAddress = allTokens.find((aToken) => aToken.symbol === 'aREW')?.tokenAddress; const aWEthAddress = allTokens.find((aToken) => aToken.symbol === 'aWETH')?.tokenAddress; const reservesTokens = await testEnv.helpersContract.getAllReservesTokens(); @@ -138,6 +144,7 @@ export async function initializeMakeSuite() { const usdcAddress = reservesTokens.find((token) => token.symbol === 'USDC')?.tokenAddress; const aaveAddress = reservesTokens.find((token) => token.symbol === 'AAVE')?.tokenAddress; const wethAddress = reservesTokens.find((token) => token.symbol === 'WETH')?.tokenAddress; + const rewAddress = reservesTokens.find((token) => token.symbol === 'REW')?.tokenAddress; if (!aDaiAddress || !aWEthAddress) { process.exit(1); @@ -154,6 +161,8 @@ export async function initializeMakeSuite() { testEnv.aave = await getMintableERC20(aaveAddress); testEnv.weth = await getWETHMocked(wethAddress); testEnv.wethGateway = await getWETHGateway(); + testEnv.rew = await getRewardsToken(rewAddress); + testEnv.aRew = await getRewardsATokenMock(aRewAddress); testEnv.uniswapLiquiditySwapAdapter = await getUniswapLiquiditySwapAdapter(); testEnv.uniswapRepayAdapter = await getUniswapRepayAdapter(); diff --git a/test-suites/test-aave/helpers/rewards-distribution/base-maths.ts b/test-suites/test-aave/helpers/rewards-distribution/base-maths.ts new file mode 100644 index 00000000..a7429d63 --- /dev/null +++ b/test-suites/test-aave/helpers/rewards-distribution/base-maths.ts @@ -0,0 +1,21 @@ +import { BigNumber } from 'ethers'; + +import { + BigNumberValue, + ethersValueToZDBigNumber, + valueToZDBigNumber, +} from '../utils/ray-math/bignumber'; + +export function getRewards( + balance: BigNumber, + assetIndex: BigNumber, + userIndex: BigNumber, + precision: number = 18 +): BigNumber { + return BigNumber.from( + ethersValueToZDBigNumber(balance) + .multipliedBy(ethersValueToZDBigNumber(assetIndex).minus(ethersValueToZDBigNumber(userIndex))) + .dividedBy(valueToZDBigNumber(10).exponentiatedBy(precision)) + .toString() + ); +} diff --git a/test-suites/test-aave/helpers/rewards-distribution/verify.ts b/test-suites/test-aave/helpers/rewards-distribution/verify.ts new file mode 100644 index 00000000..a9d381eb --- /dev/null +++ b/test-suites/test-aave/helpers/rewards-distribution/verify.ts @@ -0,0 +1,112 @@ +import { expect } from 'chai'; +import { calcExpectedRewards } from '../utils/calculations'; +import { SignerWithAddress } from '../make-suite'; +import { ZERO_ADDRESS } from '../../../../helpers/constants'; +import { tEthereumAddress } from '../../../../helpers/types'; +import { IERC20Factory } from '../../../../types/IERC20Factory'; +import { getRewardsAToken } from '../../../../helpers/contracts-getters'; +import { BigNumber as EthersBigNumber } from '@ethersproject/bignumber'; +import BigNumber from 'bignumber.js'; +import '../utils/math'; + +export const checkRewards = async ( + user: SignerWithAddress, + aToken: tEthereumAddress, + block: number, + shouldReward?: boolean, + claimedToken?: tEthereumAddress, + beforeBalanceClaimedToken?: EthersBigNumber +) => { + const rewardAwareToken = await getRewardsAToken(aToken); + const rewardsAvailable = await rewardAwareToken.getRewardsTokenAddressList(); + const userBalance = await rewardAwareToken.balanceOf(user.address, { blockTag: block - 1 }); + const totalRewardsBefore = new Array(rewardsAvailable.length); + const userRewardsBefore = new Array(rewardsAvailable.length); + const userIndexesBefore = new Array(rewardsAvailable.length); + + const totalRewardsAfter = new Array(rewardsAvailable.length); + const userRewardsAfter = new Array(rewardsAvailable.length); + const userIndexesAfter = new Array(rewardsAvailable.length); + const userExpectedRewards = new Array(rewardsAvailable.length); + + for (let i = 0; i < rewardsAvailable.length; i++) { + if (rewardsAvailable[i] == ZERO_ADDRESS) break; + // Before action + + totalRewardsBefore[i] = await rewardAwareToken.getLifetimeRewards(rewardsAvailable[i], { + blockTag: block - 1, + }); + + userRewardsBefore[i] = await rewardAwareToken.getUserRewardsAccrued( + rewardsAvailable[i], + user.address, + { + blockTag: block - 1, + } + ); + + userIndexesBefore[i] = await rewardAwareToken.getUserIndex(rewardsAvailable[i], user.address, { + blockTag: block - 1, + }); + // After action + totalRewardsAfter[i] = await rewardAwareToken.getLifetimeRewards(rewardsAvailable[i], { + blockTag: block, + }); + + userRewardsAfter[i] = await rewardAwareToken.getUserRewardsAccrued( + rewardsAvailable[i], + user.address, + { + blockTag: block, + } + ); + + userIndexesAfter[i] = await rewardAwareToken.getUserIndex(rewardsAvailable[i], user.address, { + blockTag: block, + }); + + userExpectedRewards[i] = calcExpectedRewards( + userBalance, + userIndexesAfter[i], + userIndexesBefore[i] + ); + + // Explicit check rewards when the test case expects rewards to the user + if (shouldReward) { + expect(userRewardsAfter[i]).to.be.gt('0'); + expect(userRewardsAfter[i]).to.eq( + userRewardsBefore[i].add(userExpectedRewards[i]), + `User rewards for token ${rewardsAvailable[i]} does not match` + ); + if (beforeBalanceClaimedToken && rewardsAvailable[i] === claimedToken) { + const reserveFactor = await rewardAwareToken.getRewardsReserveFactor(); + const totalRewards = userRewardsBefore[i].add(userExpectedRewards[i]); + const priorClaimed = await rewardAwareToken.getUserClaimedRewards( + claimedToken, + user.address, + { + blockTag: block - 1, + } + ); + const totalClaim = totalRewards.sub(priorClaimed); + const treasureRewards = EthersBigNumber.from( + new BigNumber(totalClaim.toString()) + .percentMul(new BigNumber(reserveFactor.toString())) + .toString() + ); + const userRewards = totalClaim.sub(treasureRewards); + + const afterBalance = await IERC20Factory.connect(claimedToken, user.signer).balanceOf( + user.address + ); + + expect(afterBalance).to.be.eq(beforeBalanceClaimedToken.add(userRewards)); + } + } else { + expect(userExpectedRewards[i]).to.be.eq('0', 'This action should not reward'); + expect(userRewardsBefore[i]).to.be.eq(userRewardsAfter[i], 'Rewards should stay the same'); + } + } + + return; +}; diff --git a/test-suites/test-aave/helpers/utils/calculations.ts b/test-suites/test-aave/helpers/utils/calculations.ts index de3d31f9..cdc544d7 100644 --- a/test-suites/test-aave/helpers/utils/calculations.ts +++ b/test-suites/test-aave/helpers/utils/calculations.ts @@ -1,14 +1,9 @@ import BigNumber from 'bignumber.js'; +import { BigNumber as BigNumberEthers } from 'ethers'; import { ONE_YEAR, RAY, MAX_UINT_AMOUNT, PERCENTAGE_FACTOR } from '../../../../helpers/constants'; -import { - IReserveParams, - iAavePoolAssets, - RateMode, - tEthereumAddress, -} from '../../../../helpers/types'; +import { IReserveParams, iAavePoolAssets, RateMode } from '../../../../helpers/types'; import './math'; import { ReserveData, UserReserveData } from './interfaces'; -import { expect } from 'chai'; export const strToBN = (amount: string): BigNumber => new BigNumber(amount); @@ -1244,7 +1239,9 @@ export const calcExpectedInterestRates = ( ]; let stableBorrowRate: BigNumber = marketStableRate; - let variableBorrowRate: BigNumber = new BigNumber(reserveConfiguration.strategy.baseVariableBorrowRate); + let variableBorrowRate: BigNumber = new BigNumber( + reserveConfiguration.strategy.baseVariableBorrowRate + ); const optimalRate = new BigNumber(reserveConfiguration.strategy.optimalUtilizationRate); const excessRate = new BigNumber(RAY).minus(optimalRate); @@ -1256,13 +1253,17 @@ export const calcExpectedInterestRates = ( stableBorrowRate = stableBorrowRate .plus(reserveConfiguration.strategy.stableRateSlope1) .plus( - new BigNumber(reserveConfiguration.strategy.stableRateSlope2).rayMul(excessUtilizationRateRatio) + new BigNumber(reserveConfiguration.strategy.stableRateSlope2).rayMul( + excessUtilizationRateRatio + ) ); variableBorrowRate = variableBorrowRate .plus(reserveConfiguration.strategy.variableRateSlope1) .plus( - new BigNumber(reserveConfiguration.strategy.variableRateSlope2).rayMul(excessUtilizationRateRatio) + new BigNumber(reserveConfiguration.strategy.variableRateSlope2).rayMul( + excessUtilizationRateRatio + ) ); } else { stableBorrowRate = stableBorrowRate.plus( @@ -1433,3 +1434,14 @@ const calcExpectedTotalVariableDebt = ( ) => { return reserveData.scaledVariableDebt.rayMul(expectedVariableDebtIndex); }; + +export function calcExpectedRewards( + balance: BigNumberEthers, + userIndexAfter: BigNumberEthers, + userIndexBefore: BigNumberEthers, + precision: number = 24 +): BigNumberEthers { + return balance + .mul(userIndexAfter.sub(userIndexBefore)) + .div(BigNumberEthers.from(10).pow(precision)); +} diff --git a/test-suites/test-aave/helpers/utils/ray-math/bignumber.ts b/test-suites/test-aave/helpers/utils/ray-math/bignumber.ts new file mode 100644 index 00000000..08263a27 --- /dev/null +++ b/test-suites/test-aave/helpers/utils/ray-math/bignumber.ts @@ -0,0 +1,20 @@ +import BigNumber from 'bignumber.js'; +import { BigNumber as BigNumberEthers, BigNumberish } from 'ethers'; + +export type BigNumberValue = string | number | BigNumber | BigNumberEthers | BigNumberish; + +export const BigNumberZD = BigNumber.clone({ + DECIMAL_PLACES: 0, + ROUNDING_MODE: BigNumber.ROUND_DOWN, +}); + +export function valueToBigNumber(amount: BigNumberValue): BigNumber { + return new BigNumber(amount.toString()); +} +export function valueToZDBigNumber(amount: BigNumberValue): BigNumber { + return new BigNumberZD(amount.toString()); +} + +export function ethersValueToZDBigNumber(amount: BigNumberEthers): BigNumber { + return new BigNumberZD('0x' + amount.toHexString()); +} diff --git a/test-suites/test-aave/helpers/utils/ray-math/index.ts b/test-suites/test-aave/helpers/utils/ray-math/index.ts new file mode 100644 index 00000000..91cff635 --- /dev/null +++ b/test-suites/test-aave/helpers/utils/ray-math/index.ts @@ -0,0 +1,40 @@ +import BigNumber from 'bignumber.js'; +import { BigNumberValue, valueToZDBigNumber } from './bignumber'; + +export function getLinearCumulatedRewards( + emissionPerSecond: BigNumberValue, + lastUpdateTimestamp: BigNumberValue, + currentTimestamp: BigNumberValue +): BigNumber { + const timeDelta = valueToZDBigNumber(currentTimestamp).minus(lastUpdateTimestamp.toString()); + return timeDelta.multipliedBy(emissionPerSecond.toString()); +} + +export function getNormalizedDistribution( + balance: BigNumberValue, + oldIndex: BigNumberValue, + emissionPerSecond: BigNumberValue, + lastUpdateTimestamp: BigNumberValue, + currentTimestamp: BigNumberValue, + emissionEndTimestamp: BigNumberValue, + precision: number = 18 +): BigNumber { + if ( + balance.toString() === '0' || + valueToZDBigNumber(lastUpdateTimestamp).gte(emissionEndTimestamp.toString()) + ) { + return valueToZDBigNumber(oldIndex); + } + const linearReward = getLinearCumulatedRewards( + emissionPerSecond, + lastUpdateTimestamp, + valueToZDBigNumber(currentTimestamp).gte(emissionEndTimestamp.toString()) + ? emissionEndTimestamp + : currentTimestamp + ); + + return linearReward + .multipliedBy(valueToZDBigNumber(10).exponentiatedBy(precision)) + .div(balance.toString()) + .plus(oldIndex.toString()); +} diff --git a/test-suites/test-aave/helpers/utils/ray-math/ray-math.ts b/test-suites/test-aave/helpers/utils/ray-math/ray-math.ts new file mode 100644 index 00000000..550a8993 --- /dev/null +++ b/test-suites/test-aave/helpers/utils/ray-math/ray-math.ts @@ -0,0 +1,59 @@ +import BigNumber from 'bignumber.js'; +import { BigNumberValue, valueToZDBigNumber } from './bignumber'; + +export const WAD = valueToZDBigNumber(10).pow(18); +export const HALF_WAD = WAD.dividedBy(2); + +export const RAY = valueToZDBigNumber(10).pow(27); +export const HALF_RAY = RAY.dividedBy(2); + +export const WAD_RAY_RATIO = valueToZDBigNumber(10).pow(9); + +export function wadMul(a: BigNumberValue, b: BigNumberValue): BigNumber { + return HALF_WAD.plus(valueToZDBigNumber(a).multipliedBy(b.toString())).div(WAD); +} + +export function wadDiv(a: BigNumberValue, b: BigNumberValue): BigNumber { + const halfB = valueToZDBigNumber(b).div(2); + + return halfB.plus(valueToZDBigNumber(a).multipliedBy(WAD)).div(b.toString()); +} + +export function rayMul(a: BigNumberValue, b: BigNumberValue): BigNumber { + return HALF_RAY.plus(valueToZDBigNumber(a).multipliedBy(b.toString())).div(RAY); +} + +export function rayDiv(a: BigNumberValue, b: BigNumberValue): BigNumber { + const halfB = valueToZDBigNumber(b).div(2); + + return halfB.plus(valueToZDBigNumber(a).multipliedBy(RAY)).div(b.toString()); +} + +export function rayToWad(a: BigNumberValue): BigNumber { + const halfRatio = valueToZDBigNumber(WAD_RAY_RATIO).div(2); + + return halfRatio.plus(a.toString()).div(WAD_RAY_RATIO); +} + +export function wadToRay(a: BigNumberValue): BigNumber { + return valueToZDBigNumber(a).multipliedBy(WAD_RAY_RATIO).decimalPlaces(0); +} + +export function rayPow(a: BigNumberValue, p: BigNumberValue): BigNumber { + let x = valueToZDBigNumber(a); + let n = valueToZDBigNumber(p); + let z = !n.modulo(2).eq(0) ? x : valueToZDBigNumber(RAY); + + for (n = n.div(2); !n.eq(0); n = n.div(2)) { + x = rayMul(x, x); + + if (!n.modulo(2).eq(0)) { + z = rayMul(z, x); + } + } + return z; +} + +export function rayToDecimal(a: BigNumberValue): BigNumber { + return valueToZDBigNumber(a).dividedBy(RAY); +} diff --git a/test-suites/test-aave/rewardAwareAToken.spec.ts b/test-suites/test-aave/rewardAwareAToken.spec.ts new file mode 100644 index 00000000..3ee404a7 --- /dev/null +++ b/test-suites/test-aave/rewardAwareAToken.spec.ts @@ -0,0 +1,521 @@ +import BigNumberJs from 'bignumber.js'; +import { BigNumber } from 'ethers'; +import { parseEther } from 'ethers/lib/utils'; +import { MAX_UINT_AMOUNT } from '../../helpers/constants'; +import { evmRevert, evmSnapshot, increaseTime } from '../../helpers/misc-utils'; +import { makeSuite, SignerWithAddress, TestEnv } from './helpers/make-suite'; +import { checkRewards } from './helpers/rewards-distribution/verify'; + +const chai = require('chai'); +const { expect } = chai; + +/** + * @dev REW is a mocked mintable token named RewardsToken.sol with an emission rate of 1 REW per second that can be deposited using a RewardsAwareAToken implementation named RewardsATokenMock.sol + * The distribution of REW happens at `claim`, but there is also a `updateMintableEmission` functions that updates the state of the distribution of 1 user but does not claim. + */ + +makeSuite('Reward Aware AToken', (testEnv: TestEnv) => { + let initTimestamp; + let evmSnapshotId; + + before('Initializing configuration', async () => { + // Sets BigNumber for this suite, instead of globally + BigNumberJs.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumberJs.ROUND_DOWN }); + }); + + after('Reset', () => { + // Reset BigNumber + BigNumberJs.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumberJs.ROUND_HALF_UP }); + }); + + beforeEach(async () => { + initTimestamp = await testEnv.rew.INIT_TIMESTAMP(); + evmSnapshotId = await evmSnapshot(); + }); + + afterEach(async () => { + await evmRevert(evmSnapshotId); + }); + + const mintAndDeposit = async ( + key: SignerWithAddress, + shouldReward?: boolean, + amountSize?: BigNumber + ) => { + const { rew, aRew, pool } = testEnv; + const amount = amountSize || parseEther('1'); + const userATokenBalanceBeforeDeposit = await aRew.balanceOf(key.address); + + // Mint REW to user + await rew.connect(key.signer).mint(amount); + + // Approve and Deposit REW to pool and mint + await rew.connect(key.signer).approve(pool.address, amount); + + const txDeposit = await pool.connect(key.signer).deposit(rew.address, amount, key.address, '0'); + + expect(Promise.resolve(txDeposit)).emit(aRew, 'Mint'); + + const userBalanceAfterDeposit = await rew.balanceOf(key.address); + const userATokenBalanceAfterDeposit = await aRew.balanceOf(key.address); + + expect(userATokenBalanceAfterDeposit) + .to.be.eq( + userATokenBalanceBeforeDeposit.add(amount), + 'User aToken balance should be equal the amount deposited' + ) + .and.gt('0', 'User aToken balance should be greater than zero'); + expect(userBalanceAfterDeposit).to.be.eq('0', 'Token balance should be zero'); + if (!txDeposit.blockNumber) { + throw 'missing block number'; + } + // Check all token rewards + await checkRewards(key, aRew.address, txDeposit.blockNumber, shouldReward); + }; + + const claim = async (user: SignerWithAddress, skipRewardChecks?: boolean) => { + skipRewardChecks = true; + + const { rew, aRew } = testEnv; + + // Claim all the rewards from aToken + const txClaim = await aRew.connect(user.signer).claim(rew.address); + await expect(Promise.resolve(txClaim)).to.emit(aRew, 'Claim'); + + // Calculate expected rewards + if (!txClaim.blockNumber) { + throw 'Block number missing from tx'; + } + + if (!skipRewardChecks) { + // Check all token rewards + await checkRewards(user, aRew.address, txClaim.blockNumber, true); + } + }; + + describe('Deposits: mints', async () => { + it('User1 deposits Reward Aware Token to Lending Pool', async () => { + const { + users: [user1], + } = testEnv; + + await mintAndDeposit(user1); + }); + + it('Other users deposits Reward Aware Token to Lending Pool', async () => { + const { + users: [, user2, user3, user4], + } = testEnv; + + await mintAndDeposit(user2); + await mintAndDeposit(user3); + await mintAndDeposit(user4); + }); + + it('User1 deposits multiple time Reward Aware Token to Lending Pool', async () => { + const { + users: [user1], + } = testEnv; + + await mintAndDeposit(user1, false, parseEther('2')); + await mintAndDeposit(user1, true, parseEther('12')); + }); + }); + describe('Withdrawals: burns', async () => { + it('User1 deposits Reward Aware Token to Lending Pool', async () => { + const { + users: [user1], + aRew, + rew, + pool, + } = testEnv; + + // Deposits + await mintAndDeposit(user1); + + // Burn/Withdraw + await aRew.approve(pool.address, MAX_UINT_AMOUNT); + await pool.connect(user1.signer).withdraw(rew.address, MAX_UINT_AMOUNT, user1.address); + }); + }); + describe('Claim rewards', async () => { + it('User1 claims 100% portion of REW via the aToken contract', async () => { + const { + users: [user1], + rew, + aRew, + } = testEnv; + // User1 deposits + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + // Check rewards for aToken at rew + const token = await aRew.getRewardsTokenAddress('0'); + const aTokenRewards = await rew.getClaimableRewards(aRew.address); + // Check rewards for user at aRew + const userRewards = await aRew.getClaimableRewards(rew.address, user1.address); + // Expect user rewards to be the same as aToken rewards due 100% + expect(aTokenRewards).to.be.eq( + userRewards, + 'Rewards should be the same due user holds 100% of RewardsAware distribution' + ); + + // Claims and check rewards + await claim(user1); + }); + + it('Two users with half the portion of REW via the aToken contract', async () => { + const { + users: [user1, user2], + rew, + aRew, + } = testEnv; + // User1 and user2 deposits + await mintAndDeposit(user1); + await mintAndDeposit(user2); + + // Pass time to generate rewards + await increaseTime(1000); + + // Check rewards for aToken at rew + const aTokenRewards = await rew.getClaimableRewards(aRew.address); + + // Check rewards for user1 at aRew + const user1Rewards = await aRew.getClaimableRewards(rew.address, user1.address); + + // Check rewards for user2 at aRew + const user2Rewards = await aRew.getClaimableRewards(rew.address, user2.address); + + // Expect user rewards to be the same as aToken rewards + expect(aTokenRewards).to.be.eq( + user1Rewards.add(user2Rewards), + 'Rewards should be the same of all users of RewardsAware distribution' + ); + + // Claims and check rewards + await claim(user1); + await claim(user2); + }); + + it('Four users with different portions of REW via the aToken contract', async () => { + const { + users: [user1, user2, user3, user4], + rew, + aRew, + } = testEnv; + // Deposits + await mintAndDeposit(user1, false, parseEther('1')); + await mintAndDeposit(user2, false, parseEther('2.5')); + await mintAndDeposit(user3, false, parseEther('4.7')); + await mintAndDeposit(user4, false, parseEther('0.31')); + + // Pass time to generate rewards + await increaseTime(1000); + + // Claims and check rewards + await claim(user1); + await claim(user2); + await claim(user3); + await claim(user4); + + // Pass time to generate rewards + await increaseTime(2713); + + // Claims and check rewards + await claim(user1); + await claim(user2); + await claim(user3); + await claim(user4); + }); + + it('Two users with half the portion of REW via the aToken contract, one burns and them claims', async () => { + const { + users: [user1, user2], + rew, + aRew, + pool, + } = testEnv; + + // User1 and user2 deposits + await mintAndDeposit(user1, false, parseEther('1000')); + await mintAndDeposit(user2, false, parseEther('1000')); + + // Pass time to generate rewards + await increaseTime(1000); + + // Claims and check rewards + await claim(user1); + await claim(user2); + + // Pass time to generate rewards + await increaseTime(1000); + + // Burn/Withdraw to update current lifetime rewards state + await aRew.approve(pool.address, MAX_UINT_AMOUNT); + await pool.connect(user1.signer).withdraw(rew.address, MAX_UINT_AMOUNT, user1.address); + + // Claims + await claim(user1); + await claim(user2); + }); + }); + describe('Getters', () => { + describe('getClaimableRewards', () => { + it('Rewards should be zero if user with no balance', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + + const rewards = await aRew.getClaimableRewards(rew.address, user1.address); + expect(rewards).eq('0', 'Rewards should be zero'); + }); + + it('Rewards should be available after time travel', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + const rewards = await aRew.getClaimableRewards(rew.address, user1.address); + expect(rewards).gt('0', 'Rewards should be greater than zero'); + }); + }); + describe('getUserLifetimeRewardsAccrued', () => { + it('User lifetime rewards should be zero if no deposit', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + + const rewards = await aRew.getUserRewardsAccrued(rew.address, user1.address); + expect(rewards).eq('0', 'Rewards should be zero'); + }); + + it('User lifetime rewards should be zero if deposit due state is not updated', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + const rewards = await aRew.getUserRewardsAccrued(rew.address, user1.address); + expect(rewards).eq('0', 'Rewards should be zero'); + }); + + it('User should have some lifetime rewards if deposit again due state is updated', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + await mintAndDeposit(user1, true); + + const rewards = await aRew.getUserRewardsAccrued(rew.address, user1.address); + expect(rewards).gt('0', 'Rewards should be greater than zero'); + }); + + it('User should have some lifetime rewards if claims due state is updated', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + await claim(user1); + + const rewards = await aRew.getUserRewardsAccrued(rew.address, user1.address); + expect(rewards).gt('0', 'Rewards should be greater than zero'); + }); + }); + + describe('getUserIndex', () => { + it('User index should be zero if no deposit', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + + const rewards = await aRew.getUserIndex(rew.address, user1.address); + expect(rewards).eq('0', 'Rewards should be zero'); + }); + + it('User lifetime rewards should be zero if deposit due state is not updated', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + const rewards = await aRew.getUserIndex(rew.address, user1.address); + expect(rewards).eq('0', 'Rewards should be zero'); + }); + + it('User should have some lifetime rewards if deposit again due state is updated', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + await mintAndDeposit(user1, true); + + const rewards = await aRew.getUserIndex(rew.address, user1.address); + expect(rewards).gt('0', 'Rewards should be greater than zero'); + }); + + it('User should have some lifetime rewards if claims due state is updated', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + await claim(user1); + + const rewards = await aRew.getUserIndex(rew.address, user1.address); + expect(rewards).gt('0', 'Rewards should be greater than zero'); + }); + }); + describe('getUserClaimedRewards', () => { + it('User should NOT have claimed rewards if didnt claim', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + const rewards = await aRew.getUserIndex(rew.address, user1.address); + expect(rewards).eq('0', 'Rewards should be zero'); + }); + + it('User should have claimed rewards if claims', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + await claim(user1); + + const rewards = await aRew.getUserIndex(rew.address, user1.address); + expect(rewards).gt('0', 'Rewards should be greater than zero'); + }); + }); + describe('getLifetimeRewards', () => { + it('The aToken Tifetime rewards should be zero if there is no deposits', async () => { + const { aRew, rew } = testEnv; + + const rewards = await aRew.getLifetimeRewards(rew.address); + expect(rewards).eq('0', 'Rewards should be zero'); + }); + + it('The aToken lifetime rewards should be zero at init', async () => { + const { aRew, rew } = testEnv; + + const rewards = await aRew.getLifetimeRewards(rew.address); + expect(rewards).eq('0', 'Rewards should be zero'); + }); + + it('The aToken lifetime rewards should update if there is further actions: deposit', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + // Deposit again to update current lifetime rewards state + await mintAndDeposit(user1, true); + + const rewards = await aRew.getLifetimeRewards(rew.address); + expect(rewards).gte('0', 'Rewards should be greater than zero'); + }); + + it('The aToken lifetime rewards should update if there is further actions: claim', async () => { + const { + users: [user1], + aRew, + rew, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + // Claim to update current lifetime rewards state + await claim(user1); + + const rewards = await aRew.getLifetimeRewards(rew.address); + expect(rewards).gte('0', 'Rewards should be greater than zero'); + }); + + it('The aToken lifetime rewards should update if there is further actions: burn', async () => { + const { + users: [user1], + aRew, + rew, + pool, + } = testEnv; + await mintAndDeposit(user1); + + // Pass time to generate rewards + await increaseTime(1000); + + // Burn/Withdraw to update current lifetime rewards state + await aRew.approve(pool.address, MAX_UINT_AMOUNT); + await pool.connect(user1.signer).withdraw(rew.address, MAX_UINT_AMOUNT, user1.address); + + const rewards = await aRew.getLifetimeRewards(rew.address); + expect(rewards).gte('0', 'Rewards should be greater than zero'); + }); + }); + describe('getRewardsToken', () => { + it('The getter should return the current token reward address', async () => { + const { aRew, rew } = testEnv; + const rewardToken = await aRew.getRewardsTokenAddress(0); + expect(rewardToken).to.be.equal(rew.address); + }); + }); + }); +}); diff --git a/test-suites/test-amm/__setup.spec.ts b/test-suites/test-amm/__setup.spec.ts index 277998f4..8844af7c 100644 --- a/test-suites/test-amm/__setup.spec.ts +++ b/test-suites/test-amm/__setup.spec.ts @@ -26,7 +26,8 @@ import { deployUniswapLiquiditySwapAdapter, deployUniswapRepayAdapter, deployFlashLiquidationAdapter, - authorizeWETHGateway + authorizeWETHGateway, + deployATokenImplementations, } from '../../helpers/contracts-deployments'; import { Signer } from 'ethers'; import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../../helpers/types'; @@ -94,6 +95,14 @@ const deployAllMockTokens = async (deployer: Signer) => { const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { console.time('setup'); const aaveAdmin = await deployer.getAddress(); + const config = loadPoolConfig(ConfigNames.Amm); + const { + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, + ReservesConfig, + } = config; const mockTokens = await deployAllMockTokens(deployer); @@ -229,8 +238,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { lendingRateOracle, aaveAdmin ); - - const reservesParams = getReservesConfigByPool(AavePools.amm); + await deployATokenImplementations(ConfigNames.Amm, ReservesConfig); const testHelpers = await deployAaveProtocolDataProvider(addressesProvider.address); @@ -239,18 +247,10 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { console.log('Initialize configuration'); - const config = loadPoolConfig(ConfigNames.Amm); - - const { - ATokenNamePrefix, - StableDebtTokenNamePrefix, - VariableDebtTokenNamePrefix, - SymbolPrefix, - } = config; const treasuryAddress = await getTreasuryAddress(config); await initReservesByHelper( - reservesParams, + ReservesConfig, allReservesAddresses, ATokenNamePrefix, StableDebtTokenNamePrefix, @@ -259,9 +259,10 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { admin, treasuryAddress, ZERO_ADDRESS, + ConfigNames.Amm, false ); - await configureReservesByHelper(reservesParams, allReservesAddresses, testHelpers, admin); + await configureReservesByHelper(ReservesConfig, allReservesAddresses, testHelpers, admin); const collateralManager = await deployLendingPoolCollateralManager(); await waitForTx(