diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index a36bfe2f..5a866dba 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -386,7 +386,6 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt } uint256 bestAmountOut; - try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns ( uint256[] memory resultAmounts ) { diff --git a/contracts/deployments/ATokensAndRatesHelper.sol b/contracts/deployments/ATokensAndRatesHelper.sol index 0593178a..400f7ca6 100644 --- a/contracts/deployments/ATokensAndRatesHelper.sol +++ b/contracts/deployments/ATokensAndRatesHelper.sol @@ -20,6 +20,20 @@ contract ATokensAndRatesHelper is Ownable { address private poolConfigurator; event deployedContracts(address aToken, address strategy); + struct InitDeploymentInput { + address asset; + uint256[6] rates; + } + + struct ConfigureReserveInput { + address asset; + uint256 baseLTV; + uint256 liquidationThreshold; + uint256 liquidationBonus; + uint256 reserveFactor; + bool stableBorrowingEnabled; + } + constructor( address payable _pool, address _addressesProvider, @@ -30,90 +44,40 @@ contract ATokensAndRatesHelper is Ownable { poolConfigurator = _poolConfigurator; } - function initDeployment( - address[] calldata assets, - string[] calldata symbols, - uint256[6][] calldata rates, - address treasuryAddress, - address incentivesController - ) external onlyOwner { - require(assets.length == symbols.length, 't Arrays not same length'); - require(rates.length == symbols.length, 'r Arrays not same length'); - for (uint256 i = 0; i < assets.length; i++) { + function initDeployment(InitDeploymentInput[] calldata inputParams) external onlyOwner { + for (uint256 i = 0; i < inputParams.length; i++) { emit deployedContracts( - address( - new AToken( - LendingPool(pool), - assets[i], - treasuryAddress, - StringLib.concat('Aave interest bearing ', symbols[i]), - StringLib.concat('a', symbols[i]), - incentivesController - ) - ), + address(new AToken()), address( new DefaultReserveInterestRateStrategy( LendingPoolAddressesProvider(addressesProvider), - rates[i][0], - rates[i][1], - rates[i][2], - rates[i][3], - rates[i][4], - rates[i][5] + inputParams[i].rates[0], + inputParams[i].rates[1], + inputParams[i].rates[2], + inputParams[i].rates[3], + inputParams[i].rates[4], + inputParams[i].rates[5] ) ) ); } } - function initReserve( - address[] calldata stables, - address[] calldata variables, - address[] calldata aTokens, - address[] calldata strategies, - uint8[] calldata reserveDecimals - ) external onlyOwner { - require(variables.length == stables.length); - require(aTokens.length == stables.length); - require(strategies.length == stables.length); - require(reserveDecimals.length == stables.length); - - for (uint256 i = 0; i < stables.length; i++) { - LendingPoolConfigurator(poolConfigurator).initReserve( - aTokens[i], - stables[i], - variables[i], - reserveDecimals[i], - strategies[i] - ); - } - } - - function configureReserves( - address[] calldata assets, - uint256[] calldata baseLTVs, - uint256[] calldata liquidationThresholds, - uint256[] calldata liquidationBonuses, - uint256[] calldata reserveFactors, - bool[] calldata stableBorrowingEnabled - ) external onlyOwner { - require(baseLTVs.length == assets.length); - require(liquidationThresholds.length == assets.length); - require(liquidationBonuses.length == assets.length); - require(stableBorrowingEnabled.length == assets.length); - require(reserveFactors.length == assets.length); - + function configureReserves(ConfigureReserveInput[] calldata inputParams) external onlyOwner { LendingPoolConfigurator configurator = LendingPoolConfigurator(poolConfigurator); - for (uint256 i = 0; i < assets.length; i++) { + for (uint256 i = 0; i < inputParams.length; i++) { configurator.configureReserveAsCollateral( - assets[i], - baseLTVs[i], - liquidationThresholds[i], - liquidationBonuses[i] + inputParams[i].asset, + inputParams[i].baseLTV, + inputParams[i].liquidationThreshold, + inputParams[i].liquidationBonus ); - configurator.enableBorrowingOnReserve(assets[i], stableBorrowingEnabled[i]); - configurator.setReserveFactor(assets[i], reserveFactors[i]); + configurator.enableBorrowingOnReserve( + inputParams[i].asset, + inputParams[i].stableBorrowingEnabled + ); + configurator.setReserveFactor(inputParams[i].asset, inputParams[i].reserveFactor); } } } diff --git a/contracts/deployments/StableAndVariableTokensHelper.sol b/contracts/deployments/StableAndVariableTokensHelper.sol index 8f76bbe1..e31651f3 100644 --- a/contracts/deployments/StableAndVariableTokensHelper.sol +++ b/contracts/deployments/StableAndVariableTokensHelper.sol @@ -18,34 +18,11 @@ contract StableAndVariableTokensHelper is Ownable { addressesProvider = _addressesProvider; } - function initDeployment( - address[] calldata tokens, - string[] calldata symbols, - address incentivesController - ) external onlyOwner { + function initDeployment(address[] calldata tokens, string[] calldata symbols) external onlyOwner { require(tokens.length == symbols.length, 'Arrays not same length'); require(pool != address(0), 'Pool can not be zero address'); for (uint256 i = 0; i < tokens.length; i++) { - emit deployedContracts( - address( - new StableDebtToken( - pool, - tokens[i], - StringLib.concat('Aave stable debt bearing ', symbols[i]), - StringLib.concat('stableDebt', symbols[i]), - incentivesController - ) - ), - address( - new VariableDebtToken( - pool, - tokens[i], - StringLib.concat('Aave variable debt bearing ', symbols[i]), - StringLib.concat('variableDebt', symbols[i]), - incentivesController - ) - ) - ); + emit deployedContracts(address(new StableDebtToken()), address(new VariableDebtToken())); } } diff --git a/contracts/interfaces/IAToken.sol b/contracts/interfaces/IAToken.sol index 5fa1a4f0..3a6ac663 100644 --- a/contracts/interfaces/IAToken.sol +++ b/contracts/interfaces/IAToken.sol @@ -3,8 +3,10 @@ pragma solidity 0.6.12; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; +import {IInitializableAToken} from './IInitializableAToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; -interface IAToken is IERC20, IScaledBalanceToken { +interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken { /** * @dev Emitted after the mint action * @param from The address performing the mint @@ -80,9 +82,21 @@ interface IAToken is IERC20, IScaledBalanceToken { /** * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer * assets in borrow(), withdraw() and flashLoan() - * @param user The recipient of the aTokens + * @param user The recipient of the underlying * @param amount The amount getting transferred * @return The amount transferred **/ function transferUnderlyingTo(address user, uint256 amount) external returns (uint256); + + /** + * @dev Invoked to execute actions on the aToken side after a repayment. + * @param user The user executing the repayment + * @param amount The amount getting repaid + **/ + function handleRepayment(address user, uint256 amount) external; + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); } diff --git a/contracts/interfaces/IInitializableAToken.sol b/contracts/interfaces/IInitializableAToken.sol new file mode 100644 index 00000000..5038580a --- /dev/null +++ b/contracts/interfaces/IInitializableAToken.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from './ILendingPool.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IInitializableAToken + * @notice Interface for the initialize function on AToken + * @author Aave + **/ +interface IInitializableAToken { + /** + * @dev Emitted when an aToken is initialized + * @param underlyingAsset The address of the underlying asset + * @param pool The address of the associated lending pool + * @param treasury The address of the treasury + * @param incentivesController The address of the incentives controller for this aToken + * @param aTokenDecimals the decimals of the underlying + * @param aTokenName the name of the aToken + * @param aTokenSymbol the symbol of the aToken + * @param params A set of encoded parameters for additional initialization + **/ + event Initialized( + address indexed underlyingAsset, + address indexed pool, + address treasury, + address incentivesController, + uint8 aTokenDecimals, + string aTokenName, + string aTokenSymbol, + bytes params + ); + + /** + * @dev Initializes the aToken + * @param pool The address of the lending pool where this aToken will be used + * @param treasury The address of the Aave treasury, receiving the fees on this aToken + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's + * @param aTokenName The name of the aToken + * @param aTokenSymbol The symbol of the aToken + */ + function initialize( + ILendingPool pool, + address treasury, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 aTokenDecimals, + string calldata aTokenName, + string calldata aTokenSymbol, + bytes calldata params + ) external; +} diff --git a/contracts/interfaces/IInitializableDebtToken.sol b/contracts/interfaces/IInitializableDebtToken.sol new file mode 100644 index 00000000..c5bdb3f5 --- /dev/null +++ b/contracts/interfaces/IInitializableDebtToken.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from './ILendingPool.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IInitializableDebtToken + * @notice Interface for the initialize function common between debt tokens + * @author Aave + **/ +interface IInitializableDebtToken { + /** + * @dev Emitted when a debt token is initialized + * @param underlyingAsset The address of the underlying asset + * @param pool The address of the associated lending pool + * @param incentivesController The address of the incentives controller for this aToken + * @param debtTokenDecimals the decimals of the debt token + * @param debtTokenName the name of the debt token + * @param debtTokenSymbol the symbol of the debt token + * @param params A set of encoded parameters for additional initialization + **/ + event Initialized( + address indexed underlyingAsset, + address indexed pool, + address incentivesController, + uint8 debtTokenDecimals, + string debtTokenName, + string debtTokenSymbol, + bytes params + ); + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol, + bytes calldata params + ) external; +} diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol new file mode 100644 index 00000000..7554f2a8 --- /dev/null +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +interface ILendingPoolConfigurator { + struct InitReserveInput { + address aTokenImpl; + address stableDebtTokenImpl; + address variableDebtTokenImpl; + uint8 underlyingAssetDecimals; + address interestRateStrategyAddress; + address underlyingAsset; + address treasury; + address incentivesController; + string underlyingAssetName; + string aTokenName; + string aTokenSymbol; + string variableDebtTokenName; + string variableDebtTokenSymbol; + string stableDebtTokenName; + string stableDebtTokenSymbol; + bytes params; + } + + struct UpdateATokenInput { + address asset; + address treasury; + address incentivesController; + string name; + string symbol; + address implementation; + bytes params; + } + + struct UpdateDebtTokenInput { + address asset; + address incentivesController; + string name; + string symbol; + address implementation; + bytes params; + } + + /** + * @dev Emitted when a reserve is initialized. + * @param asset The address of the underlying asset of the reserve + * @param aToken The address of the associated aToken contract + * @param stableDebtToken The address of the associated stable rate debt token + * @param variableDebtToken The address of the associated variable rate debt token + * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve + **/ + event ReserveInitialized( + address indexed asset, + address indexed aToken, + address stableDebtToken, + address variableDebtToken, + address interestRateStrategyAddress + ); + + /** + * @dev Emitted when borrowing is enabled on a reserve + * @param asset The address of the underlying asset of the reserve + * @param stableRateEnabled True if stable rate borrowing is enabled, false otherwise + **/ + event BorrowingEnabledOnReserve(address indexed asset, bool stableRateEnabled); + + /** + * @dev Emitted when borrowing is disabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event BorrowingDisabledOnReserve(address indexed asset); + + /** + * @dev Emitted when the collateralization risk parameters for the specified asset are updated. + * @param asset The address of the underlying asset of the reserve + * @param ltv The loan to value of the asset when used as collateral + * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized + * @param liquidationBonus The bonus liquidators receive to liquidate this asset + **/ + event CollateralConfigurationChanged( + address indexed asset, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus + ); + + /** + * @dev Emitted when stable rate borrowing is enabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event StableRateEnabledOnReserve(address indexed asset); + + /** + * @dev Emitted when stable rate borrowing is disabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event StableRateDisabledOnReserve(address indexed asset); + + /** + * @dev Emitted when a reserve is activated + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveActivated(address indexed asset); + + /** + * @dev Emitted when a reserve is deactivated + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveDeactivated(address indexed asset); + + /** + * @dev Emitted when a reserve is frozen + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveFrozen(address indexed asset); + + /** + * @dev Emitted when a reserve is unfrozen + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveUnfrozen(address indexed asset); + + /** + * @dev Emitted when a reserve factor is updated + * @param asset The address of the underlying asset of the reserve + * @param factor The new reserve factor + **/ + event ReserveFactorChanged(address indexed asset, uint256 factor); + + /** + * @dev Emitted when the reserve decimals are updated + * @param asset The address of the underlying asset of the reserve + * @param decimals The new decimals + **/ + event ReserveDecimalsChanged(address indexed asset, uint256 decimals); + + /** + * @dev Emitted when a reserve interest strategy contract is updated + * @param asset The address of the underlying asset of the reserve + * @param strategy The new address of the interest strategy contract + **/ + event ReserveInterestRateStrategyChanged(address indexed asset, address strategy); + + /** + * @dev Emitted when an aToken implementation is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The aToken proxy address + * @param implementation The new aToken implementation + **/ + event ATokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); + + /** + * @dev Emitted when the implementation of a stable debt token is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The stable debt token proxy address + * @param implementation The new aToken implementation + **/ + event StableDebtTokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); + + /** + * @dev Emitted when the implementation of a variable debt token is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The variable debt token proxy address + * @param implementation The new aToken implementation + **/ + event VariableDebtTokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); +} diff --git a/contracts/interfaces/IReserveInterestRateStrategy.sol b/contracts/interfaces/IReserveInterestRateStrategy.sol index 430145e3..cee593cb 100644 --- a/contracts/interfaces/IReserveInterestRateStrategy.sol +++ b/contracts/interfaces/IReserveInterestRateStrategy.sol @@ -13,7 +13,25 @@ interface IReserveInterestRateStrategy { function calculateInterestRates( address reserve, - uint256 utilizationRate, + uint256 availableLiquidity, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 averageStableBorrowRate, + uint256 reserveFactor + ) + external + view + returns ( + uint256, + uint256, + uint256 + ); + + function calculateInterestRates( + address reserve, + address aToken, + uint256 liquidityAdded, + uint256 liquidityTaken, uint256 totalStableDebt, uint256 totalVariableDebt, uint256 averageStableBorrowRate, diff --git a/contracts/interfaces/IStableDebtToken.sol b/contracts/interfaces/IStableDebtToken.sol index c24750bc..e39cf8b5 100644 --- a/contracts/interfaces/IStableDebtToken.sol +++ b/contracts/interfaces/IStableDebtToken.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; +import {IInitializableDebtToken} from './IInitializableDebtToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + /** * @title IStableDebtToken * @notice Defines the interface for the stable debt token @@ -8,7 +11,7 @@ pragma solidity 0.6.12; * @author Aave **/ -interface IStableDebtToken { +interface IStableDebtToken is IInitializableDebtToken { /** * @dev Emitted when new stable debt is minted * @param user The address of the user who triggered the minting @@ -122,4 +125,9 @@ interface IStableDebtToken { * @return The debt balance of the user since the last burn/mint action **/ function principalBalanceOf(address user) external view returns (uint256); + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); } diff --git a/contracts/interfaces/ITokenConfiguration.sol b/contracts/interfaces/ITokenConfiguration.sol deleted file mode 100644 index 94607bcd..00000000 --- a/contracts/interfaces/ITokenConfiguration.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.12; - -/** - * @title ITokenConfiguration - * @author Aave - * @dev Common interface between aTokens and debt tokens to fetch the - * token configuration - **/ -interface ITokenConfiguration { - function UNDERLYING_ASSET_ADDRESS() external view returns (address); - - function POOL() external view returns (address); -} diff --git a/contracts/interfaces/IVariableDebtToken.sol b/contracts/interfaces/IVariableDebtToken.sol index 6a0c1555..d88c25fc 100644 --- a/contracts/interfaces/IVariableDebtToken.sol +++ b/contracts/interfaces/IVariableDebtToken.sol @@ -2,13 +2,15 @@ pragma solidity 0.6.12; import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; +import {IInitializableDebtToken} from './IInitializableDebtToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; /** * @title IVariableDebtToken * @author Aave * @notice Defines the basic interface for a variable debt token. **/ -interface IVariableDebtToken is IScaledBalanceToken { +interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken { /** * @dev Emitted after the mint action * @param from The address performing the mint @@ -52,4 +54,9 @@ interface IVariableDebtToken is IScaledBalanceToken { uint256 amount, uint256 index ) external; + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); } diff --git a/contracts/misc/WETHGateway.sol b/contracts/misc/WETHGateway.sol index b90c9bbf..336e8de2 100644 --- a/contracts/misc/WETHGateway.sol +++ b/contracts/misc/WETHGateway.sol @@ -18,39 +18,47 @@ contract WETHGateway is IWETHGateway, Ownable { using UserConfiguration for DataTypes.UserConfigurationMap; IWETH internal immutable WETH; - ILendingPool internal immutable POOL; - IAToken internal immutable aWETH; /** * @dev Sets the WETH address and the LendingPoolAddressesProvider address. Infinite approves lending pool. * @param weth Address of the Wrapped Ether contract - * @param pool Address of the LendingPool contract **/ - constructor(address weth, address pool) public { - ILendingPool poolInstance = ILendingPool(pool); + constructor(address weth) public { WETH = IWETH(weth); - POOL = poolInstance; - aWETH = IAToken(poolInstance.getReserveData(weth).aTokenAddress); - IWETH(weth).approve(pool, uint256(-1)); + } + + function authorizeLendingPool(address lendingPool) external onlyOwner { + WETH.approve(lendingPool, uint256(-1)); } /** * @dev deposits WETH into the reserve, using native ETH. A corresponding amount of the overlying asset (aTokens) * is minted. + * @param lendingPool address of the targeted underlying lending pool * @param onBehalfOf address of the user who will receive the aTokens representing the deposit * @param referralCode integrators are assigned a referral code and can potentially receive rewards. **/ - function depositETH(address onBehalfOf, uint16 referralCode) external payable override { + function depositETH( + address lendingPool, + address onBehalfOf, + uint16 referralCode + ) external payable override { WETH.deposit{value: msg.value}(); - POOL.deposit(address(WETH), msg.value, onBehalfOf, referralCode); + ILendingPool(lendingPool).deposit(address(WETH), msg.value, onBehalfOf, referralCode); } /** * @dev withdraws the WETH _reserves of msg.sender. + * @param lendingPool address of the targeted underlying lending pool * @param amount amount of aWETH to withdraw and receive native ETH * @param to address of the user who will receive native ETH */ - function withdrawETH(uint256 amount, address to) external override { + function withdrawETH( + address lendingPool, + uint256 amount, + address to + ) external override { + IAToken aWETH = IAToken(ILendingPool(lendingPool).getReserveData(address(WETH)).aTokenAddress); uint256 userBalance = aWETH.balanceOf(msg.sender); uint256 amountToWithdraw = amount; @@ -59,24 +67,29 @@ contract WETHGateway is IWETHGateway, Ownable { amountToWithdraw = userBalance; } aWETH.transferFrom(msg.sender, address(this), amountToWithdraw); - POOL.withdraw(address(WETH), amountToWithdraw, address(this)); + ILendingPool(lendingPool).withdraw(address(WETH), amountToWithdraw, address(this)); WETH.withdraw(amountToWithdraw); _safeTransferETH(to, amountToWithdraw); } /** * @dev repays a borrow on the WETH reserve, for the specified amount (or for the whole amount, if uint256(-1) is specified). + * @param lendingPool address of the targeted underlying lending pool * @param amount the amount to repay, or uint256(-1) if the user wants to repay everything * @param rateMode the rate mode to repay * @param onBehalfOf the address for which msg.sender is repaying */ function repayETH( + address lendingPool, uint256 amount, uint256 rateMode, address onBehalfOf ) external payable override { (uint256 stableDebt, uint256 variableDebt) = - Helpers.getUserCurrentDebtMemory(onBehalfOf, POOL.getReserveData(address(WETH))); + Helpers.getUserCurrentDebtMemory( + onBehalfOf, + ILendingPool(lendingPool).getReserveData(address(WETH)) + ); uint256 paybackAmount = DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE @@ -88,7 +101,7 @@ contract WETHGateway is IWETHGateway, Ownable { } require(msg.value >= paybackAmount, 'msg.value is less than repayment amount'); WETH.deposit{value: paybackAmount}(); - POOL.repay(address(WETH), msg.value, rateMode, onBehalfOf); + ILendingPool(lendingPool).repay(address(WETH), msg.value, rateMode, onBehalfOf); // refund remaining dust eth if (msg.value > paybackAmount) _safeTransferETH(msg.sender, msg.value - paybackAmount); @@ -96,16 +109,24 @@ contract WETHGateway is IWETHGateway, Ownable { /** * @dev borrow WETH, unwraps to ETH and send both the ETH and DebtTokens to msg.sender, via `approveDelegation` and onBehalf argument in `LendingPool.borrow`. + * @param lendingPool address of the targeted underlying lending pool * @param amount the amount of ETH to borrow * @param interesRateMode the interest rate mode * @param referralCode integrators are assigned a referral code and can potentially receive rewards */ function borrowETH( + address lendingPool, uint256 amount, uint256 interesRateMode, uint16 referralCode ) external override { - POOL.borrow(address(WETH), amount, interesRateMode, referralCode, msg.sender); + ILendingPool(lendingPool).borrow( + address(WETH), + amount, + interesRateMode, + referralCode, + msg.sender + ); WETH.withdraw(amount); _safeTransferETH(msg.sender, amount); } @@ -152,20 +173,6 @@ contract WETHGateway is IWETHGateway, Ownable { return address(WETH); } - /** - * @dev Get aWETH address used by WETHGateway - */ - function getAWETHAddress() external view returns (address) { - return address(aWETH); - } - - /** - * @dev Get LendingPool address used by WETHGateway - */ - function getLendingPoolAddress() external view returns (address) { - return address(POOL); - } - /** * @dev Only WETH contract is allowed to transfer ETH here. Prevent other addresses to send Ether to this contract. */ diff --git a/contracts/misc/interfaces/IWETHGateway.sol b/contracts/misc/interfaces/IWETHGateway.sol index c06471fb..78d913cd 100644 --- a/contracts/misc/interfaces/IWETHGateway.sol +++ b/contracts/misc/interfaces/IWETHGateway.sol @@ -2,17 +2,27 @@ pragma solidity 0.6.12; interface IWETHGateway { - function depositETH(address onBehalfOf, uint16 referralCode) external payable; + function depositETH( + address lendingPool, + address onBehalfOf, + uint16 referralCode + ) external payable; - function withdrawETH(uint256 amount, address onBehalfOf) external; + function withdrawETH( + address lendingPool, + uint256 amount, + address onBehalfOf + ) external; function repayETH( + address lendingPool, uint256 amount, uint256 rateMode, address onBehalfOf ) external payable; function borrowETH( + address lendingPool, uint256 amount, uint256 interesRateMode, uint16 referralCode diff --git a/contracts/mocks/upgradeability/MockAToken.sol b/contracts/mocks/upgradeability/MockAToken.sol index f0eda38c..7e7b6f2a 100644 --- a/contracts/mocks/upgradeability/MockAToken.sol +++ b/contracts/mocks/upgradeability/MockAToken.sol @@ -2,39 +2,11 @@ pragma solidity 0.6.12; import {AToken} from '../../protocol/tokenization/AToken.sol'; -import {LendingPool} from '../../protocol/lendingpool/LendingPool.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; contract MockAToken is AToken { - constructor( - LendingPool pool, - address underlyingAssetAddress, - address reserveTreasury, - string memory tokenName, - string memory tokenSymbol, - address incentivesController - ) - public - AToken( - pool, - underlyingAssetAddress, - reserveTreasury, - tokenName, - tokenSymbol, - incentivesController - ) - {} - function getRevision() internal pure override returns (uint256) { return 0x2; } - - function initialize( - uint8 _underlyingAssetDecimals, - string calldata _tokenName, - string calldata _tokenSymbol - ) external virtual override initializer { - _setName(_tokenName); - _setSymbol(_tokenSymbol); - _setDecimals(_underlyingAssetDecimals); - } } diff --git a/contracts/mocks/upgradeability/MockStableDebtToken.sol b/contracts/mocks/upgradeability/MockStableDebtToken.sol index 5030c171..cbc66664 100644 --- a/contracts/mocks/upgradeability/MockStableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockStableDebtToken.sol @@ -4,17 +4,6 @@ pragma solidity 0.6.12; import {StableDebtToken} from '../../protocol/tokenization/StableDebtToken.sol'; contract MockStableDebtToken is StableDebtToken { - constructor( - address _pool, - address _underlyingAssetAddress, - string memory _tokenName, - string memory _tokenSymbol, - address incentivesController - ) - public - StableDebtToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol, incentivesController) - {} - function getRevision() internal pure override returns (uint256) { return 0x2; } diff --git a/contracts/mocks/upgradeability/MockVariableDebtToken.sol b/contracts/mocks/upgradeability/MockVariableDebtToken.sol index 0a9a03aa..497682c3 100644 --- a/contracts/mocks/upgradeability/MockVariableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockVariableDebtToken.sol @@ -4,23 +4,6 @@ pragma solidity 0.6.12; import {VariableDebtToken} from '../../protocol/tokenization/VariableDebtToken.sol'; contract MockVariableDebtToken is VariableDebtToken { - constructor( - address _pool, - address _underlyingAssetAddress, - string memory _tokenName, - string memory _tokenSymbol, - address incentivesController - ) - public - VariableDebtToken( - _pool, - _underlyingAssetAddress, - _tokenName, - _tokenSymbol, - incentivesController - ) - {} - function getRevision() internal pure override returns (uint256) { return 0x2; } diff --git a/contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol b/contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol index c2584a22..af4db241 100644 --- a/contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol +++ b/contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol @@ -7,6 +7,8 @@ import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol'; +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import 'hardhat/console.sol'; /** * @title DefaultReserveInterestRateStrategy contract @@ -96,6 +98,51 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { return _baseVariableBorrowRate.add(_variableRateSlope1).add(_variableRateSlope2); } + /** + * @dev Calculates the interest rates depending on the reserve's state and configurations + * @param reserve The address of the reserve + * @param liquidityAdded The liquidity added during the operation + * @param liquidityTaken The liquidity taken during the operation + * @param totalStableDebt The total borrowed from the reserve a stable rate + * @param totalVariableDebt The total borrowed from the reserve at a variable rate + * @param averageStableBorrowRate The weighted average of all the stable rate loans + * @param reserveFactor The reserve portion of the interest that goes to the treasury of the market + * @return The liquidity rate, the stable borrow rate and the variable borrow rate + **/ + function calculateInterestRates( + address reserve, + address aToken, + uint256 liquidityAdded, + uint256 liquidityTaken, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 averageStableBorrowRate, + uint256 reserveFactor + ) + external + view + override + returns ( + uint256, + uint256, + uint256 + ) + { + uint256 availableLiquidity = IERC20(reserve).balanceOf(aToken); + //avoid stack too deep + availableLiquidity = availableLiquidity.add(liquidityAdded).sub(liquidityTaken); + + return + calculateInterestRates( + reserve, + availableLiquidity, + totalStableDebt, + totalVariableDebt, + averageStableBorrowRate, + reserveFactor + ); + } + struct CalcInterestRatesLocalVars { uint256 totalDebt; uint256 currentVariableBorrowRate; @@ -105,9 +152,11 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { } /** - * @dev Calculates the interest rates depending on the reserve's state and configurations + * @dev Calculates the interest rates depending on the reserve's state and configurations. + * NOTE This function is kept for compatibility with the previous DefaultInterestRateStrategy interface. + * New protocol implementation uses the new calculateInterestRates() interface * @param reserve The address of the reserve - * @param availableLiquidity The liquidity available in the reserve + * @param availableLiquidity The liquidity available in the corresponding aToken * @param totalStableDebt The total borrowed from the reserve a stable rate * @param totalVariableDebt The total borrowed from the reserve at a variable rate * @param averageStableBorrowRate The weighted average of all the stable rate loans @@ -122,7 +171,7 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { uint256 averageStableBorrowRate, uint256 reserveFactor ) - external + public view override returns ( @@ -138,15 +187,16 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { vars.currentStableBorrowRate = 0; vars.currentLiquidityRate = 0; - uint256 utilizationRate = - vars.totalDebt == 0 ? 0 : vars.totalDebt.rayDiv(availableLiquidity.add(vars.totalDebt)); + vars.utilizationRate = vars.totalDebt == 0 + ? 0 + : vars.totalDebt.rayDiv(availableLiquidity.add(vars.totalDebt)); vars.currentStableBorrowRate = ILendingRateOracle(addressesProvider.getLendingRateOracle()) .getMarketBorrowRate(reserve); - if (utilizationRate > OPTIMAL_UTILIZATION_RATE) { + if (vars.utilizationRate > OPTIMAL_UTILIZATION_RATE) { uint256 excessUtilizationRateRatio = - utilizationRate.sub(OPTIMAL_UTILIZATION_RATE).rayDiv(EXCESS_UTILIZATION_RATE); + vars.utilizationRate.sub(OPTIMAL_UTILIZATION_RATE).rayDiv(EXCESS_UTILIZATION_RATE); vars.currentStableBorrowRate = vars.currentStableBorrowRate.add(_stableRateSlope1).add( _stableRateSlope2.rayMul(excessUtilizationRateRatio) @@ -157,10 +207,10 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { ); } else { vars.currentStableBorrowRate = vars.currentStableBorrowRate.add( - _stableRateSlope1.rayMul(utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE)) + _stableRateSlope1.rayMul(vars.utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE)) ); vars.currentVariableBorrowRate = _baseVariableBorrowRate.add( - utilizationRate.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_UTILIZATION_RATE) + vars.utilizationRate.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_UTILIZATION_RATE) ); } @@ -171,7 +221,7 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { .currentVariableBorrowRate, averageStableBorrowRate ) - .rayMul(utilizationRate) + .rayMul(vars.utilizationRate) .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(reserveFactor)); return ( diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 40c7154e..8e38650c 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -49,10 +49,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage using PercentageMath for uint256; using SafeERC20 for IERC20; - //main configuration parameters - uint256 public constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 2500; - uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9; - uint256 public constant MAX_NUMBER_RESERVES = 128; uint256 public constant LENDINGPOOL_REVISION = 0x2; modifier whenNotPaused() { @@ -89,6 +85,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage **/ function initialize(ILendingPoolAddressesProvider provider) public initializer { _addressesProvider = provider; + _maxStableRateBorrowSizePercent = 2500; + _flashLoanPremiumTotal = 9; + _maxNumberOfReserves = 128; } /** @@ -283,6 +282,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount); + IAToken(aToken).handleRepayment(msg.sender, paybackAmount); + emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); return paybackAmount; @@ -500,7 +501,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage for (vars.i = 0; vars.i < assets.length; vars.i++) { aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; - premiums[vars.i] = amounts[vars.i].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000); + premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000); IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); } @@ -704,6 +705,27 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage return _addressesProvider; } + /** + * @dev Returns the percentage of available liquidity that can be borrowed at once at stable rate + */ + function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() public view returns (uint256) { + return _maxStableRateBorrowSizePercent; + } + + /** + * @dev Returns the fee on flash loans + */ + function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { + return _flashLoanPremiumTotal; + } + + /** + * @dev Returns the maximum number of reserves supported to be listed in this LendingPool + */ + function MAX_NUMBER_RESERVES() public view returns (uint256) { + return _maxNumberOfReserves; + } + /** * @dev Validates and finalizes an aToken transfer * - Only callable by the overlying aToken of the `asset` @@ -848,7 +870,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage vars.amount, amountInETH, vars.interestRateMode, - MAX_STABLE_RATE_BORROW_SIZE_PERCENT, + _maxStableRateBorrowSizePercent, _reserves, userConfig, _reservesList, @@ -910,7 +932,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function _addReserveToList(address asset) internal { uint256 reservesCount = _reservesCount; - require(reservesCount < MAX_NUMBER_RESERVES, Errors.LP_NO_MORE_RESERVES_ALLOWED); + require(reservesCount < _maxNumberOfReserves, Errors.LP_NO_MORE_RESERVES_ALLOWED); bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index bb3fb774..49451d92 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -10,11 +10,14 @@ import { import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPool} from '../../interfaces/ILendingPool.sol'; -import {ITokenConfiguration} from '../../interfaces/ITokenConfiguration.sol'; import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; +import {IInitializableDebtToken} from '../../interfaces/IInitializableDebtToken.sol'; +import {IInitializableAToken} from '../../interfaces/IInitializableAToken.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; +import {ILendingPoolConfigurator} from '../../interfaces/ILendingPoolConfigurator.sol'; /** * @title LendingPoolConfigurator contract @@ -22,147 +25,11 @@ import {DataTypes} from '../libraries/types/DataTypes.sol'; * @dev Implements the configuration methods for the Aave protocol **/ -contract LendingPoolConfigurator is VersionedInitializable { +contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigurator { using SafeMath for uint256; using PercentageMath for uint256; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - /** - * @dev Emitted when a reserve is initialized. - * @param asset The address of the underlying asset of the reserve - * @param aToken The address of the associated aToken contract - * @param stableDebtToken The address of the associated stable rate debt token - * @param variableDebtToken The address of the associated variable rate debt token - * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve - **/ - event ReserveInitialized( - address indexed asset, - address indexed aToken, - address stableDebtToken, - address variableDebtToken, - address interestRateStrategyAddress - ); - - /** - * @dev Emitted when borrowing is enabled on a reserve - * @param asset The address of the underlying asset of the reserve - * @param stableRateEnabled True if stable rate borrowing is enabled, false otherwise - **/ - event BorrowingEnabledOnReserve(address indexed asset, bool stableRateEnabled); - - /** - * @dev Emitted when borrowing is disabled on a reserve - * @param asset The address of the underlying asset of the reserve - **/ - event BorrowingDisabledOnReserve(address indexed asset); - - /** - * @dev Emitted when the collateralization risk parameters for the specified asset are updated. - * @param asset The address of the underlying asset of the reserve - * @param ltv The loan to value of the asset when used as collateral - * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized - * @param liquidationBonus The bonus liquidators receive to liquidate this asset - **/ - event CollateralConfigurationChanged( - address indexed asset, - uint256 ltv, - uint256 liquidationThreshold, - uint256 liquidationBonus - ); - - /** - * @dev Emitted when stable rate borrowing is enabled on a reserve - * @param asset The address of the underlying asset of the reserve - **/ - event StableRateEnabledOnReserve(address indexed asset); - - /** - * @dev Emitted when stable rate borrowing is disabled on a reserve - * @param asset The address of the underlying asset of the reserve - **/ - event StableRateDisabledOnReserve(address indexed asset); - - /** - * @dev Emitted when a reserve is activated - * @param asset The address of the underlying asset of the reserve - **/ - event ReserveActivated(address indexed asset); - - /** - * @dev Emitted when a reserve is deactivated - * @param asset The address of the underlying asset of the reserve - **/ - event ReserveDeactivated(address indexed asset); - - /** - * @dev Emitted when a reserve is frozen - * @param asset The address of the underlying asset of the reserve - **/ - event ReserveFrozen(address indexed asset); - - /** - * @dev Emitted when a reserve is unfrozen - * @param asset The address of the underlying asset of the reserve - **/ - event ReserveUnfrozen(address indexed asset); - - /** - * @dev Emitted when a reserve factor is updated - * @param asset The address of the underlying asset of the reserve - * @param factor The new reserve factor - **/ - event ReserveFactorChanged(address indexed asset, uint256 factor); - - /** - * @dev Emitted when the reserve decimals are updated - * @param asset The address of the underlying asset of the reserve - * @param decimals The new decimals - **/ - event ReserveDecimalsChanged(address indexed asset, uint256 decimals); - - /** - * @dev Emitted when a reserve interest strategy contract is updated - * @param asset The address of the underlying asset of the reserve - * @param strategy The new address of the interest strategy contract - **/ - event ReserveInterestRateStrategyChanged(address indexed asset, address strategy); - - /** - * @dev Emitted when an aToken implementation is upgraded - * @param asset The address of the underlying asset of the reserve - * @param proxy The aToken proxy address - * @param implementation The new aToken implementation - **/ - event ATokenUpgraded( - address indexed asset, - address indexed proxy, - address indexed implementation - ); - - /** - * @dev Emitted when the implementation of a stable debt token is upgraded - * @param asset The address of the underlying asset of the reserve - * @param proxy The stable debt token proxy address - * @param implementation The new aToken implementation - **/ - event StableDebtTokenUpgraded( - address indexed asset, - address indexed proxy, - address indexed implementation - ); - - /** - * @dev Emitted when the implementation of a variable debt token is upgraded - * @param asset The address of the underlying asset of the reserve - * @param proxy The variable debt token proxy address - * @param implementation The new aToken implementation - **/ - event VariableDebtTokenUpgraded( - address indexed asset, - address indexed proxy, - address indexed implementation - ); - ILendingPoolAddressesProvider internal addressesProvider; ILendingPool internal pool; @@ -191,114 +58,189 @@ contract LendingPoolConfigurator is VersionedInitializable { } /** - * @dev Initializes a reserve - * @param aTokenImpl The address of the aToken contract implementation - * @param stableDebtTokenImpl The address of the stable debt token contract - * @param variableDebtTokenImpl The address of the variable debt token contract - * @param underlyingAssetDecimals The decimals of the reserve underlying asset - * @param interestRateStrategyAddress The address of the interest rate strategy contract for this reserve + * @dev Initializes reserves in batch **/ - function initReserve( - address aTokenImpl, - address stableDebtTokenImpl, - address variableDebtTokenImpl, - uint8 underlyingAssetDecimals, - address interestRateStrategyAddress - ) public onlyPoolAdmin { - address asset = ITokenConfiguration(aTokenImpl).UNDERLYING_ASSET_ADDRESS(); + function batchInitReserve(InitReserveInput[] calldata input) external onlyPoolAdmin { + ILendingPool cachedPool = pool; + for (uint256 i = 0; i < input.length; i++) { + _initReserve(cachedPool, input[i]); + } + } - require( - address(pool) == ITokenConfiguration(aTokenImpl).POOL(), - Errors.LPC_INVALID_ATOKEN_POOL_ADDRESS - ); - require( - address(pool) == ITokenConfiguration(stableDebtTokenImpl).POOL(), - Errors.LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS - ); - require( - address(pool) == ITokenConfiguration(variableDebtTokenImpl).POOL(), - Errors.LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS - ); - require( - asset == ITokenConfiguration(stableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(), - Errors.LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS - ); - require( - asset == ITokenConfiguration(variableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(), - Errors.LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS - ); - - address aTokenProxyAddress = _initTokenWithProxy(aTokenImpl, underlyingAssetDecimals); + function _initReserve(ILendingPool pool, InitReserveInput calldata input) internal { + address aTokenProxyAddress = + _initTokenWithProxy( + input.aTokenImpl, + abi.encodeWithSelector( + IInitializableAToken.initialize.selector, + pool, + input.treasury, + input.underlyingAsset, + IAaveIncentivesController(input.incentivesController), + input.underlyingAssetDecimals, + input.aTokenName, + input.aTokenSymbol, + input.params + ) + ); address stableDebtTokenProxyAddress = - _initTokenWithProxy(stableDebtTokenImpl, underlyingAssetDecimals); + _initTokenWithProxy( + input.stableDebtTokenImpl, + abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + pool, + input.underlyingAsset, + IAaveIncentivesController(input.incentivesController), + input.underlyingAssetDecimals, + input.stableDebtTokenName, + input.stableDebtTokenSymbol, + input.params + ) + ); address variableDebtTokenProxyAddress = - _initTokenWithProxy(variableDebtTokenImpl, underlyingAssetDecimals); + _initTokenWithProxy( + input.variableDebtTokenImpl, + abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + pool, + input.underlyingAsset, + IAaveIncentivesController(input.incentivesController), + input.underlyingAssetDecimals, + input.variableDebtTokenName, + input.variableDebtTokenSymbol, + input.params + ) + ); pool.initReserve( - asset, + input.underlyingAsset, aTokenProxyAddress, stableDebtTokenProxyAddress, variableDebtTokenProxyAddress, - interestRateStrategyAddress + input.interestRateStrategyAddress ); - DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + DataTypes.ReserveConfigurationMap memory currentConfig = + pool.getConfiguration(input.underlyingAsset); - currentConfig.setDecimals(underlyingAssetDecimals); + currentConfig.setDecimals(input.underlyingAssetDecimals); currentConfig.setActive(true); currentConfig.setFrozen(false); - pool.setConfiguration(asset, currentConfig.data); + pool.setConfiguration(input.underlyingAsset, currentConfig.data); emit ReserveInitialized( - asset, + input.underlyingAsset, aTokenProxyAddress, stableDebtTokenProxyAddress, variableDebtTokenProxyAddress, - interestRateStrategyAddress + input.interestRateStrategyAddress ); } /** * @dev Updates the aToken implementation for the reserve - * @param asset The address of the underlying asset of the reserve to be updated - * @param implementation The address of the new aToken implementation **/ - function updateAToken(address asset, address implementation) external onlyPoolAdmin { - DataTypes.ReserveData memory reserveData = pool.getReserveData(asset); + function updateAToken(UpdateATokenInput calldata input) external onlyPoolAdmin { + ILendingPool cachedPool = pool; - _upgradeTokenImplementation(asset, reserveData.aTokenAddress, implementation); + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); - emit ATokenUpgraded(asset, reserveData.aTokenAddress, implementation); + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); + + bytes memory encodedCall = abi.encodeWithSelector( + IInitializableAToken.initialize.selector, + cachedPool, + input.treasury, + input.asset, + input.incentivesController, + decimals, + input.name, + input.symbol, + input.params + ); + + _upgradeTokenImplementation( + reserveData.aTokenAddress, + input.implementation, + encodedCall + ); + + emit ATokenUpgraded(input.asset, reserveData.aTokenAddress, input.implementation); } /** * @dev Updates the stable debt token implementation for the reserve - * @param asset The address of the underlying asset of the reserve to be updated - * @param implementation The address of the new aToken implementation **/ - function updateStableDebtToken(address asset, address implementation) external onlyPoolAdmin { - DataTypes.ReserveData memory reserveData = pool.getReserveData(asset); + function updateStableDebtToken(UpdateDebtTokenInput calldata input) external onlyPoolAdmin { + ILendingPool cachedPool = pool; - _upgradeTokenImplementation(asset, reserveData.stableDebtTokenAddress, implementation); + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); + + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); - emit StableDebtTokenUpgraded(asset, reserveData.stableDebtTokenAddress, implementation); + bytes memory encodedCall = abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + cachedPool, + input.asset, + input.incentivesController, + decimals, + input.name, + input.symbol, + input.params + ); + + _upgradeTokenImplementation( + reserveData.stableDebtTokenAddress, + input.implementation, + encodedCall + ); + + emit StableDebtTokenUpgraded( + input.asset, + reserveData.stableDebtTokenAddress, + input.implementation + ); } /** * @dev Updates the variable debt token implementation for the asset - * @param asset The address of the underlying asset of the reserve to be updated - * @param implementation The address of the new aToken implementation **/ - function updateVariableDebtToken(address asset, address implementation) external onlyPoolAdmin { - DataTypes.ReserveData memory reserveData = pool.getReserveData(asset); + function updateVariableDebtToken(UpdateDebtTokenInput calldata input) + external + onlyPoolAdmin + { + ILendingPool cachedPool = pool; - _upgradeTokenImplementation(asset, reserveData.variableDebtTokenAddress, implementation); + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); - emit VariableDebtTokenUpgraded(asset, reserveData.variableDebtTokenAddress, implementation); + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); + + bytes memory encodedCall = abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + cachedPool, + input.asset, + input.incentivesController, + decimals, + input.name, + input.symbol, + input.params + ); + + _upgradeTokenImplementation( + reserveData.variableDebtTokenAddress, + input.implementation, + encodedCall + ); + + emit VariableDebtTokenUpgraded( + input.asset, + reserveData.variableDebtTokenAddress, + input.implementation + ); } /** @@ -509,44 +451,27 @@ contract LendingPoolConfigurator is VersionedInitializable { pool.setPause(val); } - function _initTokenWithProxy(address implementation, uint8 decimals) internal returns (address) { + function _initTokenWithProxy(address implementation, bytes memory initParams) + internal + returns (address) + { InitializableImmutableAdminUpgradeabilityProxy proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this)); - bytes memory params = - abi.encodeWithSignature( - 'initialize(uint8,string,string)', - decimals, - IERC20Detailed(implementation).name(), - IERC20Detailed(implementation).symbol() - ); - - proxy.initialize(implementation, params); + proxy.initialize(implementation, initParams); return address(proxy); } function _upgradeTokenImplementation( - address asset, address proxyAddress, - address implementation + address implementation, + bytes memory initParams ) internal { InitializableImmutableAdminUpgradeabilityProxy proxy = InitializableImmutableAdminUpgradeabilityProxy(payable(proxyAddress)); - DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(asset); - - (, , , uint256 decimals, ) = configuration.getParamsMemory(); - - bytes memory params = - abi.encodeWithSignature( - 'initialize(uint8,string,string)', - uint8(decimals), - IERC20Detailed(implementation).name(), - IERC20Detailed(implementation).symbol() - ); - - proxy.upgradeToAndCall(implementation, params); + proxy.upgradeToAndCall(implementation, initParams); } function _checkNoLiquidity(address asset) internal view { diff --git a/contracts/protocol/lendingpool/LendingPoolStorage.sol b/contracts/protocol/lendingpool/LendingPoolStorage.sol index 4edff4a3..198a3eea 100644 --- a/contracts/protocol/lendingpool/LendingPoolStorage.sol +++ b/contracts/protocol/lendingpool/LendingPoolStorage.sol @@ -23,4 +23,10 @@ contract LendingPoolStorage { uint256 internal _reservesCount; bool internal _paused; + + uint256 internal _maxStableRateBorrowSizePercent; + + uint256 internal _flashLoanPremiumTotal; + + uint256 internal _maxNumberOfReserves; } diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 3b0884c2..2b5b2cf4 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -216,15 +216,15 @@ library ReserveLogic { .scaledTotalSupply() .rayMul(reserve.variableBorrowIndex); - vars.availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress); - ( vars.newLiquidityRate, vars.newStableRate, vars.newVariableRate ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( reserveAddress, - vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken), + aTokenAddress, + liquidityAdded, + liquidityTaken, vars.totalStableDebt, vars.totalVariableDebt, vars.avgStableRate, diff --git a/contracts/protocol/tokenization/AToken.sol b/contracts/protocol/tokenization/AToken.sol index 00df0b61..f2784732 100644 --- a/contracts/protocol/tokenization/AToken.sol +++ b/contracts/protocol/tokenization/AToken.sol @@ -9,13 +9,18 @@ import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; import {IncentivizedERC20} from './IncentivizedERC20.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; /** * @title Aave ERC20 AToken * @dev Implementation of the interest bearing token for the Aave protocol * @author Aave */ -contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { +contract AToken is + VersionedInitializable, + IncentivizedERC20('ATOKEN_IMPL', 'ATOKEN_IMPL', 0), + IAToken +{ using WadRayMath for uint256; using SafeERC20 for IERC20; @@ -25,44 +30,47 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { bytes32 public constant PERMIT_TYPEHASH = keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'); - uint256 public constant UINT_MAX_VALUE = uint256(-1); uint256 public constant ATOKEN_REVISION = 0x1; - address public immutable UNDERLYING_ASSET_ADDRESS; - address public immutable RESERVE_TREASURY_ADDRESS; - ILendingPool public immutable POOL; /// @dev owner => next valid nonce to submit with permit() mapping(address => uint256) public _nonces; bytes32 public DOMAIN_SEPARATOR; - modifier onlyLendingPool { - require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_LENDING_POOL); - _; - } + ILendingPool internal _pool; + address internal _treasury; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; - constructor( - ILendingPool pool, - address underlyingAssetAddress, - address reserveTreasuryAddress, - string memory tokenName, - string memory tokenSymbol, - address incentivesController - ) public IncentivizedERC20(tokenName, tokenSymbol, 18, incentivesController) { - POOL = pool; - UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; - RESERVE_TREASURY_ADDRESS = reserveTreasuryAddress; + modifier onlyLendingPool { + require(_msgSender() == address(_pool), Errors.CT_CALLER_MUST_BE_LENDING_POOL); + _; } function getRevision() internal pure virtual override returns (uint256) { return ATOKEN_REVISION; } + /** + * @dev Initializes the aToken + * @param pool The address of the lending pool where this aToken will be used + * @param treasury The address of the Aave treasury, receiving the fees on this aToken + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's + * @param aTokenName The name of the aToken + * @param aTokenSymbol The symbol of the aToken + */ function initialize( - uint8 underlyingAssetDecimals, - string calldata tokenName, - string calldata tokenSymbol - ) external virtual initializer { + ILendingPool pool, + address treasury, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 aTokenDecimals, + string calldata aTokenName, + string calldata aTokenSymbol, + bytes calldata params + ) external override initializer { uint256 chainId; //solium-disable-next-line @@ -73,16 +81,32 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { DOMAIN_SEPARATOR = keccak256( abi.encode( EIP712_DOMAIN, - keccak256(bytes(tokenName)), + keccak256(bytes(aTokenName)), keccak256(EIP712_REVISION), chainId, address(this) ) ); - _setName(tokenName); - _setSymbol(tokenSymbol); - _setDecimals(underlyingAssetDecimals); + _setName(aTokenName); + _setSymbol(aTokenSymbol); + _setDecimals(aTokenDecimals); + + _pool = pool; + _treasury = treasury; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; + + emit Initialized( + underlyingAsset, + address(pool), + treasury, + address(incentivesController), + aTokenDecimals, + aTokenName, + aTokenSymbol, + params + ); } /** @@ -103,7 +127,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); _burn(user, amountScaled); - IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount); + IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount); emit Transfer(user, address(0), amount); emit Burn(user, receiverOfUnderlying, amount, index); @@ -145,14 +169,16 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { return; } + address treasury = _treasury; + // Compared to the normal mint, we don't check for rounding errors. // The amount to mint can easily be very small since it is a fraction of the interest ccrued. // In that case, the treasury will experience a (very small) loss, but it // wont cause potentially valid transactions to fail. - _mint(RESERVE_TREASURY_ADDRESS, amount.rayDiv(index)); + _mint(treasury, amount.rayDiv(index)); - emit Transfer(address(0), RESERVE_TREASURY_ADDRESS, amount); - emit Mint(RESERVE_TREASURY_ADDRESS, amount, index); + emit Transfer(address(0), treasury, amount); + emit Mint(treasury, amount, index); } /** @@ -185,7 +211,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { override(IncentivizedERC20, IERC20) returns (uint256) { - return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); + return super.balanceOf(user).rayMul(_pool.getReserveNormalizedIncome(_underlyingAsset)); } /** @@ -226,7 +252,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { return 0; } - return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); + return currentSupplyScaled.rayMul(_pool.getReserveNormalizedIncome(_underlyingAsset)); } /** @@ -237,6 +263,41 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { return super.totalSupply(); } + /** + * @dev Returns the address of the Aave treasury, receiving the fees on this aToken + **/ + function RESERVE_TREASURY_ADDRESS() public view returns (address) { + return _treasury; + } + + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + /** + * @dev For internal usage in the logic of the parent contract IncentivizedERC20 + **/ + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + /** * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer * assets in borrow(), withdraw() and flashLoan() @@ -250,10 +311,17 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { onlyLendingPool returns (uint256) { - IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(target, amount); + IERC20(_underlyingAsset).safeTransfer(target, amount); return amount; } + /** + * @dev Invoked to execute actions on the aToken side after a repayment. + * @param user The user executing the repayment + * @param amount The amount getting repaid + **/ + function handleRepayment(address user, uint256 amount) external override onlyLendingPool {} + /** * @dev implements the permit function as for * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md @@ -305,7 +373,10 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { uint256 amount, bool validate ) internal { - uint256 index = POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS); + address underlyingAsset = _underlyingAsset; + ILendingPool pool = _pool; + + uint256 index = pool.getReserveNormalizedIncome(underlyingAsset); uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index); uint256 toBalanceBefore = super.balanceOf(to).rayMul(index); @@ -313,14 +384,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { super._transfer(from, to, amount.rayDiv(index)); if (validate) { - POOL.finalizeTransfer( - UNDERLYING_ASSET_ADDRESS, - from, - to, - amount, - fromBalanceBefore, - toBalanceBefore - ); + pool.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore); } emit BalanceTransfer(from, to, amount, index); diff --git a/contracts/protocol/tokenization/DelegationAwareAToken.sol b/contracts/protocol/tokenization/DelegationAwareAToken.sol index 4e49f2b6..443e10fb 100644 --- a/contracts/protocol/tokenization/DelegationAwareAToken.sol +++ b/contracts/protocol/tokenization/DelegationAwareAToken.sol @@ -14,36 +14,17 @@ import {AToken} from './AToken.sol'; contract DelegationAwareAToken is AToken { modifier onlyPoolAdmin { require( - _msgSender() == ILendingPool(POOL).getAddressesProvider().getPoolAdmin(), + _msgSender() == ILendingPool(_pool).getAddressesProvider().getPoolAdmin(), Errors.CALLER_NOT_POOL_ADMIN ); _; } - constructor( - ILendingPool pool, - address underlyingAssetAddress, - address reserveTreasury, - string memory tokenName, - string memory tokenSymbol, - address incentivesController - ) - public - AToken( - pool, - underlyingAssetAddress, - reserveTreasury, - tokenName, - tokenSymbol, - incentivesController - ) - {} - /** * @dev Delegates voting power of the underlying asset to a `delegatee` address * @param delegatee The address that will receive the delegation **/ function delegateUnderlyingTo(address delegatee) external onlyPoolAdmin { - IDelegationToken(UNDERLYING_ASSET_ADDRESS).delegate(delegatee); + IDelegationToken(_underlyingAsset).delegate(delegatee); } } diff --git a/contracts/protocol/tokenization/IncentivizedERC20.sol b/contracts/protocol/tokenization/IncentivizedERC20.sol index 101eaf9a..fb831769 100644 --- a/contracts/protocol/tokenization/IncentivizedERC20.sol +++ b/contracts/protocol/tokenization/IncentivizedERC20.sol @@ -12,11 +12,9 @@ import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesControl * @notice Basic ERC20 implementation * @author Aave, inspired by the Openzeppelin ERC20 implementation **/ -contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { +abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { using SafeMath for uint256; - IAaveIncentivesController internal immutable _incentivesController; - mapping(address => uint256) internal _balances; mapping(address => mapping(address => uint256)) private _allowances; @@ -28,13 +26,11 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { constructor( string memory name, string memory symbol, - uint8 decimals, - address incentivesController + uint8 decimals ) public { _name = name; _symbol = symbol; _decimals = decimals; - _incentivesController = IAaveIncentivesController(incentivesController); } /** @@ -72,6 +68,12 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { return _balances[account]; } + /** + * @return Abstract function implemented by the child aToken/debtToken. + * Done this way in order to not break compatibility with previous versions of aTokens/debtTokens + **/ + function _getIncentivesController() internal view virtual returns(IAaveIncentivesController); + /** * @dev Executes a transfer of tokens from _msgSender() to recipient * @param recipient The recipient of the tokens @@ -180,11 +182,11 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { uint256 oldRecipientBalance = _balances[recipient]; _balances[recipient] = _balances[recipient].add(amount); - if (address(_incentivesController) != address(0)) { + if (address(_getIncentivesController()) != address(0)) { uint256 currentTotalSupply = _totalSupply; - _incentivesController.handleAction(sender, currentTotalSupply, oldSenderBalance); + _getIncentivesController().handleAction(sender, currentTotalSupply, oldSenderBalance); if (sender != recipient) { - _incentivesController.handleAction(recipient, currentTotalSupply, oldRecipientBalance); + _getIncentivesController().handleAction(recipient, currentTotalSupply, oldRecipientBalance); } } } @@ -200,8 +202,8 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { uint256 oldAccountBalance = _balances[account]; _balances[account] = oldAccountBalance.add(amount); - if (address(_incentivesController) != address(0)) { - _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); + if (address(_getIncentivesController()) != address(0)) { + _getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance); } } @@ -216,8 +218,8 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { uint256 oldAccountBalance = _balances[account]; _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); - if (address(_incentivesController) != address(0)) { - _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); + if (address(_getIncentivesController()) != address(0)) { + _getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance); } } diff --git a/contracts/protocol/tokenization/StableDebtToken.sol b/contracts/protocol/tokenization/StableDebtToken.sol index 78401405..2212e9cf 100644 --- a/contracts/protocol/tokenization/StableDebtToken.sol +++ b/contracts/protocol/tokenization/StableDebtToken.sol @@ -5,6 +5,8 @@ import {DebtTokenBase} from './base/DebtTokenBase.sol'; import {MathUtils} from '../libraries/math/MathUtils.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; /** @@ -23,13 +25,46 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { mapping(address => uint256) internal _usersStableRate; uint40 internal _totalSupplyTimestamp; - constructor( - address pool, + ILendingPool internal _pool; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, address underlyingAsset, - string memory name, - string memory symbol, - address incentivesController - ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {} + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol, + bytes calldata params + ) public override initializer { + _setName(debtTokenName); + _setSymbol(debtTokenSymbol); + _setDecimals(debtTokenDecimals); + + _pool = pool; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; + + emit Initialized( + underlyingAsset, + address(pool), + address(incentivesController), + debtTokenDecimals, + debtTokenName, + debtTokenSymbol, + params + ); + } /** * @dev Gets the revision of the stable debt token implementation @@ -300,6 +335,48 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { return super.balanceOf(user); } + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getUnderlyingAssetAddress() internal view override returns (address) { + return _underlyingAsset; + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getLendingPool() internal view override returns (ILendingPool) { + return _pool; + } + /** * @dev Calculates the total supply * @param avgRate The average rate at which the total supply increases diff --git a/contracts/protocol/tokenization/VariableDebtToken.sol b/contracts/protocol/tokenization/VariableDebtToken.sol index d2e27ac3..a7a28176 100644 --- a/contracts/protocol/tokenization/VariableDebtToken.sol +++ b/contracts/protocol/tokenization/VariableDebtToken.sol @@ -5,6 +5,8 @@ import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {DebtTokenBase} from './base/DebtTokenBase.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; /** * @title VariableDebtToken @@ -17,13 +19,46 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { uint256 public constant DEBT_TOKEN_REVISION = 0x1; - constructor( - address pool, + ILendingPool internal _pool; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, address underlyingAsset, - string memory name, - string memory symbol, - address incentivesController - ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {} + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol, + bytes calldata params + ) public override initializer { + _setName(debtTokenName); + _setSymbol(debtTokenSymbol); + _setDecimals(debtTokenDecimals); + + _pool = pool; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; + + emit Initialized( + underlyingAsset, + address(pool), + address(incentivesController), + debtTokenDecimals, + debtTokenName, + debtTokenSymbol, + params + ); + } /** * @dev Gets the revision of the stable debt token implementation @@ -44,7 +79,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { return 0; } - return scaledBalance.rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS)); + return scaledBalance.rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAsset)); } /** @@ -113,8 +148,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { * @return The total supply **/ function totalSupply() public view virtual override returns (uint256) { - return - super.totalSupply().rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS)); + return super.totalSupply().rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAsset)); } /** @@ -139,4 +173,37 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { { return (super.balanceOf(user), super.totalSupply()); } + + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + function _getUnderlyingAssetAddress() internal view override returns (address) { + return _underlyingAsset; + } + + function _getLendingPool() internal view override returns (ILendingPool) { + return _pool; + } } diff --git a/contracts/protocol/tokenization/base/DebtTokenBase.sol b/contracts/protocol/tokenization/base/DebtTokenBase.sol index 07bdef22..4d75bc2f 100644 --- a/contracts/protocol/tokenization/base/DebtTokenBase.sol +++ b/contracts/protocol/tokenization/base/DebtTokenBase.sol @@ -16,54 +16,20 @@ import {Errors} from '../../libraries/helpers/Errors.sol'; */ abstract contract DebtTokenBase is - IncentivizedERC20, + IncentivizedERC20('DEBTTOKEN_IMPL', 'DEBTTOKEN_IMPL', 0), VersionedInitializable, ICreditDelegationToken { - address public immutable UNDERLYING_ASSET_ADDRESS; - ILendingPool public immutable POOL; - mapping(address => mapping(address => uint256)) internal _borrowAllowances; /** * @dev Only lending pool can call functions marked by this modifier **/ modifier onlyLendingPool { - require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_LENDING_POOL); + require(_msgSender() == address(_getLendingPool()), Errors.CT_CALLER_MUST_BE_LENDING_POOL); _; } - /** - * @dev The metadata of the token will be set on the proxy, that the reason of - * passing "NULL" and 0 as metadata - */ - constructor( - address pool, - address underlyingAssetAddress, - string memory name, - string memory symbol, - address incentivesController - ) public IncentivizedERC20(name, symbol, 18, incentivesController) { - POOL = ILendingPool(pool); - UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; - } - - /** - * @dev Initializes the debt token. - * @param name The name of the token - * @param symbol The symbol of the token - * @param decimals The decimals of the token - */ - function initialize( - uint8 decimals, - string memory name, - string memory symbol - ) public initializer { - _setName(name); - _setSymbol(symbol); - _setDecimals(decimals); - } - /** * @dev delegates borrowing power to a user on the specific debt token * @param delegatee the address receiving the delegated borrowing power @@ -73,7 +39,7 @@ abstract contract DebtTokenBase is **/ function approveDelegation(address delegatee, uint256 amount) external override { _borrowAllowances[_msgSender()][delegatee] = amount; - emit BorrowAllowanceDelegated(_msgSender(), delegatee, UNDERLYING_ASSET_ADDRESS, amount); + emit BorrowAllowanceDelegated(_msgSender(), delegatee, _getUnderlyingAssetAddress(), amount); } /** @@ -162,6 +128,10 @@ abstract contract DebtTokenBase is _borrowAllowances[delegator][delegatee] = newAllowance; - emit BorrowAllowanceDelegated(delegator, delegatee, UNDERLYING_ASSET_ADDRESS, newAllowance); + emit BorrowAllowanceDelegated(delegator, delegatee, _getUnderlyingAssetAddress(), newAllowance); } + + function _getUnderlyingAssetAddress() internal view virtual returns (address); + + function _getLendingPool() internal view virtual returns (ILendingPool); } diff --git a/docker-compose.yml b/docker-compose.yml index f6cca30f..7c94bcb3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,3 +19,5 @@ services: TENDERLY_PROJECT: ${TENDERLY_PROJECT} TENDERLY_USERNAME: ${TENDERLY_USERNAME} ALCHEMY_KEY: ${ALCHEMY_KEY} + TENDERLY_FORK_ID: ${TENDERLY_FORK_ID} + TENDERLY_HEAD_ID: ${TENDERLY_HEAD_ID} diff --git a/hardhat.config.ts b/hardhat.config.ts index a6d411ea..0c6fa1e1 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -3,8 +3,9 @@ import fs from 'fs'; import { HardhatUserConfig } from 'hardhat/types'; // @ts-ignore import { accounts } from './test-wallets.js'; -import { eEthereumNetwork } from './helpers/types'; +import { eEthereumNetwork, eNetwork, ePolygonNetwork, eXDaiNetwork } from './helpers/types'; import { BUIDLEREVM_CHAINID, COVERAGE_CHAINID } from './helpers/buidler-constants'; +import { NETWORKS_RPC_URL, NETWORKS_DEFAULT_GAS } from './helper-hardhat-config'; require('dotenv').config(); @@ -18,10 +19,7 @@ import '@tenderly/hardhat-tenderly'; const SKIP_LOAD = process.env.SKIP_LOAD === 'true'; const DEFAULT_BLOCK_GAS_LIMIT = 12450000; const DEFAULT_GAS_MUL = 5; -const DEFAULT_GAS_PRICE = 65000000000; const HARDFORK = 'istanbul'; -const INFURA_KEY = process.env.INFURA_KEY || ''; -const ALCHEMY_KEY = process.env.ALCHEMY_KEY || ''; const ETHERSCAN_KEY = process.env.ETHERSCAN_KEY || ''; const MNEMONIC_PATH = "m/44'/60'/0'/0"; const MNEMONIC = process.env.MNEMONIC || ''; @@ -43,32 +41,25 @@ if (!SKIP_LOAD) { require(`${path.join(__dirname, 'tasks/misc')}/set-bre.ts`); -const getCommonNetworkConfig = (networkName: eEthereumNetwork, networkId: number) => { - const net = networkName === 'main' ? 'mainnet' : networkName; - return { - url: ALCHEMY_KEY - ? `https://eth-${net}.alchemyapi.io/v2/${ALCHEMY_KEY}` - : `https://${net}.infura.io/v3/${INFURA_KEY}`, - hardfork: HARDFORK, - blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT, - gasMultiplier: DEFAULT_GAS_MUL, - gasPrice: DEFAULT_GAS_PRICE, - chainId: networkId, - accounts: { - mnemonic: MNEMONIC, - path: MNEMONIC_PATH, - initialIndex: 0, - count: 20, - }, - }; -}; +const getCommonNetworkConfig = (networkName: eNetwork, networkId: number) => ({ + url: NETWORKS_RPC_URL[networkName], + hardfork: HARDFORK, + blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT, + gasMultiplier: DEFAULT_GAS_MUL, + gasPrice: NETWORKS_DEFAULT_GAS[networkName], + chainId: networkId, + accounts: { + mnemonic: MNEMONIC, + path: MNEMONIC_PATH, + initialIndex: 0, + count: 20, + }, +}); const mainnetFork = MAINNET_FORK ? { - blockNumber: 11608298, - url: ALCHEMY_KEY - ? `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_KEY}` - : `https://mainnet.infura.io/v3/${INFURA_KEY}`, + blockNumber: 11739065, + url: NETWORKS_RPC_URL['main'], } : undefined; @@ -103,7 +94,10 @@ const buidlerConfig: HardhatUserConfig = { kovan: getCommonNetworkConfig(eEthereumNetwork.kovan, 42), ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3), main: getCommonNetworkConfig(eEthereumNetwork.main, 1), - tenderlyMain: getCommonNetworkConfig(eEthereumNetwork.main, 1), + tenderlyMain: getCommonNetworkConfig(eEthereumNetwork.tenderlyMain, 3030), + matic: getCommonNetworkConfig(ePolygonNetwork.matic, 137), + mumbai: getCommonNetworkConfig(ePolygonNetwork.mumbai, 80001), + xdai: getCommonNetworkConfig(eXDaiNetwork.xdai, 100), hardhat: { hardfork: 'istanbul', blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT, diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts new file mode 100644 index 00000000..552a2e52 --- /dev/null +++ b/helper-hardhat-config.ts @@ -0,0 +1,47 @@ +// @ts-ignore +import { + eEthereumNetwork, + ePolygonNetwork, + eXDaiNetwork, + iParamsPerNetwork, +} from './helpers/types'; + +require('dotenv').config(); + +const INFURA_KEY = process.env.INFURA_KEY || ''; +const ALCHEMY_KEY = process.env.ALCHEMY_KEY || ''; +const TENDERLY_FORK_ID = process.env.TENDERLY_FORK_ID || ''; + +const GWEI = 1000 * 1000 * 1000; + +export const NETWORKS_RPC_URL: iParamsPerNetwork = { + [eEthereumNetwork.kovan]: ALCHEMY_KEY + ? `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_KEY}` + : `https://kovan.infura.io/v3/${INFURA_KEY}`, + [eEthereumNetwork.ropsten]: ALCHEMY_KEY + ? `https://eth-ropsten.alchemyapi.io/v2/${ALCHEMY_KEY}` + : `https://ropsten.infura.io/v3/${INFURA_KEY}`, + [eEthereumNetwork.main]: ALCHEMY_KEY + ? `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_KEY}` + : `https://mainnet.infura.io/v3/${INFURA_KEY}`, + [eEthereumNetwork.coverage]: 'http://localhost:8555', + [eEthereumNetwork.hardhat]: 'http://localhost:8545', + [eEthereumNetwork.buidlerevm]: 'http://localhost:8545', + [eEthereumNetwork.tenderlyMain]: `https://rpc.tenderly.co/fork/${TENDERLY_FORK_ID}`, + [ePolygonNetwork.mumbai]: 'https://rpc-mumbai.maticvigil.com', + [ePolygonNetwork.matic]: 'https://rpc-mainnet.matic.network', + [eXDaiNetwork.xdai]: 'https://rpc.xdaichain.com/', +}; + +export const NETWORKS_DEFAULT_GAS: iParamsPerNetwork = { + [eEthereumNetwork.kovan]: 65 * GWEI, + [eEthereumNetwork.ropsten]: 65 * GWEI, + [eEthereumNetwork.main]: 65 * GWEI, + [eEthereumNetwork.coverage]: 65 * GWEI, + [eEthereumNetwork.hardhat]: 65 * GWEI, + [eEthereumNetwork.buidlerevm]: 65 * GWEI, + [eEthereumNetwork.tenderlyMain]: 0.01 * GWEI, + [ePolygonNetwork.mumbai]: 1 * GWEI, + [ePolygonNetwork.matic]: 2 * GWEI, + [eXDaiNetwork.xdai]: 1 * GWEI, +}; diff --git a/helpers/configuration.ts b/helpers/configuration.ts index a06ce3b3..618eec82 100644 --- a/helpers/configuration.ts +++ b/helpers/configuration.ts @@ -4,10 +4,12 @@ import { IReserveParams, PoolConfiguration, ICommonConfiguration, - eEthereumNetwork, + eNetwork, } from './types'; import { getParamPerPool } from './contracts-helpers'; import AaveConfig from '../markets/aave'; +import MaticConfig from '../markets/matic'; +import AmmConfig from '../markets/amm'; import { CommonsConfig } from '../markets/aave/commons'; import { DRE, filterMapBy } from './misc-utils'; import { tEthereumAddress } from './types'; @@ -17,13 +19,18 @@ import { deployWETHMocked } from './contracts-deployments'; export enum ConfigNames { Commons = 'Commons', Aave = 'Aave', - Uniswap = 'Uniswap', + Matic = 'Matic', + Amm = 'Amm', } export const loadPoolConfig = (configName: ConfigNames): PoolConfiguration => { switch (configName) { case ConfigNames.Aave: return AaveConfig; + case ConfigNames.Matic: + return MaticConfig; + case ConfigNames.Amm: + return AmmConfig; case ConfigNames.Commons: return CommonsConfig; default: @@ -41,6 +48,12 @@ export const getReservesConfigByPool = (pool: AavePools): iMultiPoolsAssets => { const currentNetwork = process.env.MAINNET_FORK === 'true' ? 'main' : DRE.network.name; - const targetAddress = getParamPerNetwork(config.PoolAdmin, currentNetwork); + const targetAddress = getParamPerNetwork(config.PoolAdmin, currentNetwork); if (targetAddress) { return targetAddress; } @@ -64,7 +77,7 @@ export const getEmergencyAdmin = async ( config: ICommonConfiguration ): Promise => { const currentNetwork = process.env.MAINNET_FORK === 'true' ? 'main' : DRE.network.name; - const targetAddress = getParamPerNetwork(config.EmergencyAdmin, currentNetwork); + const targetAddress = getParamPerNetwork(config.EmergencyAdmin, currentNetwork); if (targetAddress) { return targetAddress; } @@ -79,17 +92,17 @@ export const getTreasuryAddress = async ( config: ICommonConfiguration ): Promise => { const currentNetwork = process.env.MAINNET_FORK === 'true' ? 'main' : DRE.network.name; - return getParamPerNetwork(config.ReserveFactorTreasuryAddress, currentNetwork); + return getParamPerNetwork(config.ReserveFactorTreasuryAddress, currentNetwork); }; export const getATokenDomainSeparatorPerNetwork = ( - network: eEthereumNetwork, + network: eNetwork, config: ICommonConfiguration ): tEthereumAddress => getParamPerNetwork(config.ATokenDomainSeparator, network); export const getWethAddress = async (config: ICommonConfiguration) => { const currentNetwork = process.env.MAINNET_FORK === 'true' ? 'main' : DRE.network.name; - const wethAddress = getParamPerNetwork(config.WETH, currentNetwork); + const wethAddress = getParamPerNetwork(config.WETH, currentNetwork); if (wethAddress) { return wethAddress; } diff --git a/helpers/constants.ts b/helpers/constants.ts index 708fe98d..95e8922b 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -28,3 +28,46 @@ export const TOKEN_DISTRIBUTOR_PERCENTAGE_BASE = '10000'; export const MOCK_USD_PRICE_IN_WEI = '5848466240000000'; export const USD_ADDRESS = '0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96'; export const AAVE_REFERRAL = '0'; + +export const MOCK_CHAINLINK_AGGREGATORS_PRICES = { + AAVE: oneEther.multipliedBy('0.003620948469').toFixed(), + BAT: oneEther.multipliedBy('0.00137893825230').toFixed(), + BUSD: oneEther.multipliedBy('0.00736484').toFixed(), + DAI: oneEther.multipliedBy('0.00369068412860').toFixed(), + ENJ: oneEther.multipliedBy('0.00029560').toFixed(), + KNC: oneEther.multipliedBy('0.001072').toFixed(), + LINK: oneEther.multipliedBy('0.009955').toFixed(), + MANA: oneEther.multipliedBy('0.000158').toFixed(), + MKR: oneEther.multipliedBy('2.508581').toFixed(), + REN: oneEther.multipliedBy('0.00065133').toFixed(), + SNX: oneEther.multipliedBy('0.00442616').toFixed(), + SUSD: oneEther.multipliedBy('0.00364714136416').toFixed(), + TUSD: oneEther.multipliedBy('0.00364714136416').toFixed(), + UNI: oneEther.multipliedBy('0.00536479').toFixed(), + USDC: oneEther.multipliedBy('0.00367714136416').toFixed(), + USDT: oneEther.multipliedBy('0.00369068412860').toFixed(), + WETH: oneEther.toFixed(), + WBTC: oneEther.multipliedBy('47.332685').toFixed(), + YFI: oneEther.multipliedBy('22.407436').toFixed(), + ZRX: oneEther.multipliedBy('0.001151').toFixed(), + UniDAIWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniWBTCWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniAAVEWETH: oneEther.multipliedBy('0.003620948469').toFixed(), + UniBATWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniDAIUSDC: oneEther.multipliedBy('22.407436').toFixed(), + UniCRVWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniLINKWETH: oneEther.multipliedBy('0.009955').toFixed(), + UniMKRWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniRENWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniSNXWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniUNIWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniUSDCWETH: oneEther.multipliedBy('22.407436').toFixed(), + UniWBTCUSDC: oneEther.multipliedBy('22.407436').toFixed(), + UniYFIWETH: oneEther.multipliedBy('22.407436').toFixed(), + BptWBTCWETH: oneEther.multipliedBy('22.407436').toFixed(), + BptBALWETH: oneEther.multipliedBy('22.407436').toFixed(), + WMATIC: oneEther.multipliedBy('0.003620948469').toFixed(), + STAKE: oneEther.multipliedBy('0.003620948469').toFixed(), + xSUSHI: oneEther.multipliedBy('0.00913428586').toFixed(), + USD: '5848466240000000', +}; diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index fb284b35..07f34a91 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -11,7 +11,6 @@ import { PoolConfiguration, eEthereumNetwork, } from './types'; - import { MintableERC20 } from '../types/MintableERC20'; import { MockContract } from 'ethereum-waffle'; import { getReservesConfigByPool } from './configuration'; @@ -69,6 +68,7 @@ const readArtifact = async (id: string) => { } return (DRE as HardhatRuntimeEnvironment).artifacts.readArtifact(id); }; + export const deployLendingPoolAddressesProvider = async (marketId: string, verify?: boolean) => withSaveAndVerify( await new LendingPoolAddressesProviderFactory(await getFirstSigner()).deploy(marketId), @@ -300,82 +300,133 @@ export const deployDefaultReserveInterestRateStrategy = async ( ); export const deployStableDebtToken = async ( - args: [tEthereumAddress, tEthereumAddress, string, string, tEthereumAddress], + args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string], verify: boolean -) => - withSaveAndVerify( - await new StableDebtTokenFactory(await getFirstSigner()).deploy(...args), +) => { + const instance = await withSaveAndVerify( + await new StableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.StableDebtToken, - args, + [], 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, string, string, tEthereumAddress], + args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string], verify: boolean -) => - withSaveAndVerify( - await new VariableDebtTokenFactory(await getFirstSigner()).deploy(...args), +) => { + const instance = await withSaveAndVerify( + await new VariableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.VariableDebtToken, - args, + [], verify ); + await instance.initialize(args[0], args[1], args[2], '18', args[3], args[4], '0x10'); + + return instance; +}; + +export const deployGenericStableDebtToken = async () => + withSaveAndVerify( + await new StableDebtTokenFactory(await getFirstSigner()).deploy(), + eContractid.StableDebtToken, + [], + false + ); + +export const deployGenericVariableDebtToken = async () => + withSaveAndVerify( + await new VariableDebtTokenFactory(await getFirstSigner()).deploy(), + eContractid.VariableDebtToken, + [], + false + ); + export const deployGenericAToken = async ( - [poolAddress, underlyingAssetAddress, treasuryAddress, name, symbol, incentivesController]: [ + [poolAddress, underlyingAssetAddress, treasuryAddress, incentivesController, name, symbol]: [ + tEthereumAddress, tEthereumAddress, tEthereumAddress, tEthereumAddress, string, - string, - tEthereumAddress + string ], verify: boolean ) => { - const args: [ - tEthereumAddress, - tEthereumAddress, - string, - string, - tEthereumAddress, - tEthereumAddress - ] = [poolAddress, underlyingAssetAddress, treasuryAddress, name, symbol, incentivesController]; - return withSaveAndVerify( - await new ATokenFactory(await getFirstSigner()).deploy(...args), + const instance = await withSaveAndVerify( + await new ATokenFactory(await getFirstSigner()).deploy(), eContractid.AToken, - args, + [], 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 ( - [poolAddress, underlyingAssetAddress, treasuryAddress, name, symbol, incentivesController]: [ + [pool, underlyingAssetAddress, treasuryAddress, incentivesController, name, symbol]: [ + tEthereumAddress, tEthereumAddress, tEthereumAddress, tEthereumAddress, string, - string, - tEthereumAddress + string ], verify: boolean ) => { - const args: [ - tEthereumAddress, - tEthereumAddress, - string, - string, - tEthereumAddress, - tEthereumAddress - ] = [poolAddress, underlyingAssetAddress, treasuryAddress, name, symbol, incentivesController]; - - return withSaveAndVerify( - await new DelegationAwareATokenFactory(await getFirstSigner()).deploy(...args), + const instance = await withSaveAndVerify( + await new DelegationAwareATokenFactory(await getFirstSigner()).deploy(), eContractid.DelegationAwareAToken, - args, + [], 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 } = {}; @@ -390,6 +441,7 @@ export const deployAllMockTokens = async (verify?: boolean) => { [tokenSymbol, tokenSymbol, configData ? configData.reserveDecimals : decimals], verify ); + await registerContractInJsonDb(tokenSymbol.toUpperCase(), tokens[tokenSymbol]); } return tokens; }; @@ -437,10 +489,7 @@ export const deployATokensAndRatesHelper = async ( verify ); -export const deployWETHGateway = async ( - args: [tEthereumAddress, tEthereumAddress], - verify?: boolean -) => +export const deployWETHGateway = async (args: [tEthereumAddress], verify?: boolean) => withSaveAndVerify( await new WETHGatewayFactory(await getFirstSigner()).deploy(...args), eContractid.WETHGateway, @@ -448,17 +497,30 @@ export const deployWETHGateway = async ( verify ); -export const deployMockStableDebtToken = async ( - args: [tEthereumAddress, tEthereumAddress, string, string, tEthereumAddress], - verify?: boolean +export const authorizeWETHGateway = async ( + wethGateWay: tEthereumAddress, + lendingPool: tEthereumAddress ) => - withSaveAndVerify( - await new MockStableDebtTokenFactory(await getFirstSigner()).deploy(...args), + 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, - args, + [], 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(), @@ -468,27 +530,37 @@ export const deployWETHMocked = async (verify?: boolean) => ); export const deployMockVariableDebtToken = async ( - args: [tEthereumAddress, tEthereumAddress, string, string, tEthereumAddress], + args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string, string], verify?: boolean -) => - withSaveAndVerify( - await new MockVariableDebtTokenFactory(await getFirstSigner()).deploy(...args), +) => { + const instance = await withSaveAndVerify( + await new MockVariableDebtTokenFactory(await getFirstSigner()).deploy(), eContractid.MockVariableDebtToken, - args, + [], 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, string, string, tEthereumAddress], + args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string, string], verify?: boolean -) => - withSaveAndVerify( - await new MockATokenFactory(await getFirstSigner()).deploy(...args), +) => { + const instance = await withSaveAndVerify( + await new MockATokenFactory(await getFirstSigner()).deploy(), eContractid.MockAToken, - args, + [], 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(), diff --git a/helpers/contracts-getters.ts b/helpers/contracts-getters.ts index 6504cceb..0f70c48b 100644 --- a/helpers/contracts-getters.ts +++ b/helpers/contracts-getters.ts @@ -175,16 +175,16 @@ export const getPairsTokenAggregator = ( const { ETH, USD, WETH, ...assetsAddressesWithoutEth } = allAssetsAddresses; const pairs = Object.entries(assetsAddressesWithoutEth).map(([tokenSymbol, tokenAddress]) => { - if (tokenSymbol !== 'WETH' && tokenSymbol !== 'ETH') { - const aggregatorAddressIndex = Object.keys(aggregatorsAddresses).findIndex( - (value) => value === tokenSymbol - ); - const [, aggregatorAddress] = (Object.entries(aggregatorsAddresses) as [ - string, - tEthereumAddress - ][])[aggregatorAddressIndex]; - return [tokenAddress, aggregatorAddress]; - } + //if (true/*tokenSymbol !== 'WETH' && tokenSymbol !== 'ETH' && tokenSymbol !== 'LpWETH'*/) { + const aggregatorAddressIndex = Object.keys(aggregatorsAddresses).findIndex( + (value) => value === tokenSymbol + ); + const [, aggregatorAddress] = (Object.entries(aggregatorsAddresses) as [ + string, + tEthereumAddress + ][])[aggregatorAddressIndex]; + return [tokenAddress, aggregatorAddress]; + //} }) as [string, string][]; const mappedPairs = pairs.map(([asset]) => asset); diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index 6fce99b6..455dbb78 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -11,6 +11,13 @@ import { AavePools, iParamsPerNetwork, iParamsPerPool, + ePolygonNetwork, + eXDaiNetwork, + eNetwork, + iParamsPerNetworkAll, + iEthereumParamsPerNetwork, + iPolygonParamsPerNetwork, + iXDaiParamsPerNetwork, } from './types'; import { MintableERC20 } from '../types/MintableERC20'; import { Artifact } from 'hardhat/types'; @@ -92,11 +99,14 @@ export const withSaveAndVerify = async ( await waitForTx(instance.deployTransaction); await registerContractInJsonDb(id, instance); if (usingTenderly()) { - console.log('doing verify of', id); + console.log(); + console.log('Doing Tenderly contract verification of', id); await (DRE as any).tenderlyRPC.verify({ name: id, address: instance.address, }); + console.log(`Verified ${id} at Tenderly!`); + console.log(); } if (verify) { await verifyContract(instance.address, args); @@ -132,10 +142,17 @@ export const linkBytecode = (artifact: BuidlerArtifact | Artifact, libraries: an return bytecode; }; -export const getParamPerNetwork = ( - { kovan, ropsten, main, buidlerevm, coverage, tenderlyMain }: iParamsPerNetwork, - network: eEthereumNetwork -) => { +export const getParamPerNetwork = (param: iParamsPerNetwork, network: eNetwork) => { + const { + main, + ropsten, + kovan, + coverage, + buidlerevm, + tenderlyMain, + } = param as iEthereumParamsPerNetwork; + const { matic, mumbai } = param as iPolygonParamsPerNetwork; + const { xdai } = param as iXDaiParamsPerNetwork; const MAINNET_FORK = process.env.MAINNET_FORK === 'true'; if (MAINNET_FORK) { return main; @@ -156,13 +173,23 @@ export const getParamPerNetwork = ( return main; case eEthereumNetwork.tenderlyMain: return tenderlyMain; + case ePolygonNetwork.matic: + return matic; + case ePolygonNetwork.mumbai: + return mumbai; + case eXDaiNetwork.xdai: + return xdai; } }; -export const getParamPerPool = ({ proto }: iParamsPerPool, pool: AavePools) => { +export const getParamPerPool = ({ proto, amm, matic }: iParamsPerPool, pool: AavePools) => { switch (pool) { case AavePools.proto: return proto; + case AavePools.amm: + return amm; + case AavePools.matic: + return matic; default: return proto; } diff --git a/helpers/init-helpers.ts b/helpers/init-helpers.ts index 55c372a7..eb483420 100644 --- a/helpers/init-helpers.ts +++ b/helpers/init-helpers.ts @@ -1,6 +1,7 @@ import { eContractid, eEthereumNetwork, + eNetwork, iMultiPoolsAssets, IReserveParams, tEthereumAddress, @@ -12,6 +13,7 @@ import { getAToken, getATokensAndRatesHelper, getLendingPoolAddressesProvider, + getLendingPoolConfiguratorProxy, getStableAndVariableTokensHelper, } from './contracts-getters'; import { rawInsertContractAddressInDb } from './contracts-helpers'; @@ -19,12 +21,17 @@ 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) { @@ -40,6 +47,10 @@ export const chooseATokenDeployment = (id: eContractid) => { export const initReservesByHelper = async ( reservesParams: iMultiPoolsAssets, tokenAddresses: { [symbol: string]: tEthereumAddress }, + aTokenNamePrefix: string, + stableDebtTokenNamePrefix: string, + variableDebtTokenNamePrefix: string, + symbolPrefix: string, admin: tEthereumAddress, treasuryAddress: tEthereumAddress, incentivesController: tEthereumAddress, @@ -47,139 +58,93 @@ export const initReservesByHelper = async ( ): Promise => { let gasUsage = BigNumber.from('0'); const stableAndVariableDeployer = await getStableAndVariableTokensHelper(); - const atokenAndRatesDeployer = await getATokensAndRatesHelper(); const addressProvider = await getLendingPoolAddressesProvider(); - const poolAddress = await addressProvider.getLendingPool(); - - // Set aTokenAndRatesDeployer as temporal admin - await waitForTx(await addressProvider.setPoolAdmin(atokenAndRatesDeployer.address)); // CHUNK CONFIGURATION - const tokensChunks = 2; const initChunks = 4; - // Deploy tokens and rates that uses common aToken in chunks - const reservesChunks = chunk( - Object.entries(reservesParams).filter( - ([_, { aTokenImpl }]) => aTokenImpl === eContractid.AToken - ) as [string, IReserveParams][], - tokensChunks - ); // Initialize variables for future reserves initialization - let deployedStableTokens: string[] = []; - let deployedVariableTokens: string[] = []; - let deployedATokens: string[] = []; - let deployedRates: string[] = []; let reserveTokens: string[] = []; let reserveInitDecimals: string[] = []; let reserveSymbols: string[] = []; - console.log( - `- Token deployments in ${reservesChunks.length * 2} txs instead of ${ - Object.entries(reservesParams).length * 4 - } txs` - ); - for (let reservesChunk of reservesChunks) { - // Prepare data - const tokens: string[] = []; - const symbols: string[] = []; - const strategyRates: [ - BigNumberish, - BigNumberish, - BigNumberish, - BigNumberish, - BigNumberish, - BigNumberish - ][] = []; - const reservesDecimals: string[] = []; + let initInputParams: { + aTokenImpl: string; + stableDebtTokenImpl: string; + variableDebtTokenImpl: string; + underlyingAssetDecimals: BigNumberish; + interestRateStrategyAddress: string; + underlyingAsset: string; + treasury: string; + incentivesController: string; + underlyingAssetName: string; + aTokenName: string; + aTokenSymbol: string; + variableDebtTokenName: string; + variableDebtTokenSymbol: string; + stableDebtTokenName: string; + stableDebtTokenSymbol: string; + params: string; + }[] = []; - for (let [assetSymbol, { reserveDecimals }] of reservesChunk) { - const assetAddressIndex = Object.keys(tokenAddresses).findIndex( - (value) => value === assetSymbol - ); - const [, tokenAddress] = (Object.entries(tokenAddresses) as [string, string][])[ - assetAddressIndex - ]; + let strategyRates: [ + string, // addresses provider + string, + string, + string, + string, + string, + string + ]; + let rateStrategies: Record = {}; + let strategyAddresses: Record = {}; + let strategyAddressPerAsset: Record = {}; + let aTokenType: Record = {}; + let delegationAwareATokenImplementationAddress = ''; + let aTokenImplementationAddress = ''; + let stableDebtTokenImplementationAddress = ''; + let variableDebtTokenImplementationAddress = ''; - const reserveParamIndex = Object.keys(reservesParams).findIndex( - (value) => value === assetSymbol - ); - const [ - , - { - optimalUtilizationRate, - baseVariableBorrowRate, - variableRateSlope1, - variableRateSlope2, - stableRateSlope1, - stableRateSlope2, - }, - ] = (Object.entries(reservesParams) as [string, IReserveParams][])[reserveParamIndex]; - // Add to lists - tokens.push(tokenAddress); - symbols.push(assetSymbol); - strategyRates.push([ - optimalUtilizationRate, - baseVariableBorrowRate, - variableRateSlope1, - variableRateSlope2, - stableRateSlope1, - stableRateSlope2, - ]); - reservesDecimals.push(reserveDecimals); - } + // 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; - // Deploy stable and variable deployers and save implementations - const tx1 = await waitForTx( - await stableAndVariableDeployer.initDeployment(tokens, symbols, incentivesController) - ); - tx1.events?.forEach((event, index) => { - rawInsertContractAddressInDb(`stableDebt${symbols[index]}`, event?.args?.stableToken); - rawInsertContractAddressInDb(`variableDebt${symbols[index]}`, event?.args?.variableToken); - }); + const aTokenImplementation = await deployGenericATokenImpl(verify); + aTokenImplementationAddress = aTokenImplementation.address; + rawInsertContractAddressInDb(`aTokenImpl`, aTokenImplementationAddress); - // Deploy atokens and rate strategies and save implementations - const tx2 = await waitForTx( - await atokenAndRatesDeployer.initDeployment( - tokens, - symbols, - strategyRates, - treasuryAddress, - incentivesController - ) - ); - tx2.events?.forEach((event, index) => { - rawInsertContractAddressInDb(`a${symbols[index]}`, event?.args?.aToken); - rawInsertContractAddressInDb(`strategy${symbols[index]}`, event?.args?.strategy); - }); - - console.log(` - Deployed aToken, DebtTokens and Strategy for: ${symbols.join(', ')} `); - console.log(' * gasUsed: debtTokens batch', tx1.gasUsed.toString()); - console.log(' * gasUsed: aTokens and Strategy batch', tx2.gasUsed.toString()); - gasUsage = gasUsage.add(tx1.gasUsed).add(tx2.gasUsed); - - const stableTokens: string[] = tx1.events?.map((e) => e.args?.stableToken) || []; - const variableTokens: string[] = tx1.events?.map((e) => e.args?.variableToken) || []; - const aTokens: string[] = tx2.events?.map((e) => e.args?.aToken) || []; - const strategies: string[] = tx2.events?.map((e) => e.args?.strategy) || []; - - deployedStableTokens = [...deployedStableTokens, ...stableTokens]; - deployedVariableTokens = [...deployedVariableTokens, ...variableTokens]; - deployedATokens = [...deployedATokens, ...aTokens]; - deployedRates = [...deployedRates, ...strategies]; - reserveInitDecimals = [...reserveInitDecimals, ...reservesDecimals]; - reserveTokens = [...reserveTokens, ...tokens]; - reserveSymbols = [...reserveSymbols, ...symbols]; - } - - // Deploy delegated aware reserves tokens const delegatedAwareReserves = Object.entries(reservesParams).filter( ([_, { aTokenImpl }]) => aTokenImpl === eContractid.DelegationAwareAToken ) as [string, IReserveParams][]; - for (let [symbol, params] of delegatedAwareReserves) { - console.log(` - Deploy ${symbol} delegation aware aToken, debts tokens, and strategy`); + 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][]; + + for (let [symbol, params] of reserves) { + const { strategy, aTokenImpl, reserveDecimals } = params; const { optimalUtilizationRate, baseVariableBorrowRate, @@ -187,89 +152,86 @@ export const initReservesByHelper = async ( variableRateSlope2, stableRateSlope1, stableRateSlope2, - } = params; - const deployCustomAToken = chooseATokenDeployment(params.aTokenImpl); - const aToken = await deployCustomAToken( - [ - poolAddress, - tokenAddresses[symbol], - treasuryAddress, - `Aave interest bearing ${symbol}`, - `a${symbol}`, - ZERO_ADDRESS, - ], - verify - ); - const stableDebt = await deployStableDebtToken( - [ - poolAddress, - tokenAddresses[symbol], - `Aave stable debt bearing ${symbol}`, - `stableDebt${symbol}`, - ZERO_ADDRESS, - ], - verify - ); - const variableDebt = await deployVariableDebtToken( - [ - poolAddress, - tokenAddresses[symbol], - `Aave variable debt bearing ${symbol}`, - `variableDebt${symbol}`, - ZERO_ADDRESS, - ], - verify - ); - const rates = await deployDefaultReserveInterestRateStrategy( - [ - tokenAddresses[symbol], + } = strategy; + if (!strategyAddresses[strategy.name]) { + // Strategy does not exist, create a new one + rateStrategies[strategy.name] = [ + addressProvider.address, optimalUtilizationRate, baseVariableBorrowRate, variableRateSlope1, variableRateSlope2, stableRateSlope1, stableRateSlope2, - ], - verify - ); + ]; + strategyAddresses[strategy.name] = ( + await deployDefaultReserveInterestRateStrategy(rateStrategies[strategy.name], verify) + ).address; + // This causes the last strategy to be printed twice, once under "DefaultReserveInterestRateStrategy" + // 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]); - deployedStableTokens.push(stableDebt.address); - deployedVariableTokens.push(variableDebt.address); - deployedATokens.push(aToken.address); - deployedRates.push(rates.address); - reserveInitDecimals.push(params.reserveDecimals); + if (aTokenImpl === eContractid.AToken) { + aTokenType[symbol] = 'generic'; + } else if (aTokenImpl === eContractid.DelegationAwareAToken) { + aTokenType[symbol] = 'delegation aware'; + } + + reserveInitDecimals.push(reserveDecimals); reserveTokens.push(tokenAddresses[symbol]); reserveSymbols.push(symbol); } - // Deploy init reserves per chunks - const chunkedStableTokens = chunk(deployedStableTokens, initChunks); - const chunkedVariableTokens = chunk(deployedVariableTokens, initChunks); - const chunkedAtokens = chunk(deployedATokens, initChunks); - const chunkedRates = chunk(deployedRates, initChunks); - const chunkedDecimals = chunk(reserveInitDecimals, initChunks); - const chunkedSymbols = chunk(reserveSymbols, initChunks); + for (let i = 0; i < reserveSymbols.length; i++) { + let aTokenToUse: string; + if (aTokenType[reserveSymbols[i]] === 'generic') { + aTokenToUse = aTokenImplementationAddress; + } else { + aTokenToUse = delegationAwareATokenImplementationAddress; + } - console.log(`- Reserves initialization in ${chunkedStableTokens.length} txs`); - for (let chunkIndex = 0; chunkIndex < chunkedDecimals.length; chunkIndex++) { + initInputParams.push({ + aTokenImpl: aTokenToUse, + stableDebtTokenImpl: stableDebtTokenImplementationAddress, + variableDebtTokenImpl: variableDebtTokenImplementationAddress, + underlyingAssetDecimals: reserveInitDecimals[i], + interestRateStrategyAddress: strategyAddressPerAsset[reserveSymbols[i]], + underlyingAsset: reserveTokens[i], + treasury: treasuryAddress, + incentivesController: ZERO_ADDRESS, + 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]}`, + params: '0x10' + }); + } + + // Deploy init reserves per chunks + const chunkedSymbols = chunk(reserveSymbols, initChunks); + 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++) { const tx3 = await waitForTx( - await atokenAndRatesDeployer.initReserve( - chunkedStableTokens[chunkIndex], - chunkedVariableTokens[chunkIndex], - chunkedAtokens[chunkIndex], - chunkedRates[chunkIndex], - chunkedDecimals[chunkIndex] - ) + await configurator.batchInitReserve(chunkedInitInputParams[chunkIndex]) ); console.log(` - Reserve ready for: ${chunkedSymbols[chunkIndex].join(', ')}`); console.log(' * gasUsed', tx3.gasUsed.toString()); - gasUsage = gasUsage.add(tx3.gasUsed); + //gasUsage = gasUsage.add(tx3.gasUsed); } - // Set deployer back as admin - await waitForTx(await addressProvider.setPoolAdmin(admin)); - return gasUsage; + return gasUsage; // Deprecated }; export const getPairsTokenAggregator = ( @@ -315,6 +277,15 @@ export const configureReservesByHelper = async ( const reserveFactors: string[] = []; const stableRatesEnabled: boolean[] = []; + const inputParams: { + asset: string; + baseLTV: BigNumberish; + liquidationThreshold: BigNumberish; + liquidationBonus: BigNumberish; + reserveFactor: BigNumberish; + stableBorrowingEnabled: boolean; + }[] = []; + for (const [ assetSymbol, { @@ -342,6 +313,16 @@ export const configureReservesByHelper = async ( continue; } // Push data + + inputParams.push({ + asset: tokenAddress, + baseLTV: baseLTVAsCollateral, + liquidationThreshold: liquidationThreshold, + liquidationBonus: liquidationBonus, + reserveFactor: reserveFactor, + stableBorrowingEnabled: stableBorrowRateEnabled, + }); + tokens.push(tokenAddress); symbols.push(assetSymbol); baseLTVA.push(baseLTVAsCollateral); @@ -356,26 +337,15 @@ export const configureReservesByHelper = async ( // Deploy init per chunks const enableChunks = 20; - const chunkedTokens = chunk(tokens, enableChunks); const chunkedSymbols = chunk(symbols, enableChunks); - const chunkedBase = chunk(baseLTVA, enableChunks); - const chunkedliquidationThresholds = chunk(liquidationThresholds, enableChunks); - const chunkedliquidationBonuses = chunk(liquidationBonuses, enableChunks); - const chunkedReserveFactors = chunk(reserveFactors, enableChunks); - const chunkedStableRatesEnabled = chunk(stableRatesEnabled, enableChunks); + const chunkedInputParams = chunk(inputParams, enableChunks); - console.log(`- Configure reserves in ${chunkedTokens.length} txs`); - for (let chunkIndex = 0; chunkIndex < chunkedTokens.length; chunkIndex++) { + console.log(`- Configure reserves in ${chunkedInputParams.length} txs`); + for (let chunkIndex = 0; chunkIndex < chunkedInputParams.length; chunkIndex++) { await waitForTx( - await atokenAndRatesDeployer.configureReserves( - chunkedTokens[chunkIndex], - chunkedBase[chunkIndex], - chunkedliquidationThresholds[chunkIndex], - chunkedliquidationBonuses[chunkIndex], - chunkedReserveFactors[chunkIndex], - chunkedStableRatesEnabled[chunkIndex], - { gasLimit: 12000000 } - ) + await atokenAndRatesDeployer.configureReserves(chunkedInputParams[chunkIndex], { + gasLimit: 12000000, + }) ); console.log(` - Init for: ${chunkedSymbols[chunkIndex].join(', ')}`); } @@ -386,10 +356,13 @@ export const configureReservesByHelper = async ( const getAddressById = async ( id: string, - network: eEthereumNetwork + network: eNetwork ): Promise => (await getDb().get(`${id}.${network}`).value())?.address || undefined; +// Function deprecated? Updated but untested, script is not updated on package.json. +// This is not called during regular deployment, only in the "full:initialize-tokens" +// hardhat task. export const initTokenReservesByHelper = async ( reservesParams: iMultiPoolsAssets, tokenAddresses: { [symbol: string]: tEthereumAddress }, @@ -415,7 +388,7 @@ export const initTokenReservesByHelper = async ( const poolAddress = await addressProvider.getLendingPool(); // Set aTokenAndRatesDeployer as temporal admin - await waitForTx(await addressProvider.setPoolAdmin(atokenAndRatesDeployer.address)); + //await waitForTx(await addressProvider.setPoolAdmin(atokenAndRatesDeployer.address)); // CHUNK CONFIGURATION const initChunks = 4; @@ -425,12 +398,31 @@ export const initTokenReservesByHelper = async ( let deployedVariableTokens: string[] = []; let deployedATokens: string[] = []; let deployedRates: string[] = []; + //let reserveTokens: string[] = []; let reserveInitDecimals: string[] = []; let reserveSymbols: string[] = []; + + let initInputParams: { + aTokenImpl: string; + stableDebtTokenImpl: string; + variableDebtTokenImpl: string; + underlyingAssetDecimals: BigNumberish; + interestRateStrategyAddress: string; + underlyingAsset: string; + treasury: string; + incentivesController: string; + underlyingAssetName: string; + aTokenName: string; + aTokenSymbol: string; + variableDebtTokenName: string; + variableDebtTokenSymbol: string; + stableDebtTokenName: string; + stableDebtTokenSymbol: string; + params: string; + }[] = []; + const network = - process.env.MAINNET_FORK === 'true' - ? eEthereumNetwork.main - : (DRE.network.name as eEthereumNetwork); + process.env.MAINNET_FORK === 'true' ? eEthereumNetwork.main : (DRE.network.name as eNetwork); // Grab config from DB for (const [symbol, address] of Object.entries(tokenAddresses)) { const { aTokenAddress } = await protocolDataProvider.getReserveTokensAddresses(address); @@ -444,19 +436,29 @@ export const initTokenReservesByHelper = async ( console.log(`- Skipping ${symbol} due already initialized`); continue; } - let stableTokenImpl = await getAddressById(`stableDebt${symbol}`, network); - let variableTokenImpl = await getAddressById(`variableDebt${symbol}`, network); - let aTokenImplementation = await getAddressById(`a${symbol}`, network); - let strategyImpl = await getAddressById(`strategy${symbol}`, network); + let stableTokenImpl = await getAddressById(`stableDebtTokenImpl`, network); + let variableTokenImpl = await getAddressById(`variableDebtTokenImpl`, network); + let aTokenImplementation: string | undefined = ''; + const [, { aTokenImpl, strategy }] = (Object.entries(reservesParams) as [ + string, + IReserveParams + ][])[reserveParamIndex]; + if (aTokenImpl === eContractid.AToken) { + aTokenImplementation = await getAddressById(`aTokenImpl`, network); + } else if (aTokenImpl === eContractid.DelegationAwareAToken) { + aTokenImplementation = await getAddressById(`delegationAwareATokenImpl`, network); + } + + let strategyImpl = await getAddressById(strategy.name, network); if (!stableTokenImpl) { const stableDebt = await deployStableDebtToken( [ poolAddress, tokenAddresses[symbol], + ZERO_ADDRESS, // Incentives controller `Aave stable debt bearing ${symbol}`, `stableDebt${symbol}`, - ZERO_ADDRESS, ], verify ); @@ -467,9 +469,9 @@ export const initTokenReservesByHelper = async ( [ poolAddress, tokenAddresses[symbol], + ZERO_ADDRESS, // Incentives Controller `Aave variable debt bearing ${symbol}`, `variableDebt${symbol}`, - ZERO_ADDRESS, ], verify ); @@ -485,26 +487,26 @@ export const initTokenReservesByHelper = async ( poolAddress, tokenAddresses[symbol], treasuryAddress, + ZERO_ADDRESS, `Aave interest bearing ${symbol}`, `a${symbol}`, - ZERO_ADDRESS, ], verify ); aTokenImplementation = aToken.address; } if (!strategyImpl) { - const [ - , - { - optimalUtilizationRate, - baseVariableBorrowRate, - variableRateSlope1, - variableRateSlope2, - stableRateSlope1, - stableRateSlope2, - }, - ] = (Object.entries(reservesParams) as [string, IReserveParams][])[reserveParamIndex]; + const [, { strategy }] = (Object.entries(reservesParams) as [string, IReserveParams][])[ + reserveParamIndex + ]; + const { + optimalUtilizationRate, + baseVariableBorrowRate, + variableRateSlope1, + variableRateSlope2, + stableRateSlope1, + stableRateSlope2, + } = strategy; const rates = await deployDefaultReserveInterestRateStrategy( [ tokenAddresses[symbol], @@ -519,53 +521,69 @@ export const initTokenReservesByHelper = async ( ); strategyImpl = rates.address; } - const symbols = [`a${symbol}`, `variableDebt${symbol}`, `stableDebt${symbol}`]; - const tokens = [aTokenImplementation, variableTokenImpl, stableTokenImpl]; - for (let index = 0; index < symbols.length; index++) { - if (!(await isErc20SymbolCorrect(tokens[index], symbols[index]))) { - console.error(`${symbol} and implementation does not match: ${tokens[index]}`); - throw Error('Symbol does not match implementation.'); - } - } + // --- REMOVED BECAUSE WE NOW USE THE SAME IMPLEMENTATIONS --- + // const symbols = [`a${symbol}`, `variableDebt${symbol}`, `stableDebt${symbol}`]; + // const tokens = [aTokenImplementation, variableTokenImpl, stableTokenImpl]; + // for (let index = 0; index < symbols.length; index++) { + // if (!(await isErc20SymbolCorrect(tokens[index], symbols[index]))) { + // console.error(`${symbol} and implementation does not match: ${tokens[index]}`); + // throw Error('Symbol does not match implementation.'); + // } + // } console.log(`- Added ${symbol} to the initialize batch`); deployedStableTokens.push(stableTokenImpl); deployedVariableTokens.push(variableTokenImpl); deployedATokens.push(aTokenImplementation); + //reserveTokens.push(); deployedRates.push(strategyImpl); reserveInitDecimals.push(decimals.toString()); reserveSymbols.push(symbol); } - // Deploy init reserves per chunks - const chunkedStableTokens = chunk(deployedStableTokens, initChunks); - const chunkedVariableTokens = chunk(deployedVariableTokens, initChunks); - const chunkedAtokens = chunk(deployedATokens, initChunks); - const chunkedRates = chunk(deployedRates, initChunks); - const chunkedDecimals = chunk(reserveInitDecimals, initChunks); - const chunkedSymbols = chunk(reserveSymbols, initChunks); + for (let i = 0; i < deployedATokens.length; i++) { + initInputParams.push({ + aTokenImpl: deployedATokens[i], + stableDebtTokenImpl: deployedStableTokens[i], + variableDebtTokenImpl: deployedVariableTokens[i], + underlyingAssetDecimals: reserveInitDecimals[i], + interestRateStrategyAddress: deployedRates[i], + underlyingAsset: tokenAddresses[reserveSymbols[i]], + treasury: treasuryAddress, + incentivesController: ZERO_ADDRESS, + underlyingAssetName: reserveSymbols[i], + aTokenName: `Aave interest bearing ${reserveSymbols[i]}`, + aTokenSymbol: `a${reserveSymbols[i]}`, + variableDebtTokenName: `Aave variable debt bearing ${reserveSymbols[i]}`, + variableDebtTokenSymbol: `variableDebt${reserveSymbols[i]}`, + stableDebtTokenName: `Aave stable debt bearing ${reserveSymbols[i]}`, + stableDebtTokenSymbol: `stableDebt${reserveSymbols[i]}`, + params: '0x10' + }); + } - console.log(`- Reserves initialization in ${chunkedStableTokens.length} txs`); - for (let chunkIndex = 0; chunkIndex < chunkedDecimals.length; chunkIndex++) { + // Deploy init reserves per chunks + const chunkedSymbols = chunk(reserveSymbols, initChunks); + 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++) { const tx3 = await waitForTx( - await atokenAndRatesDeployer.initReserve( - chunkedStableTokens[chunkIndex], - chunkedVariableTokens[chunkIndex], - chunkedAtokens[chunkIndex], - chunkedRates[chunkIndex], - chunkedDecimals[chunkIndex] - ) + await configurator.batchInitReserve(chunkedInitInputParams[chunkIndex]) ); console.log(` - Reserve ready for: ${chunkedSymbols[chunkIndex].join(', ')}`); console.log(' * gasUsed', tx3.gasUsed.toString()); - gasUsage = gasUsage.add(tx3.gasUsed); } // Set deployer back as admin - await waitForTx(await addressProvider.setPoolAdmin(admin)); - return gasUsage; + //await waitForTx(await addressProvider.setPoolAdmin(admin)); + return gasUsage; // No longer relevant }; +// Function deprecated const isErc20SymbolCorrect = async (token: tEthereumAddress, symbol: string) => { const erc20 = await getAToken(token); // using aToken for ERC20 interface const erc20Symbol = await erc20.symbol(); diff --git a/helpers/oracles-helpers.ts b/helpers/oracles-helpers.ts index 9fd2ba44..6ec8827a 100644 --- a/helpers/oracles-helpers.ts +++ b/helpers/oracles-helpers.ts @@ -72,6 +72,9 @@ export const setInitialAssetPricesInOracle = async ( priceOracleInstance: PriceOracle ) => { for (const [assetSymbol, price] of Object.entries(prices) as [string, string][]) { + + console.log("Trying for ", assetsAddresses, assetSymbol); + const assetAddressIndex = Object.keys(assetsAddresses).findIndex( (value) => value === assetSymbol ); diff --git a/helpers/types.ts b/helpers/types.ts index 4ea1ab06..ea8826a0 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -4,6 +4,8 @@ export interface SymbolMap { [symbol: string]: T; } +export type eNetwork = eEthereumNetwork | ePolygonNetwork | eXDaiNetwork; + export enum eEthereumNetwork { buidlerevm = 'buidlerevm', kovan = 'kovan', @@ -14,14 +16,28 @@ export enum eEthereumNetwork { tenderlyMain = 'tenderlyMain', } +export enum ePolygonNetwork { + matic = 'matic', + mumbai = 'mumbai', +} + +export enum eXDaiNetwork { + xdai = 'xdai', +} + export enum EthereumNetworkNames { kovan = 'kovan', ropsten = 'ropsten', main = 'main', + matic = 'matic', + mumbai = 'mumbai', + xdai = 'xdai', } export enum AavePools { proto = 'proto', + matic = 'matic', + amm = 'amm', } export enum eContractid { @@ -204,6 +220,24 @@ export interface iAssetBase { USD: T; REN: T; ENJ: T; + UniDAIWETH: T; + UniWBTCWETH: T; + UniAAVEWETH: T; + UniBATWETH: T; + UniDAIUSDC: T; + UniCRVWETH: T; + UniLINKWETH: T; + UniMKRWETH: T; + UniRENWETH: T; + UniSNXWETH: T; + UniUNIWETH: T; + UniUSDCWETH: T; + UniWBTCUSDC: T; + UniYFIWETH: T; + BptWBTCWETH: T; + BptBALWETH: T; + WMATIC: T; + STAKE: T; xSUSHI: T; } @@ -236,6 +270,41 @@ export type iAavePoolAssets = Pick< | 'xSUSHI' >; +export type iLpPoolAssets = Pick< + iAssetsWithoutUSD, + | 'DAI' + | 'USDC' + | 'USDT' + | 'WBTC' + | 'WETH' + | 'UniDAIWETH' + | 'UniWBTCWETH' + | 'UniAAVEWETH' + | 'UniBATWETH' + | 'UniDAIUSDC' + | 'UniCRVWETH' + | 'UniLINKWETH' + | 'UniMKRWETH' + | 'UniRENWETH' + | 'UniSNXWETH' + | 'UniUNIWETH' + | 'UniUSDCWETH' + | 'UniWBTCUSDC' + | 'UniYFIWETH' + | 'BptWBTCWETH' + | 'BptBALWETH' +>; + +export type iMaticPoolAssets = Pick< + iAssetsWithoutUSD, + 'DAI' | 'USDC' | 'USDT' | 'WBTC' | 'WETH' | 'WMATIC' +>; + +export type iXDAIPoolAssets = Pick< + iAssetsWithoutUSD, + 'DAI' | 'USDC' | 'USDT' | 'WBTC' | 'WETH' | 'STAKE' +>; + export type iMultiPoolsAssets = iAssetCommon | iAavePoolAssets; export type iAavePoolTokens = Omit, 'ETH'>; @@ -264,21 +333,50 @@ export enum TokenContractId { YFI = 'YFI', UNI = 'UNI', ENJ = 'ENJ', + UniDAIWETH = 'UniDAIWETH', + UniWBTCWETH = 'UniWBTCWETH', + UniAAVEWETH = 'UniAAVEWETH', + UniBATWETH = 'UniBATWETH', + UniDAIUSDC = 'UniDAIUSDC', + UniCRVWETH = 'UniCRVWETH', + UniLINKWETH = 'UniLINKWETH', + UniMKRWETH = 'UniMKRWETH', + UniRENWETH = 'UniRENWETH', + UniSNXWETH = 'UniSNXWETH', + UniUNIWETH = 'UniUNIWETH', + UniUSDCWETH = 'UniUSDCWETH', + UniWBTCUSDC = 'UniWBTCUSDC', + UniYFIWETH = 'UniYFIWETH', + BptWBTCWETH = 'BptWBTCWETH', + BptBALWETH = 'BptBALWETH', + WMATIC = 'WMATIC', + STAKE = 'STAKE', xSUSHI = 'xSUSHI' } export interface IReserveParams extends IReserveBorrowParams, IReserveCollateralParams { aTokenImpl: eContractid; reserveFactor: string; + strategy: IInterestRateStrategyParams; } -export interface IReserveBorrowParams { +export interface IInterestRateStrategyParams { + name: string; optimalUtilizationRate: string; baseVariableBorrowRate: string; variableRateSlope1: string; variableRateSlope2: string; stableRateSlope1: string; stableRateSlope2: string; +} + +export interface IReserveBorrowParams { + // optimalUtilizationRate: string; + // baseVariableBorrowRate: string; + // variableRateSlope1: string; + // variableRateSlope2: string; + // stableRateSlope1: string; + // stableRateSlope2: string; borrowingEnabled: boolean; stableBorrowRateEnabled: boolean; reserveDecimals: string; @@ -293,7 +391,17 @@ export interface IMarketRates { borrowRate: string; } -export interface iParamsPerNetwork { +export type iParamsPerNetwork = + | iEthereumParamsPerNetwork + | iPolygonParamsPerNetwork + | iXDaiParamsPerNetwork; + +export interface iParamsPerNetworkAll + extends iEthereumParamsPerNetwork, + iPolygonParamsPerNetwork, + iXDaiParamsPerNetwork {} + +export interface iEthereumParamsPerNetwork { [eEthereumNetwork.coverage]: T; [eEthereumNetwork.buidlerevm]: T; [eEthereumNetwork.kovan]: T; @@ -303,8 +411,19 @@ export interface iParamsPerNetwork { [eEthereumNetwork.tenderlyMain]: T; } +export interface iPolygonParamsPerNetwork { + [ePolygonNetwork.matic]: T; + [ePolygonNetwork.mumbai]: T; +} + +export interface iXDaiParamsPerNetwork { + [eXDaiNetwork.xdai]: T; +} + export interface iParamsPerPool { [AavePools.proto]: T; + [AavePools.matic]: T; + [AavePools.amm]: T; } export interface iBasicDistributionParams { @@ -322,15 +441,6 @@ export interface ObjectString { [key: string]: string; } -export enum EthereumNetwork { - kovan = 'kovan', - ropsten = 'ropsten', - development = 'development', - main = 'main', - coverage = 'soliditycoverage', - tenderlyMain = 'tenderlyMain', -} - export interface IProtocolGlobalConfig { TokenDistributorPercentageBase: string; MockUsdPriceInWei: string; @@ -354,11 +464,18 @@ export interface ILendingRate { export interface ICommonConfiguration { MarketId: string; + ATokenNamePrefix: string; + StableDebtTokenNamePrefix: string; + VariableDebtTokenNamePrefix: string; + SymbolPrefix: string; ProviderId: number; ProtocolGlobalParams: IProtocolGlobalConfig; Mocks: IMocksConfig; ProviderRegistry: iParamsPerNetwork; ProviderRegistryOwner: iParamsPerNetwork; + LendingPoolCollateralManager: iParamsPerNetwork; + LendingPoolConfigurator: iParamsPerNetwork; + LendingPool: iParamsPerNetwork; LendingRateOracleRatesCommon: iMultiPoolsAssets; LendingRateOracle: iParamsPerNetwork; TokenDistributor: iParamsPerNetwork; @@ -373,12 +490,26 @@ export interface ICommonConfiguration { ReservesConfig: iMultiPoolsAssets; ATokenDomainSeparator: iParamsPerNetwork; WETH: iParamsPerNetwork; + WethGateway: iParamsPerNetwork; ReserveFactorTreasuryAddress: iParamsPerNetwork; } export interface IAaveConfiguration extends ICommonConfiguration { ReservesConfig: iAavePoolAssets; } + +export interface IAmmConfiguration extends ICommonConfiguration { + ReservesConfig: iLpPoolAssets; +} + +export interface IMaticConfiguration extends ICommonConfiguration { + ReservesConfig: iMaticPoolAssets; +} + +export interface IXDAIConfiguration extends ICommonConfiguration { + ReservesConfig: iXDAIPoolAssets; +} + export interface ITokenAddress { [token: string]: tEthereumAddress; } diff --git a/markets/aave/commons.ts b/markets/aave/commons.ts index 6833e67f..f16c9227 100644 --- a/markets/aave/commons.ts +++ b/markets/aave/commons.ts @@ -1,38 +1,18 @@ import BigNumber from 'bignumber.js'; -import { oneEther, oneRay, RAY, ZERO_ADDRESS } from '../../helpers/constants'; -import { ICommonConfiguration, EthereumNetwork, eEthereumNetwork } from '../../helpers/types'; +import { oneEther, oneRay, RAY, ZERO_ADDRESS, MOCK_CHAINLINK_AGGREGATORS_PRICES } from '../../helpers/constants'; +import { ICommonConfiguration, eEthereumNetwork } from '../../helpers/types'; -const MOCK_CHAINLINK_AGGREGATORS_PRICES = { - AAVE: oneEther.multipliedBy('0.003620948469').toFixed(), - BAT: oneEther.multipliedBy('0.00137893825230').toFixed(), - BUSD: oneEther.multipliedBy('0.00736484').toFixed(), - DAI: oneEther.multipliedBy('0.00369068412860').toFixed(), - ENJ: oneEther.multipliedBy('0.00029560').toFixed(), - KNC: oneEther.multipliedBy('0.001072').toFixed(), - LINK: oneEther.multipliedBy('0.009955').toFixed(), - MANA: oneEther.multipliedBy('0.000158').toFixed(), - MKR: oneEther.multipliedBy('2.508581').toFixed(), - REN: oneEther.multipliedBy('0.00065133').toFixed(), - SNX: oneEther.multipliedBy('0.00442616').toFixed(), - SUSD: oneEther.multipliedBy('0.00364714136416').toFixed(), - TUSD: oneEther.multipliedBy('0.00364714136416').toFixed(), - UNI: oneEther.multipliedBy('0.00536479').toFixed(), - USDC: oneEther.multipliedBy('0.00367714136416').toFixed(), - USDT: oneEther.multipliedBy('0.00369068412860').toFixed(), - WETH: oneEther.toFixed(), - WBTC: oneEther.multipliedBy('47.332685').toFixed(), - YFI: oneEther.multipliedBy('22.407436').toFixed(), - ZRX: oneEther.multipliedBy('0.001151').toFixed(), - xSUSHI: oneEther.multipliedBy('0.00913428586').toFixed(), - USD: '5848466240000000', -}; // ---------------- // PROTOCOL GLOBAL PARAMS // ---------------- export const CommonsConfig: ICommonConfiguration = { MarketId: 'Commons', - ProviderId: 0, + ATokenNamePrefix: 'Aave interest bearing', + StableDebtTokenNamePrefix: 'Aave stable debt bearing', + VariableDebtTokenNamePrefix: 'Aave variable debt bearing', + SymbolPrefix: '', + ProviderId: 0, // Overriden in index.ts ProtocolGlobalParams: { TokenDistributorPercentageBase: '10000', MockUsdPriceInWei: '5848466240000000', @@ -159,43 +139,79 @@ export const CommonsConfig: ICommonConfiguration = { [eEthereumNetwork.coverage]: '', [eEthereumNetwork.hardhat]: '', [eEthereumNetwork.buidlerevm]: '', - [eEthereumNetwork.kovan]: '0xdCde9Bb6a49e37fA433990832AB541AE2d4FEB4a', + [eEthereumNetwork.kovan]: '',//'0xdCde9Bb6a49e37fA433990832AB541AE2d4FEB4a', [eEthereumNetwork.ropsten]: '0x05dcca805a6562c1bdd0423768754acb6993241b', - [eEthereumNetwork.main]: '0x8A32f49FFbA88aba6EFF96F45D8BD1D4b3f35c7D', + [eEthereumNetwork.main]: '',//'0x8A32f49FFbA88aba6EFF96F45D8BD1D4b3f35c7D', [eEthereumNetwork.tenderlyMain]: '0x8A32f49FFbA88aba6EFF96F45D8BD1D4b3f35c7D', + }, + LendingPoolCollateralManager: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '0x9269b6453d0d75370c4c85e5a42977a53efdb72a', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '0xbd4765210d4167CE2A5b87280D9E8Ee316D5EC7C', + [eEthereumNetwork.tenderlyMain]: '0xbd4765210d4167CE2A5b87280D9E8Ee316D5EC7C', + }, + LendingPoolConfigurator: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '', + [eEthereumNetwork.tenderlyMain]: '', + }, + LendingPool: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '', + [eEthereumNetwork.tenderlyMain]: '', + }, + WethGateway: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '0xf99b8E67a0E044734B01EC4586D1c88C9a869718', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '', + [eEthereumNetwork.tenderlyMain]: '', }, TokenDistributor: { [eEthereumNetwork.coverage]: '', [eEthereumNetwork.buidlerevm]: '', [eEthereumNetwork.hardhat]: '', - [EthereumNetwork.kovan]: '0x971efe90088f21dc6a36f610ffed77fc19710708', - [EthereumNetwork.ropsten]: '0xeba2ea67942b8250d870b12750b594696d02fc9c', - [EthereumNetwork.main]: '0xe3d9988f676457123c5fd01297605efdd0cba1ae', - [EthereumNetwork.tenderlyMain]: '0xe3d9988f676457123c5fd01297605efdd0cba1ae', + [eEthereumNetwork.kovan]: '0x971efe90088f21dc6a36f610ffed77fc19710708', + [eEthereumNetwork.ropsten]: '0xeba2ea67942b8250d870b12750b594696d02fc9c', + [eEthereumNetwork.main]: '0xe3d9988f676457123c5fd01297605efdd0cba1ae', + [eEthereumNetwork.tenderlyMain]: '0xe3d9988f676457123c5fd01297605efdd0cba1ae', }, AaveOracle: { [eEthereumNetwork.coverage]: '', [eEthereumNetwork.hardhat]: '', [eEthereumNetwork.buidlerevm]: '', - [EthereumNetwork.kovan]: '0xB8bE51E6563BB312Cbb2aa26e352516c25c26ac1', - [EthereumNetwork.ropsten]: ZERO_ADDRESS, - [EthereumNetwork.main]: '0xA50ba011c48153De246E5192C8f9258A2ba79Ca9', - [EthereumNetwork.tenderlyMain]: '0xA50ba011c48153De246E5192C8f9258A2ba79Ca9', + [eEthereumNetwork.kovan]: '',//'0xB8bE51E6563BB312Cbb2aa26e352516c25c26ac1', + [eEthereumNetwork.ropsten]: ZERO_ADDRESS, + [eEthereumNetwork.main]: '',//'0xA50ba011c48153De246E5192C8f9258A2ba79Ca9', + [eEthereumNetwork.tenderlyMain]: '0xA50ba011c48153De246E5192C8f9258A2ba79Ca9', }, FallbackOracle: { [eEthereumNetwork.coverage]: '', [eEthereumNetwork.hardhat]: '', [eEthereumNetwork.buidlerevm]: '', - [EthereumNetwork.kovan]: '0x50913E8E1c650E790F8a1E741FF9B1B1bB251dfe', - [EthereumNetwork.ropsten]: '0xAD1a978cdbb8175b2eaeC47B01404f8AEC5f4F0d', - [EthereumNetwork.main]: ZERO_ADDRESS, - [EthereumNetwork.tenderlyMain]: ZERO_ADDRESS, + [eEthereumNetwork.kovan]: '0x50913E8E1c650E790F8a1E741FF9B1B1bB251dfe', + [eEthereumNetwork.ropsten]: '0xAD1a978cdbb8175b2eaeC47B01404f8AEC5f4F0d', + [eEthereumNetwork.main]: ZERO_ADDRESS, + [eEthereumNetwork.tenderlyMain]: ZERO_ADDRESS, }, ChainlinkAggregator: { [eEthereumNetwork.coverage]: {}, [eEthereumNetwork.hardhat]: {}, [eEthereumNetwork.buidlerevm]: {}, - [EthereumNetwork.kovan]: { + [eEthereumNetwork.kovan]: { AAVE: '0xd04647B7CB523bb9f26730E9B6dE1174db7591Ad', BAT: '0x0e4fcEC26c9f85c3D714370c98f43C4E02Fc35Ae', BUSD: '0xbF7A18ea5DE0501f7559144e702b29c55b055CcB', @@ -217,7 +233,7 @@ export const CommonsConfig: ICommonConfiguration = { ZRX: '0xBc3f28Ccc21E9b5856E81E6372aFf57307E2E883', USD: '0x9326BFA02ADD2366b30bacB125260Af641031331', }, - [EthereumNetwork.ropsten]: { + [eEthereumNetwork.ropsten]: { AAVE: ZERO_ADDRESS, BAT: '0xafd8186c962daf599f171b8600f3e19af7b52c92', BUSD: '0x0A32D96Ff131cd5c3E0E5AAB645BF009Eda61564', @@ -239,7 +255,7 @@ export const CommonsConfig: ICommonConfiguration = { ZRX: '0x1d0052e4ae5b4ae4563cbac50edc3627ca0460d7', USD: '0x8468b2bDCE073A157E560AA4D9CcF6dB1DB98507', }, - [EthereumNetwork.main]: { + [eEthereumNetwork.main]: { AAVE: '0x6Df09E975c830ECae5bd4eD9d90f3A95a4f88012', BAT: '0x0d16d4528239e9ee52fa531af613AcdB23D88c94', BUSD: '0x614715d2Af89E6EC99A233818275142cE88d1Cfd', @@ -261,7 +277,7 @@ export const CommonsConfig: ICommonConfiguration = { ZRX: '0x2Da4983a622a8498bb1a21FaE9D8F6C664939962', USD: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419', }, - [EthereumNetwork.tenderlyMain]: { + [eEthereumNetwork.tenderlyMain]: { AAVE: '0x6Df09E975c830ECae5bd4eD9d90f3A95a4f88012', BAT: '0x0d16d4528239e9ee52fa531af613AcdB23D88c94', BUSD: '0x614715d2Af89E6EC99A233818275142cE88d1Cfd', @@ -288,10 +304,10 @@ export const CommonsConfig: ICommonConfiguration = { [eEthereumNetwork.coverage]: {}, [eEthereumNetwork.hardhat]: {}, [eEthereumNetwork.buidlerevm]: {}, - [EthereumNetwork.main]: {}, - [EthereumNetwork.kovan]: {}, - [EthereumNetwork.ropsten]: {}, - [EthereumNetwork.tenderlyMain]: {}, + [eEthereumNetwork.main]: {}, + [eEthereumNetwork.kovan]: {}, + [eEthereumNetwork.ropsten]: {}, + [eEthereumNetwork.tenderlyMain]: {}, }, ReservesConfig: {}, ATokenDomainSeparator: { diff --git a/markets/aave/index.ts b/markets/aave/index.ts index d60db80f..9683dbe1 100644 --- a/markets/aave/index.ts +++ b/markets/aave/index.ts @@ -1,5 +1,5 @@ import { oneRay, ZERO_ADDRESS } from '../../helpers/constants'; -import { IAaveConfiguration, EthereumNetwork, eEthereumNetwork } from '../../helpers/types'; +import { IAaveConfiguration, eEthereumNetwork } from '../../helpers/types'; import { CommonsConfig } from './commons'; import { @@ -60,7 +60,7 @@ export const AaveConfig: IAaveConfiguration = { [eEthereumNetwork.buidlerevm]: {}, [eEthereumNetwork.hardhat]: {}, [eEthereumNetwork.coverage]: {}, - [EthereumNetwork.kovan]: { + [eEthereumNetwork.kovan]: { AAVE: '0xB597cd8D3217ea6477232F9217fa70837ff667Af', BAT: '0x2d12186Fbb9f9a8C28B3FfdD4c42920f8539D738', BUSD: '0x4c6E1EFC12FDfD568186b7BAEc0A43fFfb4bCcCf', @@ -82,7 +82,7 @@ export const AaveConfig: IAaveConfiguration = { YFI: '0xb7c325266ec274fEb1354021D27FA3E3379D840d', ZRX: '0xD0d76886cF8D952ca26177EB7CfDf83bad08C00C', }, - [EthereumNetwork.ropsten]: { + [eEthereumNetwork.ropsten]: { AAVE: '', BAT: '0x85B24b3517E3aC7bf72a14516160541A60cFF19d', BUSD: '0xFA6adcFf6A90c11f31Bc9bb59eC0a6efB38381C6', @@ -104,7 +104,7 @@ export const AaveConfig: IAaveConfiguration = { YFI: ZERO_ADDRESS, ZRX: '0x02d7055704EfF050323A2E5ee4ba05DB2A588959', }, - [EthereumNetwork.main]: { + [eEthereumNetwork.main]: { AAVE: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', BAT: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', BUSD: '0x4Fabb145d64652a948d72533023f6E7A623C7C53', @@ -127,7 +127,7 @@ export const AaveConfig: IAaveConfiguration = { ZRX: '0xE41d2489571d322189246DaFA5ebDe1F4699F498', xSUSHI: '0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272', }, - [EthereumNetwork.tenderlyMain]: { + [eEthereumNetwork.tenderlyMain]: { AAVE: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', BAT: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', BUSD: '0x4Fabb145d64652a948d72533023f6E7A623C7C53', diff --git a/markets/aave/rateStrategies.ts b/markets/aave/rateStrategies.ts new file mode 100644 index 00000000..4a7125dd --- /dev/null +++ b/markets/aave/rateStrategies.ts @@ -0,0 +1,105 @@ +import BigNumber from 'bignumber.js'; +import { oneRay } from '../../helpers/constants'; +import { IInterestRateStrategyParams } from '../../helpers/types'; + +// BUSD SUSD +export const rateStrategyStableOne: IInterestRateStrategyParams = { + name: "rateStrategyStableOne", + optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), + stableRateSlope1: '0', + stableRateSlope2: '0', +}; + +// DAI TUSD +export const rateStrategyStableTwo: IInterestRateStrategyParams = { + name: "rateStrategyStableTwo", + optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), +} + +// USDC USDT +export const rateStrategyStableThree: IInterestRateStrategyParams = { + name: "rateStrategyStableThree", + optimalUtilizationRate: new BigNumber(0.9).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), +} + +// WETH +export const rateStrategyWETH: IInterestRateStrategyParams = { + name: "rateStrategyWETH", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), +} + +// AAVE +export const rateStrategyAAVE: IInterestRateStrategyParams = { + name: "rateStrategyAAVE", + optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: '0', + variableRateSlope1: '0', + variableRateSlope2: '0', + stableRateSlope1: '0', + stableRateSlope2: '0', +} + +// BAT ENJ LINK MANA MKR REN YFI ZRX +export const rateStrategyVolatileOne: IInterestRateStrategyParams = { + name: "rateStrategyVolatileOne", + optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} + +// KNC WBTC +export const rateStrategyVolatileTwo: IInterestRateStrategyParams = { + name: "rateStrategyVolatileTwo", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} + +// SNX +export const rateStrategyVolatileThree: IInterestRateStrategyParams = { + name: "rateStrategyVolatileThree", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} + + +export const rateStrategyVolatileFour: IInterestRateStrategyParams = { + name: "rateStrategyVolatileFour", + optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: '0', + variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: '0', + stableRateSlope2: '0', +} + + + diff --git a/markets/aave/reservesConfigs.ts b/markets/aave/reservesConfigs.ts index 30f094ac..a29e16d7 100644 --- a/markets/aave/reservesConfigs.ts +++ b/markets/aave/reservesConfigs.ts @@ -1,14 +1,19 @@ -import BigNumber from 'bignumber.js'; -import { oneRay } from '../../helpers/constants'; import { eContractid, IReserveParams } from '../../helpers/types'; +import { + rateStrategyStableOne, + rateStrategyStableTwo, + rateStrategyStableThree, + rateStrategyWETH, + rateStrategyAAVE, + rateStrategyVolatileOne, + rateStrategyVolatileTwo, + rateStrategyVolatileThree, + rateStrategyVolatileFour, +} from './rateStrategies'; + export const strategyBUSD: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), - stableRateSlope1: '0', - stableRateSlope2: '0', + strategy: rateStrategyStableOne, baseLTVAsCollateral: '0', liquidationThreshold: '0', liquidationBonus: '0', @@ -20,12 +25,7 @@ export const strategyBUSD: IReserveParams = { }; export const strategyDAI: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyStableTwo, baseLTVAsCollateral: '7500', liquidationThreshold: '8000', liquidationBonus: '10500', @@ -37,12 +37,7 @@ export const strategyDAI: IReserveParams = { }; export const strategySUSD: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), - stableRateSlope1: '0', - stableRateSlope2: '0', + strategy: rateStrategyStableOne, baseLTVAsCollateral: '0', liquidationThreshold: '0', liquidationBonus: '0', @@ -54,12 +49,7 @@ export const strategySUSD: IReserveParams = { }; export const strategyTUSD: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyStableTwo, baseLTVAsCollateral: '7500', liquidationThreshold: '8000', liquidationBonus: '10500', @@ -71,12 +61,7 @@ export const strategyTUSD: IReserveParams = { }; export const strategyUSDC: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.9).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyStableThree, baseLTVAsCollateral: '8000', liquidationThreshold: '8500', liquidationBonus: '10500', @@ -88,12 +73,7 @@ export const strategyUSDC: IReserveParams = { }; export const strategyUSDT: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.9).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyStableThree, baseLTVAsCollateral: '8000', liquidationThreshold: '8500', liquidationBonus: '10500', @@ -105,12 +85,7 @@ export const strategyUSDT: IReserveParams = { }; export const strategyAAVE: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: '0', - variableRateSlope1: '0', - variableRateSlope2: '0', - stableRateSlope1: '0', - stableRateSlope2: '0', + strategy: rateStrategyAAVE, baseLTVAsCollateral: '5000', liquidationThreshold: '6500', liquidationBonus: '11000', @@ -122,12 +97,7 @@ export const strategyAAVE: IReserveParams = { }; export const strategyBAT: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileOne, baseLTVAsCollateral: '7000', liquidationThreshold: '7500', liquidationBonus: '11000', @@ -139,12 +109,7 @@ export const strategyBAT: IReserveParams = { }; export const strategyENJ: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileOne, baseLTVAsCollateral: '5500', liquidationThreshold: '6000', liquidationBonus: '11000', @@ -156,12 +121,7 @@ export const strategyENJ: IReserveParams = { }; export const strategyWETH: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyWETH, baseLTVAsCollateral: '8000', liquidationThreshold: '8250', liquidationBonus: '10500', @@ -173,12 +133,7 @@ export const strategyWETH: IReserveParams = { }; export const strategyKNC: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileTwo, baseLTVAsCollateral: '6000', liquidationThreshold: '6500', liquidationBonus: '11000', @@ -190,12 +145,7 @@ export const strategyKNC: IReserveParams = { }; export const strategyLINK: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileOne, baseLTVAsCollateral: '7000', liquidationThreshold: '7500', liquidationBonus: '11000', @@ -207,12 +157,7 @@ export const strategyLINK: IReserveParams = { }; export const strategyMANA: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileOne, baseLTVAsCollateral: '6000', liquidationThreshold: '6500', liquidationBonus: '11000', @@ -224,12 +169,7 @@ export const strategyMANA: IReserveParams = { }; export const strategyMKR: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileOne, baseLTVAsCollateral: '6000', liquidationThreshold: '6500', liquidationBonus: '11000', @@ -241,12 +181,7 @@ export const strategyMKR: IReserveParams = { }; export const strategyREN: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileOne, baseLTVAsCollateral: '5500', liquidationThreshold: '6000', liquidationBonus: '11000', @@ -258,12 +193,7 @@ export const strategyREN: IReserveParams = { }; export const strategySNX: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: new BigNumber(0.03).multipliedBy(oneRay).toFixed(), - variableRateSlope1: new BigNumber(0.12).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), - stableRateSlope1: '0', - stableRateSlope2: '0', + strategy: rateStrategyVolatileThree, baseLTVAsCollateral: '1500', liquidationThreshold: '4000', liquidationBonus: '11000', @@ -274,13 +204,9 @@ export const strategySNX: IReserveParams = { reserveFactor: '3500' }; +// Invalid borrow rates in params currently, replaced with snx params export const strategyUNI: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: '0', - variableRateSlope1: '0', - variableRateSlope2: '0', - stableRateSlope1: '0', - stableRateSlope2: '0', + strategy: rateStrategyVolatileThree, baseLTVAsCollateral: '6000', liquidationThreshold: '6500', liquidationBonus: '11000', @@ -292,12 +218,7 @@ export const strategyUNI: IReserveParams = { }; export const strategyWBTC: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: '0', - variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileTwo, baseLTVAsCollateral: '7000', liquidationThreshold: '7500', liquidationBonus: '11000', @@ -309,12 +230,7 @@ export const strategyWBTC: IReserveParams = { }; export const strategyYFI: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: '0', - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileOne, baseLTVAsCollateral: '4000', liquidationThreshold: '5500', liquidationBonus: '11500', @@ -326,12 +242,7 @@ export const strategyYFI: IReserveParams = { }; export const strategyZRX: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: '0', - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), - stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + strategy: rateStrategyVolatileOne, baseLTVAsCollateral: '6000', liquidationThreshold: '6500', liquidationBonus: '11000', @@ -343,12 +254,7 @@ export const strategyZRX: IReserveParams = { }; export const strategyXSUSHI: IReserveParams = { - optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), - baseVariableBorrowRate: '0', - variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), - variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), - stableRateSlope1: '0', - stableRateSlope2: '0', + strategy: rateStrategyVolatileFour, baseLTVAsCollateral: '2500', liquidationThreshold: '4500', liquidationBonus: '11500', diff --git a/markets/amm/commons.ts b/markets/amm/commons.ts new file mode 100644 index 00000000..b892064d --- /dev/null +++ b/markets/amm/commons.ts @@ -0,0 +1,338 @@ +import BigNumber from 'bignumber.js'; +import { + oneEther, + oneRay, + RAY, + ZERO_ADDRESS, + MOCK_CHAINLINK_AGGREGATORS_PRICES, +} from '../../helpers/constants'; +import { ICommonConfiguration, eEthereumNetwork } from '../../helpers/types'; + +// ---------------- +// PROTOCOL GLOBAL PARAMS +// ---------------- + +export const CommonsConfig: ICommonConfiguration = { + MarketId: 'Commons', + ATokenNamePrefix: 'Aave AMM Market', + StableDebtTokenNamePrefix: 'Aave AMM Market stable debt', + VariableDebtTokenNamePrefix: 'Aave AMM Market variable debt', + SymbolPrefix: 'Amm', + ProviderId: 0, // Overriden in index.ts + ProtocolGlobalParams: { + TokenDistributorPercentageBase: '10000', + MockUsdPriceInWei: '5848466240000000', + UsdAddress: '0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96', + NilAddress: '0x0000000000000000000000000000000000000000', + OneAddress: '0x0000000000000000000000000000000000000001', + AaveReferral: '0', + }, + + // ---------------- + // COMMON PROTOCOL PARAMS ACROSS POOLS AND NETWORKS + // ---------------- + + Mocks: { + AllAssetsInitialPrices: { + ...MOCK_CHAINLINK_AGGREGATORS_PRICES, + }, + }, + // TODO: reorg alphabetically, checking the reason of tests failing + LendingRateOracleRatesCommon: { + WETH: { + borrowRate: oneRay.multipliedBy(0.03).toFixed(), + }, + DAI: { + borrowRate: oneRay.multipliedBy(0.039).toFixed(), + }, + USDC: { + borrowRate: oneRay.multipliedBy(0.039).toFixed(), + }, + USDT: { + borrowRate: oneRay.multipliedBy(0.035).toFixed(), + }, + WBTC: { + borrowRate: oneRay.multipliedBy(0.03).toFixed(), + }, + UniDAIWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniWBTCWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniAAVEWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniBATWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniDAIUSDC: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniCRVWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniLINKWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniMKRWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniRENWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniSNXWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniUNIWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniUSDCWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniWBTCUSDC: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + UniYFIWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + BptWBTCWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + BptBALWETH: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), + }, + }, + // ---------------- + // COMMON PROTOCOL ADDRESSES ACROSS POOLS + // ---------------- + + // If PoolAdmin/emergencyAdmin is set, will take priority over PoolAdminIndex/emergencyAdminIndex + PoolAdmin: { + [eEthereumNetwork.coverage]: undefined, + [eEthereumNetwork.buidlerevm]: undefined, + [eEthereumNetwork.coverage]: undefined, + [eEthereumNetwork.hardhat]: undefined, + [eEthereumNetwork.kovan]: undefined, + [eEthereumNetwork.ropsten]: undefined, + [eEthereumNetwork.main]: undefined, + [eEthereumNetwork.tenderlyMain]: undefined, + }, + PoolAdminIndex: 0, + EmergencyAdmin: { + [eEthereumNetwork.hardhat]: undefined, + [eEthereumNetwork.coverage]: undefined, + [eEthereumNetwork.buidlerevm]: undefined, + [eEthereumNetwork.kovan]: undefined, + [eEthereumNetwork.ropsten]: undefined, + [eEthereumNetwork.main]: undefined, + [eEthereumNetwork.tenderlyMain]: undefined, + }, + EmergencyAdminIndex: 1, + ProviderRegistry: { + [eEthereumNetwork.kovan]: '0x1E40B561EC587036f9789aF83236f057D1ed2A90', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '0x52D306e36E3B6B02c153d0266ff0f85d18BCD413', + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.tenderlyMain]: '0x52D306e36E3B6B02c153d0266ff0f85d18BCD413', + }, + ProviderRegistryOwner: { + // DEPLOYED WITH CORRECT ADDRESS + [eEthereumNetwork.kovan]: '0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '0xbd723fc4f1d737dcfc48a07fe7336766d34cad5f', + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.tenderlyMain]: '0xbd723fc4f1d737dcfc48a07fe7336766d34cad5f', + }, + LendingRateOracle: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', // Updated to match Kovan deployment + [eEthereumNetwork.kovan]: '0xd00Bd28FAdDa9d5658D1D4e0c151973146C7A533', //'0xE48F95873855bfd97BF89572DDf5cBC44D9c545b' + [eEthereumNetwork.ropsten]: '0x05dcca805a6562c1bdd0423768754acb6993241b', + [eEthereumNetwork.main]: '', //'0x8A32f49FFbA88aba6EFF96F45D8BD1D4b3f35c7D', // Need to re-deploy because of onlyOwner + [eEthereumNetwork.tenderlyMain]: '0x8A32f49FFbA88aba6EFF96F45D8BD1D4b3f35c7D', + }, + LendingPoolCollateralManager: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '0x9269b6453d0d75370c4c85e5a42977a53efdb72a', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '0xbd4765210d4167CE2A5b87280D9E8Ee316D5EC7C', + [eEthereumNetwork.tenderlyMain]: '0xbd4765210d4167CE2A5b87280D9E8Ee316D5EC7C', + }, + LendingPoolConfigurator: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '0x36eB31800aa67a9c50df1d56EE01981A6E14Cce5', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '', + [eEthereumNetwork.tenderlyMain]: '', + }, + LendingPool: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '0x78142De7a1930412E9e50dEB3b80dB284c2dFa3A', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '', + [eEthereumNetwork.tenderlyMain]: '', + }, + WethGateway: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '0x1c4A1cC35A477aa1cF35DF671d93ACc04d8131E0', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '', + [eEthereumNetwork.tenderlyMain]: '', + }, + TokenDistributor: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.kovan]: '0x971efe90088f21dc6a36f610ffed77fc19710708', + [eEthereumNetwork.ropsten]: '0xeba2ea67942b8250d870b12750b594696d02fc9c', + [eEthereumNetwork.main]: '0xe3d9988f676457123c5fd01297605efdd0cba1ae', + [eEthereumNetwork.tenderlyMain]: '0xe3d9988f676457123c5fd01297605efdd0cba1ae', + }, + AaveOracle: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '0x8fb777d67e9945e2c01936e319057f9d41d559e6', // Need to re-deploy because of onlyOwner + [eEthereumNetwork.ropsten]: ZERO_ADDRESS, + [eEthereumNetwork.main]: '', //'0xA50ba011c48153De246E5192C8f9258A2ba79Ca9', // Need to re-deploy because of onlyOwner + [eEthereumNetwork.tenderlyMain]: '0xA50ba011c48153De246E5192C8f9258A2ba79Ca9', + }, + FallbackOracle: { + [eEthereumNetwork.coverage]: '', + [eEthereumNetwork.hardhat]: '', + [eEthereumNetwork.buidlerevm]: '', + [eEthereumNetwork.kovan]: '0x50913E8E1c650E790F8a1E741FF9B1B1bB251dfe', + [eEthereumNetwork.ropsten]: '0xAD1a978cdbb8175b2eaeC47B01404f8AEC5f4F0d', + [eEthereumNetwork.main]: ZERO_ADDRESS, + [eEthereumNetwork.tenderlyMain]: ZERO_ADDRESS, + }, + ChainlinkAggregator: { + [eEthereumNetwork.coverage]: {}, + [eEthereumNetwork.hardhat]: {}, + [eEthereumNetwork.buidlerevm]: {}, + [eEthereumNetwork.kovan]: { + USDT: '0x0bF499444525a23E7Bb61997539725cA2e928138', + WBTC: '0xF7904a295A029a3aBDFFB6F12755974a958C7C25', + USDC: '0x64EaC61A2DFda2c3Fa04eED49AA33D021AeC8838', + DAI: '0x22B58f1EbEDfCA50feF632bD73368b2FdA96D541', + UniDAIWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', // Mock oracles + UniWBTCWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniAAVEWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniBATWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniDAIUSDC: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniCRVWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniLINKWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniMKRWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniRENWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniSNXWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniUNIWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniUSDCWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniWBTCUSDC: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + UniYFIWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + BptWBTCWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + BptBALWETH: '0x5699302154A020FB1DE2B1d39f4c73785A235d8F', + USD: '0x9326BFA02ADD2366b30bacB125260Af641031331', + }, + [eEthereumNetwork.ropsten]: {}, + [eEthereumNetwork.main]: { + USDT: '0xEe9F2375b4bdF6387aa8265dD4FB8F16512A1d46', + WBTC: '0xdeb288F737066589598e9214E782fa5A8eD689e8', + USDC: '0x986b5E1e1755e3C2440e960477f25201B0a8bbD4', + DAI: '0x773616E4d11A78F511299002da57A0a94577F1f4', + UniDAIWETH: '0xf4071801C4421Db7e63DaC15B9432e50C44a7F42', + UniWBTCWETH: '0x55EF7F1226507cFd846DE009C2f097c2211b6Fb8', + UniAAVEWETH: '0x5671387d56eAB334A2D65d6D0BB4D907898C7abA', + UniBATWETH: '0xA61ca04DF33B72b235a8A28CfB535bb7A5271B70', + UniDAIUSDC: '0xFd8dFc92B030e6BA957336e9f08C2a711e19069A', + UniCRVWETH: '0xd4D344D076256Fdf806375983b2cab2Db52FD506', + UniLINKWETH: '0x8C0e5df19B998F06e57A1Db1a38232F7590abe4b', + UniMKRWETH: '0x92f2A28fE33E0b6Ea218057EEe004E3B2B6de45d', + UniRENWETH: '0xFc0398b247107138dB494395600fB0d075b72C9A', + UniSNXWETH: '0xF5CB13c859383B5fb070bd111Cae7a900c00BA07', + UniUNIWETH: '0xE50e47E37DCF55dE1c5F2c32d346BB52064f7CE6', + UniUSDCWETH: '0xBE6ac123799572c98eFdE48895465AB392534AFD', + UniWBTCUSDC: '0xd6b8b08a0d13994A5f4a1949F4870DE57e9B40d9', + UniYFIWETH: '0x94daB35789f05f54224F6851921160DE21318072', + BptWBTCWETH: '0x4a2731c9f3B4355922c676f9b538278D79C299C5', + BptBALWETH: '0xD9400999f38E1877a6dDDb0090A327F19257f9AE', + USD: '0x9326BFA02ADD2366b30bacB125260Af641031331', + }, + [eEthereumNetwork.tenderlyMain]: { + USDT: '0xEe9F2375b4bdF6387aa8265dD4FB8F16512A1d46', + WBTC: '0xdeb288F737066589598e9214E782fa5A8eD689e8', + USDC: '0x986b5E1e1755e3C2440e960477f25201B0a8bbD4', + DAI: '0x773616E4d11A78F511299002da57A0a94577F1f4', + UniDAIWETH: '0xf4071801C4421Db7e63DaC15B9432e50C44a7F42', + UniWBTCWETH: '0x55EF7F1226507cFd846DE009C2f097c2211b6Fb8', + UniAAVEWETH: '0x5671387d56eAB334A2D65d6D0BB4D907898C7abA', + UniBATWETH: '0xA61ca04DF33B72b235a8A28CfB535bb7A5271B70', + UniDAIUSDC: '0xFd8dFc92B030e6BA957336e9f08C2a711e19069A', + UniCRVWETH: '0xd4D344D076256Fdf806375983b2cab2Db52FD506', + UniLINKWETH: '0x8C0e5df19B998F06e57A1Db1a38232F7590abe4b', + UniMKRWETH: '0x92f2A28fE33E0b6Ea218057EEe004E3B2B6de45d', + UniRENWETH: '0xFc0398b247107138dB494395600fB0d075b72C9A', + UniSNXWETH: '0xF5CB13c859383B5fb070bd111Cae7a900c00BA07', + UniUNIWETH: '0xE50e47E37DCF55dE1c5F2c32d346BB52064f7CE6', + UniUSDCWETH: '0xBE6ac123799572c98eFdE48895465AB392534AFD', + UniWBTCUSDC: '0xd6b8b08a0d13994A5f4a1949F4870DE57e9B40d9', + UniYFIWETH: '0x94daB35789f05f54224F6851921160DE21318072', + BptWBTCWETH: '0x4a2731c9f3B4355922c676f9b538278D79C299C5', + BptBALWETH: '0xD9400999f38E1877a6dDDb0090A327F19257f9AE', + USD: '0x9326BFA02ADD2366b30bacB125260Af641031331', + }, + }, + ReserveAssets: { + [eEthereumNetwork.coverage]: {}, + [eEthereumNetwork.hardhat]: {}, + [eEthereumNetwork.buidlerevm]: {}, + [eEthereumNetwork.main]: {}, + [eEthereumNetwork.kovan]: {}, + [eEthereumNetwork.ropsten]: {}, + [eEthereumNetwork.tenderlyMain]: {}, + }, + ReservesConfig: {}, + ATokenDomainSeparator: { + [eEthereumNetwork.coverage]: + '0x95b73a72c6ecf4ccbbba5178800023260bad8e75cdccdb8e4827a2977a37c820', + [eEthereumNetwork.hardhat]: + '0xbae024d959c6a022dc5ed37294cd39c141034b2ae5f02a955cce75c930a81bf5', + [eEthereumNetwork.buidlerevm]: + '0xbae024d959c6a022dc5ed37294cd39c141034b2ae5f02a955cce75c930a81bf5', + [eEthereumNetwork.kovan]: '', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '', + [eEthereumNetwork.tenderlyMain]: '', + }, + WETH: { + [eEthereumNetwork.coverage]: '', // deployed in local evm + [eEthereumNetwork.hardhat]: '', // deployed in local evm + [eEthereumNetwork.buidlerevm]: '', // deployed in local evm + [eEthereumNetwork.kovan]: '0xd0a1e359811322d97991e03f863a0c30c2cf029c', + [eEthereumNetwork.ropsten]: '0xc778417e063141139fce010982780140aa0cd5ab', + [eEthereumNetwork.main]: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + [eEthereumNetwork.tenderlyMain]: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + }, + ReserveFactorTreasuryAddress: { + [eEthereumNetwork.coverage]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', + [eEthereumNetwork.hardhat]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', + [eEthereumNetwork.buidlerevm]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', + [eEthereumNetwork.kovan]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', + [eEthereumNetwork.ropsten]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', + [eEthereumNetwork.main]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', + [eEthereumNetwork.tenderlyMain]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', + }, +}; diff --git a/markets/amm/index.ts b/markets/amm/index.ts new file mode 100644 index 00000000..e42dbdec --- /dev/null +++ b/markets/amm/index.ts @@ -0,0 +1,137 @@ +import { oneRay, ZERO_ADDRESS } from '../../helpers/constants'; +import { IAmmConfiguration, eEthereumNetwork } from '../../helpers/types'; + +import { CommonsConfig } from './commons'; +import { + strategyDAI, + strategyUSDC, + strategyUSDT, + strategyWETH, + strategyWBTC, + strategyWBTCWETH, + strategyDAIWETH, + strategyAAVEWETH, + strategyBATWETH, + strategyDAIUSDC, + strategyCRVWETH, + strategyLINKWETH, + strategyMKRWETH, + strategyRENWETH, + strategySNXWETH, + strategyUNIWETH, + strategyUSDCWETH, + strategyWBTCUSDC, + strategyYFIWETH, + strategyBALWETH, +} from './reservesConfigs'; + +// ---------------- +// POOL--SPECIFIC PARAMS +// ---------------- + +export const AmmConfig: IAmmConfiguration = { + ...CommonsConfig, + MarketId: 'Aave AMM market', + ProviderId: 2, + ReservesConfig: { + WETH: strategyWETH, + DAI: strategyDAI, + USDC: strategyUSDC, + USDT: strategyUSDT, + WBTC: strategyWBTC, + UniDAIWETH: strategyDAIWETH, + UniWBTCWETH: strategyWBTCWETH, + UniAAVEWETH: strategyAAVEWETH, + UniBATWETH: strategyBATWETH, + UniDAIUSDC: strategyDAIUSDC, + UniCRVWETH: strategyCRVWETH, + UniLINKWETH: strategyLINKWETH, + UniMKRWETH: strategyMKRWETH, + UniRENWETH: strategyRENWETH, + UniSNXWETH: strategySNXWETH, + UniUNIWETH: strategyUNIWETH, + UniUSDCWETH: strategyUSDCWETH, + UniWBTCUSDC: strategyWBTCUSDC, + UniYFIWETH: strategyYFIWETH, + BptWBTCWETH: strategyWBTCWETH, + BptBALWETH: strategyBALWETH, + }, + ReserveAssets: { + [eEthereumNetwork.buidlerevm]: {}, + [eEthereumNetwork.hardhat]: {}, + [eEthereumNetwork.coverage]: {}, + [eEthereumNetwork.kovan]: { + DAI: '0xFf795577d9AC8bD7D90Ee22b6C1703490b6512FD', + USDC: '0xe22da380ee6B445bb8273C81944ADEB6E8450422', + USDT: '0x13512979ADE267AB5100878E2e0f485B568328a4', + WBTC: '0xD1B98B6607330172f1D991521145A22BCe793277', + WETH: '0xd0a1e359811322d97991e03f863a0c30c2cf029c', + UniDAIWETH: '0x0C652EeEA3d7D35759ba1E16183F1D89C386C9ea', + UniWBTCWETH: '0x796d562B1dF5b9dc85A4612187B6f29Ed213d960', + UniAAVEWETH: '0x657A7B8b46F35C5C6583AEF43824744B236EF826', + UniBATWETH: '0xf8CEBA8b16579956B3aE4B5D09002a30f873F783', + UniDAIUSDC: '0x8e80b7a7531c276dD1dBEC2f1Cc281c11c859e62', + UniCRVWETH: '0x9c31b7538467bF0b01e6d5fA789e66Ce540a521e', + UniLINKWETH: '0x5Acab7f8B79620ec7127A96E5D8837d2124D5D7c', + UniMKRWETH: '0xB0C6EC5d58ddbF4cd1e419A56a19924E9904e4Dd', + UniRENWETH: '0xcF428637A9f8Af21920Bc0A94fd81071bc790105', + UniSNXWETH: '0xc8F2a0d698f675Ece74042e9fB06ea52b9517521', + UniUNIWETH: '0xcC99A5f95a86d30e3DeF113bCf22f00ecF90D050', + UniUSDCWETH: '0x8C00D2428ed1857E61652aca663323A85E6e76a9', + UniWBTCUSDC: '0x3d35B5F289f55A580e6F85eE22E6a8f57053b966', + UniYFIWETH: '0x5af95ddFACC150a1695A3Fc606459fd0dE57b91f', + BptWBTCWETH: '0x110569E3261bC0934dA637b019f6f1b6F50ec574', + BptBALWETH: '0xad01D8e0Fa9EAA8Fe76dA30CFb1BCe12707aE6c5', + }, + [eEthereumNetwork.ropsten]: { + }, + [eEthereumNetwork.main]: { + DAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + USDT: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + WBTC: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + UniDAIWETH: '0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11', + UniWBTCWETH: '0xBb2b8038a1640196FbE3e38816F3e67Cba72D940', + UniAAVEWETH: '0xDFC14d2Af169B0D36C4EFF567Ada9b2E0CAE044f', + UniBATWETH: '0xB6909B960DbbE7392D405429eB2b3649752b4838', + UniDAIUSDC: '0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5', + UniCRVWETH: '0x3dA1313aE46132A397D90d95B1424A9A7e3e0fCE', + UniLINKWETH: '0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974', + UniMKRWETH: '0xC2aDdA861F89bBB333c90c492cB837741916A225', + UniRENWETH: '0x8Bd1661Da98EBDd3BD080F0bE4e6d9bE8cE9858c', + UniSNXWETH: '0x43AE24960e5534731Fc831386c07755A2dc33D47', + UniUNIWETH: '0xd3d2E2692501A5c9Ca623199D38826e513033a17', + UniUSDCWETH: '0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc', + UniWBTCUSDC: '0x004375Dff511095CC5A197A54140a24eFEF3A416', + UniYFIWETH: '0x2fDbAdf3C4D5A8666Bc06645B8358ab803996E28', + BptWBTCWETH: '0x1efF8aF5D577060BA4ac8A29A13525bb0Ee2A3D5', + BptBALWETH: '0x59A19D8c652FA0284f44113D0ff9aBa70bd46fB4', + }, + [eEthereumNetwork.tenderlyMain]: { + DAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + USDT: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + WBTC: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + UniDAIWETH: '0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11', + UniWBTCWETH: '0xBb2b8038a1640196FbE3e38816F3e67Cba72D940', + UniAAVEWETH: '0xDFC14d2Af169B0D36C4EFF567Ada9b2E0CAE044f', + UniBATWETH: '0xB6909B960DbbE7392D405429eB2b3649752b4838', + UniDAIUSDC: '0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5', + UniCRVWETH: '0x3dA1313aE46132A397D90d95B1424A9A7e3e0fCE', + UniLINKWETH: '0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974', + UniMKRWETH: '0xC2aDdA861F89bBB333c90c492cB837741916A225', + UniRENWETH: '0x8Bd1661Da98EBDd3BD080F0bE4e6d9bE8cE9858c', + UniSNXWETH: '0x43AE24960e5534731Fc831386c07755A2dc33D47', + UniUNIWETH: '0xd3d2E2692501A5c9Ca623199D38826e513033a17', + UniUSDCWETH: '0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc', + UniWBTCUSDC: '0x004375Dff511095CC5A197A54140a24eFEF3A416', + UniYFIWETH: '0x2fDbAdf3C4D5A8666Bc06645B8358ab803996E28', + BptWBTCWETH: '0x1efF8aF5D577060BA4ac8A29A13525bb0Ee2A3D5', + BptBALWETH: '0x59A19D8c652FA0284f44113D0ff9aBa70bd46fB4', + }, + }, +}; + +export default AmmConfig; diff --git a/markets/amm/rateStrategies.ts b/markets/amm/rateStrategies.ts new file mode 100644 index 00000000..e6df73ce --- /dev/null +++ b/markets/amm/rateStrategies.ts @@ -0,0 +1,36 @@ +import BigNumber from 'bignumber.js'; +import { oneRay } from '../../helpers/constants'; +import { IInterestRateStrategyParams } from '../../helpers/types'; + +// DAIWETH WBTCWETH AAVEWETH BATWETH DAIUSDC CRVWETH LINKWETH MKRWETH RENWETH SNXWETH UNIWETH USDCWETH WBTCUSDC YFIWETH +export const rateStrategyAmmBase: IInterestRateStrategyParams = { + name: "rateStrategyAmmBase", + optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0.03).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.10).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3.00).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} + +// WETH WBTC +export const rateStrategyBaseOne: IInterestRateStrategyParams = { + name: "rateStrategyBaseOne", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), +} + +// DAI USDC USDT +export const rateStrategyStable: IInterestRateStrategyParams = { + name: "rateStrategyStable", + optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), +} \ No newline at end of file diff --git a/markets/amm/reservesConfigs.ts b/markets/amm/reservesConfigs.ts new file mode 100644 index 00000000..c92aa42f --- /dev/null +++ b/markets/amm/reservesConfigs.ts @@ -0,0 +1,247 @@ +import { eContractid, IReserveParams} from '../../helpers/types'; +import { + rateStrategyAmmBase, + rateStrategyStable, + rateStrategyBaseOne, +} from './rateStrategies'; + + +export const strategyWETH: IReserveParams = { + strategy: rateStrategyBaseOne, + baseLTVAsCollateral: '8000', + liquidationThreshold: '8250', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyWBTC: IReserveParams = { + strategy: rateStrategyBaseOne, + baseLTVAsCollateral: '7000', + liquidationThreshold: '7500', + liquidationBonus: '11000', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '8', + aTokenImpl: eContractid.AToken, + reserveFactor: '2000' +}; + +export const strategyDAI: IReserveParams = { + strategy: rateStrategyStable, + baseLTVAsCollateral: '7500', + liquidationThreshold: '8000', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyUSDC: IReserveParams = { + strategy: rateStrategyStable, + baseLTVAsCollateral: '8000', + liquidationThreshold: '8500', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '6', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyUSDT: IReserveParams = { + strategy: rateStrategyStable, + baseLTVAsCollateral: '-1', + liquidationThreshold: '8500', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '6', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyDAIWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyWBTCWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategyAAVEWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '500' +}; + +export const strategyBATWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategyDAIUSDC: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyCRVWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '5000', + liquidationThreshold: '6000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategyLINKWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategyMKRWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategyRENWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategySNXWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '4000', + liquidationThreshold: '6000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '2000' +}; + +export const strategyUNIWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategyUSDCWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyWBTCUSDC: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategyYFIWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '5000', + liquidationThreshold: '6000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +}; + +export const strategyBALWETH: IReserveParams = { + strategy: rateStrategyAmmBase, + baseLTVAsCollateral: '6000', + liquidationThreshold: '7000', + liquidationBonus: '11500', + borrowingEnabled: true, + stableBorrowRateEnabled: false, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1500' +} \ No newline at end of file diff --git a/markets/arbitrum/commons.ts b/markets/arbitrum/commons.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/arbitrum/index.ts b/markets/arbitrum/index.ts new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/markets/arbitrum/index.ts @@ -0,0 +1 @@ + diff --git a/markets/arbitrum/rateStrategies.ts b/markets/arbitrum/rateStrategies.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/arbitrum/reservesConfigs.ts b/markets/arbitrum/reservesConfigs.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/bsc/commons.ts b/markets/bsc/commons.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/bsc/index.ts b/markets/bsc/index.ts new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/markets/bsc/index.ts @@ -0,0 +1 @@ + diff --git a/markets/bsc/rateStrategies.ts b/markets/bsc/rateStrategies.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/bsc/reservesConfigs.ts b/markets/bsc/reservesConfigs.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/matic/commons.ts b/markets/matic/commons.ts new file mode 100644 index 00000000..42b269b0 --- /dev/null +++ b/markets/matic/commons.ts @@ -0,0 +1,144 @@ +import BigNumber from 'bignumber.js'; +import { oneEther, oneRay, RAY, ZERO_ADDRESS, MOCK_CHAINLINK_AGGREGATORS_PRICES } from '../../helpers/constants'; +import { ICommonConfiguration, ePolygonNetwork } from '../../helpers/types'; + +// ---------------- +// PROTOCOL GLOBAL PARAMS +// ---------------- + +export const CommonsConfig: ICommonConfiguration = { + MarketId: 'Commons', + ATokenNamePrefix: 'Aave Matic Market', + StableDebtTokenNamePrefix: 'Aave Matic Market stable debt', + VariableDebtTokenNamePrefix: 'Aave Matic Market variable debt', + SymbolPrefix: 'm', + ProviderId: 0, // Overriden in index.ts + ProtocolGlobalParams: { + TokenDistributorPercentageBase: '10000', + MockUsdPriceInWei: '5848466240000000', + UsdAddress: '0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96', + NilAddress: '0x0000000000000000000000000000000000000000', + OneAddress: '0x0000000000000000000000000000000000000001', + AaveReferral: '0', + }, + + // ---------------- + // COMMON PROTOCOL PARAMS ACROSS POOLS AND NETWORKS + // ---------------- + + Mocks: { + AllAssetsInitialPrices: { + ...MOCK_CHAINLINK_AGGREGATORS_PRICES, + }, + }, + // TODO: reorg alphabetically, checking the reason of tests failing + LendingRateOracleRatesCommon: { + WETH: { + borrowRate: oneRay.multipliedBy(0.03).toFixed(), + }, + DAI: { + borrowRate: oneRay.multipliedBy(0.039).toFixed(), + }, + USDC: { + borrowRate: oneRay.multipliedBy(0.039).toFixed(), + }, + USDT: { + borrowRate: oneRay.multipliedBy(0.035).toFixed(), + }, + WBTC: { + borrowRate: oneRay.multipliedBy(0.03).toFixed(), + }, + WMATIC: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), // TEMP + }, + }, + // ---------------- + // COMMON PROTOCOL ADDRESSES ACROSS POOLS + // ---------------- + + // If PoolAdmin/emergencyAdmin is set, will take priority over PoolAdminIndex/emergencyAdminIndex + PoolAdmin: { + [ePolygonNetwork.mumbai]: undefined, + [ePolygonNetwork.matic]: undefined, + }, + PoolAdminIndex: 0, + EmergencyAdmin: { + [ePolygonNetwork.mumbai]: undefined, + [ePolygonNetwork.matic]: undefined, + }, + LendingPool: { + [ePolygonNetwork.mumbai]: '', + [ePolygonNetwork.matic]: '0xABdC61Cd16e5111f335f4135B7A0e65Cc7F86327', + }, + LendingPoolConfigurator: { + [ePolygonNetwork.mumbai]: '', + [ePolygonNetwork.matic]: '0x17c4A170FFF882862F656597889016D3a6073534', + }, + EmergencyAdminIndex: 1, + ProviderRegistry: { + [ePolygonNetwork.mumbai]: '0x569859d41499B4dDC28bfaA43915051FF0A38a6F', // TEMP + [ePolygonNetwork.matic]: '0x28334e4791860a0c1eCF89a62B973ba04a5d643F', // TEMP + }, + ProviderRegistryOwner: { + [ePolygonNetwork.mumbai]: '0x18d9bA2baEfBdE0FF137C4ad031427EF205f1Fd9', // TEMP + [ePolygonNetwork.matic]: '0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F', // TEMP + }, + LendingRateOracle: { + [ePolygonNetwork.mumbai]: '', + [ePolygonNetwork.matic]: '', + }, + LendingPoolCollateralManager: { + [ePolygonNetwork.mumbai]: '', + [ePolygonNetwork.matic]: '0x9Af76e0575D139570D3B4c821567Fe935E8c25C5', + }, + TokenDistributor: { + [ePolygonNetwork.mumbai]: '', + [ePolygonNetwork.matic]: '', + }, + WethGateway: { + [ePolygonNetwork.mumbai]: '', + [ePolygonNetwork.matic]: '0x15A46f5073789b7D16F6F46632aE50Bae838d938', + }, + AaveOracle: { + [ePolygonNetwork.mumbai]: '', + [ePolygonNetwork.matic]: '0x1B38fa90596F2C25bCf1B193A6c6a718349AFDfC', + }, + FallbackOracle: { + [ePolygonNetwork.mumbai]: ZERO_ADDRESS, + [ePolygonNetwork.matic]: ZERO_ADDRESS, + }, + ChainlinkAggregator: { + [ePolygonNetwork.matic]: { + DAI: '0x4746DeC9e833A82EC7C2C1356372CcF2cfcD2F3D', + USDC: '0xfE4A8cc5b5B2366C1B58Bea3858e81843581b2F7', + USDT: '0x0A6513e40db6EB1b165753AD52E80663aeA50545', + WBTC: '0xc907E116054Ad103354f2D350FD2514433D57F6f', + WETH: '0xF9680D99D6C9589e2a93a78A04A279e509205945', + WMATIC: '0xAB594600376Ec9fD91F8e885dADF0CE036862dE0', + }, + [ePolygonNetwork.mumbai]: { + DAI: ZERO_ADDRESS, + USDC: ZERO_ADDRESS, + USDT: ZERO_ADDRESS, + WBTC: ZERO_ADDRESS, + WMATIC: ZERO_ADDRESS, + }, + }, + ReserveAssets: { + [ePolygonNetwork.matic]: {}, + [ePolygonNetwork.mumbai]: {}, + }, + ReservesConfig: {}, + ATokenDomainSeparator: { + [ePolygonNetwork.mumbai]: '', + [ePolygonNetwork.matic]: '', + }, + WETH: { + [ePolygonNetwork.mumbai]: '0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889', // WMATIC address (untested) + [ePolygonNetwork.matic]: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', // WMATIC address + }, + ReserveFactorTreasuryAddress: { + [ePolygonNetwork.mumbai]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', // TEMP + [ePolygonNetwork.matic]: '0x464c71f6c2f760dda6093dcb91c24c39e5d6e18c', // TEMP + }, +}; diff --git a/markets/matic/index.ts b/markets/matic/index.ts new file mode 100644 index 00000000..cc1484ae --- /dev/null +++ b/markets/matic/index.ts @@ -0,0 +1,50 @@ +import { oneRay, ZERO_ADDRESS } from '../../helpers/constants'; +import { IMaticConfiguration, ePolygonNetwork } from '../../helpers/types'; + +import { CommonsConfig } from './commons'; +import { + strategyDAI, + strategyUSDC, + strategyUSDT, + strategyWBTC, + strategyWETH, + strategyMATIC, +} from './reservesConfigs'; + +// ---------------- +// POOL--SPECIFIC PARAMS +// ---------------- + +export const MaticConfig: IMaticConfiguration = { + ...CommonsConfig, + MarketId: 'Matic Market', + ProviderId: 3, // Unknown? + ReservesConfig: { + DAI: strategyDAI, + USDC: strategyUSDC, + USDT: strategyUSDT, + WBTC: strategyWBTC, + WETH: strategyWETH, + WMATIC: strategyMATIC, + }, + ReserveAssets: { + [ePolygonNetwork.matic]: { + DAI: '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', + USDC: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', + USDT: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', + WBTC: '0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6', + WETH: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619', + WMATIC: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', + }, + [ePolygonNetwork.mumbai]: { // Mock tokens with a simple "mint" external function, except wmatic + DAI: '0x13b3fda609C1eeb23b4F4b69257840760dCa6C4a', + USDC: '0x52b63223994433FdE2F1350Ba69Dfd2779f06ABA', + USDT: '0xB3abd1912F586fDFFa13606882c28E27913853d2', + WBTC: '0x393E3512d45a956A628124665672312ea86930Ba', + WETH: '0x53CDb16B8C031B779e996406546614E5F05BC4Bf', + WMATIC: '0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889', + }, + }, +}; + +export default MaticConfig; diff --git a/markets/matic/rateStrategies.ts b/markets/matic/rateStrategies.ts new file mode 100644 index 00000000..afe19b63 --- /dev/null +++ b/markets/matic/rateStrategies.ts @@ -0,0 +1,91 @@ +import BigNumber from 'bignumber.js'; +import { oneRay } from '../../helpers/constants'; +import { IInterestRateStrategyParams } from '../../helpers/types'; + +// BUSD SUSD +export const rateStrategyStableOne: IInterestRateStrategyParams = { + name: "rateStrategyStableOne", + optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), + stableRateSlope1: '0', + stableRateSlope2: '0', +}; + +// DAI TUSD +export const rateStrategyStableTwo: IInterestRateStrategyParams = { + name: "rateStrategyStableTwo", + optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), +} + +// USDC USDT +export const rateStrategyStableThree: IInterestRateStrategyParams = { + name: "rateStrategyStableThree", + optimalUtilizationRate: new BigNumber(0.9).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), +} + +// WETH +export const rateStrategyWETH: IInterestRateStrategyParams = { + name: "rateStrategyWETH", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), +} + +// AAVE +export const rateStrategyAAVE: IInterestRateStrategyParams = { + name: "rateStrategyAAVE", + optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: '0', + variableRateSlope1: '0', + variableRateSlope2: '0', + stableRateSlope1: '0', + stableRateSlope2: '0', +} + +// BAT ENJ LINK MANA MKR REN YFI ZRX +export const rateStrategyVolatileOne: IInterestRateStrategyParams = { + name: "rateStrategyVolatileOne", + optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} + +// KNC WBTC +export const rateStrategyVolatileTwo: IInterestRateStrategyParams = { + name: "rateStrategyVolatileTwo", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} + +// SNX +export const rateStrategyVolatileThree: IInterestRateStrategyParams = { + name: "rateStrategyVolatileThree", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} diff --git a/markets/matic/reservesConfigs.ts b/markets/matic/reservesConfigs.ts new file mode 100644 index 00000000..608d028b --- /dev/null +++ b/markets/matic/reservesConfigs.ts @@ -0,0 +1,85 @@ +// import BigNumber from 'bignumber.js'; +// import { oneRay } from '../../helpers/constants'; +import { eContractid, IReserveParams } from '../../helpers/types'; +import { + rateStrategyStableOne, + rateStrategyStableTwo, + rateStrategyStableThree, + rateStrategyWETH, + rateStrategyAAVE, + rateStrategyVolatileOne, + rateStrategyVolatileTwo, + rateStrategyVolatileThree, +} from './rateStrategies'; + +export const strategyDAI: IReserveParams = { + strategy: rateStrategyStableTwo, + baseLTVAsCollateral: '7500', + liquidationThreshold: '8000', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyUSDC: IReserveParams = { + strategy: rateStrategyStableThree, + baseLTVAsCollateral: '8000', + liquidationThreshold: '8500', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '6', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyUSDT: IReserveParams = { + strategy: rateStrategyStableThree, + baseLTVAsCollateral: '8000', + liquidationThreshold: '8500', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '6', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyWETH: IReserveParams = { + strategy: rateStrategyWETH, + baseLTVAsCollateral: '8000', + liquidationThreshold: '8250', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyWBTC: IReserveParams = { + strategy: rateStrategyVolatileTwo, + baseLTVAsCollateral: '7000', + liquidationThreshold: '7500', + liquidationBonus: '11000', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '8', + aTokenImpl: eContractid.AToken, + reserveFactor: '2000' +}; + +export const strategyMATIC: IReserveParams = { + strategy: rateStrategyVolatileOne, //Temp? + baseLTVAsCollateral: '5000', + liquidationThreshold: '6500', + liquidationBonus: '11000', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '2000' +}; \ No newline at end of file diff --git a/markets/ovm/commons.ts b/markets/ovm/commons.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/ovm/index.ts b/markets/ovm/index.ts new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/markets/ovm/index.ts @@ -0,0 +1 @@ + diff --git a/markets/ovm/rateStrategies.ts b/markets/ovm/rateStrategies.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/ovm/reservesConfigs.ts b/markets/ovm/reservesConfigs.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/solana/commons.ts b/markets/solana/commons.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/solana/index.ts b/markets/solana/index.ts new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/markets/solana/index.ts @@ -0,0 +1 @@ + diff --git a/markets/solana/rateStrategies.ts b/markets/solana/rateStrategies.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/solana/reservesConfigs.ts b/markets/solana/reservesConfigs.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/xdai/commons.ts b/markets/xdai/commons.ts new file mode 100644 index 00000000..433d54ea --- /dev/null +++ b/markets/xdai/commons.ts @@ -0,0 +1,120 @@ +import BigNumber from 'bignumber.js'; +import { oneEther, oneRay, RAY, ZERO_ADDRESS, MOCK_CHAINLINK_AGGREGATORS_PRICES } from '../../helpers/constants'; +import { ICommonConfiguration, eXDaiNetwork } from '../../helpers/types'; + +// ---------------- +// PROTOCOL GLOBAL PARAMS +// ---------------- + +export const CommonsConfig: ICommonConfiguration = { + MarketId: 'Commons', + ATokenNamePrefix: 'Aave XDAI Market', + StableDebtTokenNamePrefix: 'Aave XDAI Market stable debt', + VariableDebtTokenNamePrefix: 'Aave XDAI Market variable debt', + SymbolPrefix: 'm', + ProviderId: 0, // Overriden in index.ts + ProtocolGlobalParams: { + TokenDistributorPercentageBase: '10000', + MockUsdPriceInWei: '5848466240000000', + UsdAddress: '0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96', + NilAddress: '0x0000000000000000000000000000000000000000', + OneAddress: '0x0000000000000000000000000000000000000001', + AaveReferral: '0', + }, + + // ---------------- + // COMMON PROTOCOL PARAMS ACROSS POOLS AND NETWORKS + // ---------------- + + Mocks: { + AllAssetsInitialPrices: { + ...MOCK_CHAINLINK_AGGREGATORS_PRICES, + }, + }, + // TODO: reorg alphabetically, checking the reason of tests failing + LendingRateOracleRatesCommon: { + WETH: { + borrowRate: oneRay.multipliedBy(0.03).toFixed(), + }, + DAI: { + borrowRate: oneRay.multipliedBy(0.039).toFixed(), + }, + USDC: { + borrowRate: oneRay.multipliedBy(0.039).toFixed(), + }, + USDT: { + borrowRate: oneRay.multipliedBy(0.035).toFixed(), + }, + WBTC: { + borrowRate: oneRay.multipliedBy(0.03).toFixed(), + }, + STAKE: { + borrowRate: oneRay.multipliedBy(0.05).toFixed(), // TEMP + }, + }, + // ---------------- + // COMMON PROTOCOL ADDRESSES ACROSS POOLS + // ---------------- + + // If PoolAdmin/emergencyAdmin is set, will take priority over PoolAdminIndex/emergencyAdminIndex + PoolAdmin: { + [eXDaiNetwork.xdai]: undefined, + }, + PoolAdminIndex: 0, + EmergencyAdmin: { + [eXDaiNetwork.xdai]: undefined, + }, + EmergencyAdminIndex: 1, + ProviderRegistry: { + [eXDaiNetwork.xdai]: '', + }, + ProviderRegistryOwner: { + [eXDaiNetwork.xdai]: '', + }, + LendingPoolConfigurator: { + [eXDaiNetwork.xdai]: '0', + }, + LendingPool: { + [eXDaiNetwork.xdai]: '0', + }, + LendingRateOracle: { + [eXDaiNetwork.xdai]: '', + }, + LendingPoolCollateralManager: { + [eXDaiNetwork.xdai]: '', + }, + TokenDistributor: { + [eXDaiNetwork.xdai]: '', + }, + WethGateway: { + [eXDaiNetwork.xdai]: '', + }, + AaveOracle: { + [eXDaiNetwork.xdai]: '', + }, + FallbackOracle: { + [eXDaiNetwork.xdai]: ZERO_ADDRESS, + }, + ChainlinkAggregator: { + [eXDaiNetwork.xdai]: { + DAI: ZERO_ADDRESS, + USDC: ZERO_ADDRESS, + USDT: ZERO_ADDRESS, + WBTC: ZERO_ADDRESS, + STAKE: ZERO_ADDRESS, + }, + }, + ReserveAssets: { + [eXDaiNetwork.xdai]: {}, + }, + ReservesConfig: {}, + ATokenDomainSeparator: { + [eXDaiNetwork.xdai]: '', + }, + WETH: { + [eXDaiNetwork.xdai]: '', // DAI: xDAI is the base token, DAI is also there, We need WXDAI + }, + ReserveFactorTreasuryAddress: { + [eXDaiNetwork.xdai]: '', // TEMP + }, +}; diff --git a/markets/xdai/index.ts b/markets/xdai/index.ts new file mode 100644 index 00000000..fadf52c8 --- /dev/null +++ b/markets/xdai/index.ts @@ -0,0 +1,42 @@ +import { oneRay, ZERO_ADDRESS } from '../../helpers/constants'; +import { IXDAIConfiguration, eXDaiNetwork } from '../../helpers/types'; + +import { CommonsConfig } from './commons'; +import { + strategyDAI, + strategyUSDC, + strategyUSDT, + strategyWBTC, + strategyWETH, + strategySTAKE, +} from './reservesConfigs'; + +// ---------------- +// POOL--SPECIFIC PARAMS +// ---------------- + +export const XDAIConfig: IXDAIConfiguration = { + ...CommonsConfig, + MarketId: 'XDAI Market', + ProviderId: 4, // Unknown? + ReservesConfig: { + DAI: strategyDAI, + USDC: strategyUSDC, + USDT: strategyUSDT, + WBTC: strategyWBTC, + WETH: strategyWETH, + STAKE: strategySTAKE, + }, + ReserveAssets: { + [eXDaiNetwork.xdai]: { + DAI: '0x44fA8E6f47987339850636F88629646662444217', + USDC: '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83', + USDT: '0x4ECaBa5870353805a9F068101A40E0f32ed605C6', + WBTC: '0x8e5bBbb09Ed1ebdE8674Cda39A0c169401db4252', + WETH: '0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1', + STAKE: '0xb7D311E2Eb55F2f68a9440da38e7989210b9A05e' + }, + }, +}; + +export default XDAIConfig; diff --git a/markets/xdai/rateStrategies.ts b/markets/xdai/rateStrategies.ts new file mode 100644 index 00000000..afe19b63 --- /dev/null +++ b/markets/xdai/rateStrategies.ts @@ -0,0 +1,91 @@ +import BigNumber from 'bignumber.js'; +import { oneRay } from '../../helpers/constants'; +import { IInterestRateStrategyParams } from '../../helpers/types'; + +// BUSD SUSD +export const rateStrategyStableOne: IInterestRateStrategyParams = { + name: "rateStrategyStableOne", + optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), + stableRateSlope1: '0', + stableRateSlope2: '0', +}; + +// DAI TUSD +export const rateStrategyStableTwo: IInterestRateStrategyParams = { + name: "rateStrategyStableTwo", + optimalUtilizationRate: new BigNumber(0.8).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(0.75).multipliedBy(oneRay).toFixed(), +} + +// USDC USDT +export const rateStrategyStableThree: IInterestRateStrategyParams = { + name: "rateStrategyStableThree", + optimalUtilizationRate: new BigNumber(0.9).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.04).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.02).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(0.60).multipliedBy(oneRay).toFixed(), +} + +// WETH +export const rateStrategyWETH: IInterestRateStrategyParams = { + name: "rateStrategyWETH", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(1).multipliedBy(oneRay).toFixed(), +} + +// AAVE +export const rateStrategyAAVE: IInterestRateStrategyParams = { + name: "rateStrategyAAVE", + optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: '0', + variableRateSlope1: '0', + variableRateSlope2: '0', + stableRateSlope1: '0', + stableRateSlope2: '0', +} + +// BAT ENJ LINK MANA MKR REN YFI ZRX +export const rateStrategyVolatileOne: IInterestRateStrategyParams = { + name: "rateStrategyVolatileOne", + optimalUtilizationRate: new BigNumber(0.45).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.07).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} + +// KNC WBTC +export const rateStrategyVolatileTwo: IInterestRateStrategyParams = { + name: "rateStrategyVolatileTwo", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} + +// SNX +export const rateStrategyVolatileThree: IInterestRateStrategyParams = { + name: "rateStrategyVolatileThree", + optimalUtilizationRate: new BigNumber(0.65).multipliedBy(oneRay).toFixed(), + baseVariableBorrowRate: new BigNumber(0).multipliedBy(oneRay).toFixed(), + variableRateSlope1: new BigNumber(0.08).multipliedBy(oneRay).toFixed(), + variableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), + stableRateSlope1: new BigNumber(0.1).multipliedBy(oneRay).toFixed(), + stableRateSlope2: new BigNumber(3).multipliedBy(oneRay).toFixed(), +} diff --git a/markets/xdai/reservesConfigs.ts b/markets/xdai/reservesConfigs.ts new file mode 100644 index 00000000..6c90afc5 --- /dev/null +++ b/markets/xdai/reservesConfigs.ts @@ -0,0 +1,85 @@ +// import BigNumber from 'bignumber.js'; +// import { oneRay } from '../../helpers/constants'; +import { eContractid, IReserveParams } from '../../helpers/types'; +import { + rateStrategyStableOne, + rateStrategyStableTwo, + rateStrategyStableThree, + rateStrategyWETH, + rateStrategyAAVE, + rateStrategyVolatileOne, + rateStrategyVolatileTwo, + rateStrategyVolatileThree, +} from './rateStrategies'; + +export const strategyDAI: IReserveParams = { + strategy: rateStrategyStableTwo, + baseLTVAsCollateral: '7500', + liquidationThreshold: '8000', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyUSDC: IReserveParams = { + strategy: rateStrategyStableThree, + baseLTVAsCollateral: '8000', + liquidationThreshold: '8500', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '6', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyUSDT: IReserveParams = { + strategy: rateStrategyStableThree, + baseLTVAsCollateral: '8000', + liquidationThreshold: '8500', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '6', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyWETH: IReserveParams = { + strategy: rateStrategyWETH, + baseLTVAsCollateral: '8000', + liquidationThreshold: '8250', + liquidationBonus: '10500', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '1000' +}; + +export const strategyWBTC: IReserveParams = { + strategy: rateStrategyVolatileTwo, + baseLTVAsCollateral: '7000', + liquidationThreshold: '7500', + liquidationBonus: '11000', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '8', + aTokenImpl: eContractid.AToken, + reserveFactor: '2000' +}; + +export const strategySTAKE: IReserveParams = { + strategy: rateStrategyVolatileOne, //Temp? + baseLTVAsCollateral: '5000', + liquidationThreshold: '6500', + liquidationBonus: '11000', + borrowingEnabled: true, + stableBorrowRateEnabled: true, + reserveDecimals: '18', + aTokenImpl: eContractid.AToken, + reserveFactor: '2000' +}; \ No newline at end of file diff --git a/markets/zksync/commons.ts b/markets/zksync/commons.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/zksync/index.ts b/markets/zksync/index.ts new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/markets/zksync/index.ts @@ -0,0 +1 @@ + diff --git a/markets/zksync/rateStrategies.ts b/markets/zksync/rateStrategies.ts new file mode 100644 index 00000000..e69de29b diff --git a/markets/zksync/reservesConfigs.ts b/markets/zksync/reservesConfigs.ts new file mode 100644 index 00000000..e69de29b diff --git a/package-lock.json b/package-lock.json index 7df01b18..80fbb840 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1172,9 +1172,9 @@ } }, "@tenderly/hardhat-tenderly": { - "version": "1.1.0-beta.3", - "resolved": "https://registry.npmjs.org/@tenderly/hardhat-tenderly/-/hardhat-tenderly-1.1.0-beta.3.tgz", - "integrity": "sha512-CCiS3bBCc4MhOTI5oHRAuVy/Xan6/8oNnjiwbsRvG1hdUis+EL/UVwn5yrUM1qXQTPz/La3TvRkfEa/pq1gimw==", + "version": "1.1.0-beta.4", + "resolved": "https://registry.npmjs.org/@tenderly/hardhat-tenderly/-/hardhat-tenderly-1.1.0-beta.4.tgz", + "integrity": "sha512-B1KU0e+17KKstFLgyN1jy6ynNTZoHaVCy+1Wkx7WFnMJTfXQZEijmIyw48dLjUKvbaKWvgP7dE3VSoQnfKwo8g==", "dev": true, "requires": { "axios": "^0.20.0", @@ -1183,21 +1183,21 @@ }, "dependencies": { "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", - "universalify": "^1.0.0" + "universalify": "^2.0.0" } }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -1212,20 +1212,12 @@ "requires": { "graceful-fs": "^4.1.6", "universalify": "^2.0.0" - }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } } }, "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true } } @@ -1798,7 +1790,7 @@ "requires": { "underscore": "1.9.1", "web3-core-helpers": "1.2.1", - "websocket": "github:web3-js/WebSocket-Node#polyfill/globalThis" + "websocket": "websocket@github:web3-js/WebSocket-Node#polyfill/globalThis" } }, "web3-shh": { @@ -5583,12 +5575,14 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "ansi-styles": { "version": "3.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -5596,7 +5590,8 @@ }, "bindings": { "version": "1.5.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dev": true, "requires": { "file-uri-to-path": "1.0.0" @@ -5604,7 +5599,8 @@ }, "bip66": { "version": "1.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -5612,17 +5608,20 @@ }, "bn.js": { "version": "4.11.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", "dev": true }, "brorand": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, "browserify-aes": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { "buffer-xor": "^1.0.3", @@ -5635,22 +5634,26 @@ }, "buffer-from": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "buffer-xor": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, "camelcase": { "version": "5.3.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "cipher-base": { "version": "1.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -5659,7 +5662,8 @@ }, "cliui": { "version": "5.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { "string-width": "^3.1.0", @@ -5669,7 +5673,8 @@ }, "color-convert": { "version": "1.9.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" @@ -5677,12 +5682,14 @@ }, "color-name": { "version": "1.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "create-hash": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { "cipher-base": "^1.0.1", @@ -5694,7 +5701,8 @@ }, "create-hmac": { "version": "1.1.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { "cipher-base": "^1.0.3", @@ -5707,7 +5715,8 @@ }, "cross-spawn": { "version": "6.0.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -5719,12 +5728,14 @@ }, "decamelize": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "drbg.js": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", "dev": true, "requires": { "browserify-aes": "^1.0.6", @@ -5734,7 +5745,8 @@ }, "elliptic": { "version": "6.5.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -5748,12 +5760,14 @@ }, "emoji-regex": { "version": "7.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "end-of-stream": { "version": "1.4.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -5761,7 +5775,8 @@ }, "ethereumjs-util": { "version": "6.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==", "dev": true, "requires": { "bn.js": "^4.11.0", @@ -5775,7 +5790,8 @@ }, "ethjs-util": { "version": "0.1.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", "dev": true, "requires": { "is-hex-prefixed": "1.0.0", @@ -5784,7 +5800,8 @@ }, "evp_bytestokey": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { "md5.js": "^1.3.4", @@ -5793,7 +5810,8 @@ }, "execa": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { "cross-spawn": "^6.0.0", @@ -5807,12 +5825,14 @@ }, "file-uri-to-path": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "dev": true }, "find-up": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { "locate-path": "^3.0.0" @@ -5820,12 +5840,14 @@ }, "get-caller-file": { "version": "2.0.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { "version": "4.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { "pump": "^3.0.0" @@ -5833,7 +5855,8 @@ }, "hash-base": { "version": "3.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -5842,7 +5865,8 @@ }, "hash.js": { "version": "1.1.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -5851,7 +5875,8 @@ }, "hmac-drbg": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { "hash.js": "^1.0.3", @@ -5861,37 +5886,44 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "invert-kv": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "is-hex-prefixed": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", "dev": true }, "is-stream": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "isexe": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "keccak": { "version": "1.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", "dev": true, "requires": { "bindings": "^1.2.1", @@ -5902,7 +5934,8 @@ }, "lcid": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { "invert-kv": "^2.0.0" @@ -5910,7 +5943,8 @@ }, "locate-path": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { "p-locate": "^3.0.0", @@ -5919,7 +5953,8 @@ }, "map-age-cleaner": { "version": "0.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "requires": { "p-defer": "^1.0.0" @@ -5927,7 +5962,8 @@ }, "md5.js": { "version": "1.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { "hash-base": "^3.0.0", @@ -5937,7 +5973,8 @@ }, "mem": { "version": "4.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", "dev": true, "requires": { "map-age-cleaner": "^0.1.1", @@ -5947,32 +5984,38 @@ }, "mimic-fn": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "minimalistic-assert": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", "dev": true }, "nan": { "version": "2.14.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true }, "nice-try": { "version": "1.0.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "npm-run-path": { "version": "2.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { "path-key": "^2.0.0" @@ -5980,7 +6023,8 @@ }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -5988,7 +6032,8 @@ }, "os-locale": { "version": "3.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { "execa": "^1.0.0", @@ -5998,22 +6043,26 @@ }, "p-defer": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", "dev": true }, "p-finally": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, "p-is-promise": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", "dev": true }, "p-limit": { "version": "2.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -6021,7 +6070,8 @@ }, "p-locate": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { "p-limit": "^2.0.0" @@ -6029,22 +6079,26 @@ }, "p-try": { "version": "2.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "path-key": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "pump": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -6053,17 +6107,20 @@ }, "require-directory": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "ripemd160": { "version": "2.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "requires": { "hash-base": "^3.0.0", @@ -6072,7 +6129,8 @@ }, "rlp": { "version": "2.2.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-l6YVrI7+d2vpW6D6rS05x2Xrmq8oW7v3pieZOJKBEdjuTF4Kz/iwk55Zyh1Zaz+KOB2kC8+2jZlp2u9L4tTzCQ==", "dev": true, "requires": { "bn.js": "^4.11.1", @@ -6081,12 +6139,14 @@ }, "safe-buffer": { "version": "5.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true }, "secp256k1": { "version": "3.7.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g==", "dev": true, "requires": { "bindings": "^1.5.0", @@ -6101,17 +6161,20 @@ }, "semver": { "version": "5.7.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "sha.js": { "version": "2.4.11", - "bundled": true, + "resolved": false, + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -6120,7 +6183,8 @@ }, "shebang-command": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -6128,22 +6192,26 @@ }, "shebang-regex": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "source-map": { "version": "0.6.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { "version": "0.5.12", - "bundled": true, + "resolved": false, + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -6152,7 +6220,8 @@ }, "string-width": { "version": "3.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -6162,7 +6231,8 @@ }, "strip-ansi": { "version": "5.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -6170,12 +6240,14 @@ }, "strip-eof": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, "strip-hex-prefix": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", "dev": true, "requires": { "is-hex-prefixed": "1.0.0" @@ -6183,7 +6255,8 @@ }, "which": { "version": "1.3.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -6191,12 +6264,14 @@ }, "which-module": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "wrap-ansi": { "version": "5.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -6206,17 +6281,20 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "y18n": { "version": "4.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, "yargs": { "version": "13.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -6234,7 +6312,8 @@ }, "yargs-parser": { "version": "13.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -14516,13 +14595,13 @@ "integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=", "dev": true, "requires": { - "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git", + "ethereumjs-abi": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git", "ethereumjs-util": "^5.1.1" } }, "ethereumjs-abi": { "version": "git+https://git@github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f", - "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", + "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git", "dev": true, "requires": { "bn.js": "^4.11.8", @@ -18812,8 +18891,8 @@ "dev": true }, "scrypt-shim": { - "version": "github:web3-js/scrypt-shim#aafdadda13e660e25e1c525d1f5b2443f5eb1ebb", - "from": "github:web3-js/scrypt-shim", + "version": "git+ssh://git@github.com/web3-js/scrypt-shim.git#aafdadda13e660e25e1c525d1f5b2443f5eb1ebb", + "from": "scrypt-shim@github:web3-js/scrypt-shim", "dev": true, "requires": { "scryptsy": "^2.1.0", @@ -21516,7 +21595,7 @@ "eth-lib": "0.2.7", "ethereumjs-common": "^1.3.2", "ethereumjs-tx": "^2.1.1", - "scrypt-shim": "github:web3-js/scrypt-shim", + "scrypt-shim": "scrypt-shim@github:web3-js/scrypt-shim", "underscore": "1.9.1", "uuid": "3.3.2", "web3-core": "1.2.2", @@ -21846,7 +21925,7 @@ "requires": { "underscore": "1.9.1", "web3-core-helpers": "1.2.2", - "websocket": "github:web3-js/WebSocket-Node#polyfill/globalThis" + "websocket": "websocket@github:web3-js/WebSocket-Node#polyfill/globalThis" } }, "web3-shh": { @@ -21878,8 +21957,8 @@ } }, "websocket": { - "version": "github:web3-js/WebSocket-Node#ef5ea2f41daf4a2113b80c9223df884b4d56c400", - "from": "github:web3-js/WebSocket-Node#polyfill/globalThis", + "version": "git+ssh://git@github.com/web3-js/WebSocket-Node.git#ef5ea2f41daf4a2113b80c9223df884b4d56c400", + "from": "websocket@github:web3-js/WebSocket-Node#polyfill/globalThis", "dev": true, "requires": { "debug": "^2.2.0", diff --git a/package.json b/package.json index 1f8d3043..4532f6fa 100644 --- a/package.json +++ b/package.json @@ -14,37 +14,48 @@ "hardhat:ropsten": "hardhat--network ropsten", "hardhat:main": "hardhat --network main", "hardhat:docker": "hardhat --network hardhatevm_docker", + "hardhat:mumbai": "hardhat --network mumbai", + "hardhat:matic": "hardhat --network matic", "compile": "SKIP_LOAD=true hardhat compile", "console:fork": "MAINNET_FORK=true hardhat console", - "test": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test/*.spec.ts", - "test-scenarios": "npx hardhat test test/__setup.spec.ts test/scenario.spec.ts", - "test-repay-with-collateral": "hardhat test test/__setup.spec.ts test/repay-with-collateral.spec.ts", - "test-liquidate-with-collateral": "hardhat test test/__setup.spec.ts test/flash-liquidation-with-collateral.spec.ts", - "test-liquidate-underlying": "hardhat test test/__setup.spec.ts test/liquidation-underlying.spec.ts", - "test-configurator": "hardhat test test/__setup.spec.ts test/configurator.spec.ts", - "test-transfers": "hardhat test test/__setup.spec.ts test/atoken-transfer.spec.ts", - "test-flash": "hardhat test test/__setup.spec.ts test/flashloan.spec.ts", - "test-liquidate": "hardhat test test/__setup.spec.ts test/liquidation-atoken.spec.ts", - "test-deploy": "hardhat test test/__setup.spec.ts test/test-init.spec.ts", - "test-pausable": "hardhat test test/__setup.spec.ts test/pausable-functions.spec.ts", - "test-permit": "hardhat test test/__setup.spec.ts test/atoken-permit.spec.ts", - "test-stable-and-atokens": "hardhat test test/__setup.spec.ts test/atoken-transfer.spec.ts test/stable-token.spec.ts", - "test-subgraph:scenarios": "hardhat --network hardhatevm_docker test test/__setup.spec.ts test/subgraph-scenarios.spec.ts", - "test-weth": "hardhat test test/__setup.spec.ts test/weth-gateway.spec.ts", - "test-uniswap": "hardhat test test/__setup.spec.ts test/uniswapAdapters*.spec.ts", - "test:main:check-list": "MAINNET_FORK=true TS_NODE_TRANSPILE_ONLY=1 hardhat test test/__setup.spec.ts test/mainnet/check-list.spec.ts", + "test": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-aave/*.spec.ts", + "test-amm": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-amm/*.spec.ts", + "test-amm-scenarios": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-amm/__setup.spec.ts test-suites/test-amm/scenario.spec.ts", + "test-scenarios": "npx hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/scenario.spec.ts", + "test-repay-with-collateral": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/repay-with-collateral.spec.ts", + "test-liquidate-with-collateral": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/flash-liquidation-with-collateral.spec.ts", + "test-liquidate-underlying": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/liquidation-underlying.spec.ts", + "test-configurator": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/configurator.spec.ts", + "test-transfers": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/atoken-transfer.spec.ts", + "test-flash": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/flashloan.spec.ts", + "test-liquidate": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/liquidation-atoken.spec.ts", + "test-deploy": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/test-init.spec.ts", + "test-pausable": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/pausable-functions.spec.ts", + "test-permit": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/atoken-permit.spec.ts", + "test-stable-and-atokens": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/atoken-transfer.spec.ts test-suites/test-aave/stable-token.spec.ts", + "test-subgraph:scenarios": "hardhat --network hardhatevm_docker test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/subgraph-scenarios.spec.ts", + "test-weth:main": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/weth-gateway.spec.ts", + "test-weth:amm": "hardhat test test-suites/test-amm/__setup.spec.ts test-suites/test-amm/weth-gateway.spec.ts", + "test-uniswap": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/uniswapAdapters*.spec.ts", + "test:main:check-list": "MAINNET_FORK=true TS_NODE_TRANSPILE_ONLY=1 hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/mainnet/check-list.spec.ts", "dev:coverage": "buidler compile --force && buidler coverage --network coverage", "aave:evm:dev:migration": "npm run compile && hardhat aave:dev", "aave:docker:full:migration": "npm run compile && npm run hardhat:docker -- aave:mainnet", "aave:kovan:full:migration": "npm run compile && npm run hardhat:kovan -- aave:mainnet --verify", + "matic:mumbai:full:migration": "npm run compile && npm run hardhat:mumbai matic:mainnet", + "matic:matic:full:migration": "npm run compile && npm run hardhat:matic matic:mainnet", + "amm:kovan:full:migration": "npm run compile && npm run hardhat:kovan -- amm:mainnet --verify", "aave:kovan:full:initialize": "npm run hardhat:kovan -- full:initialize-lending-pool --verify --pool Aave", "aave:ropsten:full:migration": "npm run compile && npm run hardhat:ropsten -- aave:mainnet --verify", "aave:fork:main:tenderly": "npm run compile && npm run hardhat:tenderly-main -- aave:mainnet", - "aave:fork:main": "npm run compile && MAINNET_FORK=true hardhat aave:mainnet", + "aave:fork:main": "npm run compile && MAINNET_FORK=true hardhat aave:mainnet", + "amm:fork:main": "npm run compile && MAINNET_FORK=true hardhat amm:mainnet", + "amm:fork:main:tenderly": "npm run compile && npm run hardhat:tenderly-main -- amm:mainnet", "aave:main:full:migration": "npm run compile && npm run hardhat:main -- aave:mainnet --verify", "aave:main:full:initialize": "npm run compile && MAINNET_FORK=true full:initialize-tokens --pool Aave", - "prettier:check": "npx prettier -c 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test/**/*.ts'", - "prettier:write": "prettier --write 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test/**/*.ts'", + "amm:main:full:migration": "npm run compile && npm run hardhat:main -- amm:mainnet --verify", + "prettier:check": "npx prettier -c 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test-suites/test-aave/**/*.ts'", + "prettier:write": "prettier --write 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test-suites/test-aave/**/*.ts'", "ci:test": "npm run compile && npm run test", "ci:clean": "rm -rf ./artifacts ./cache ./types", "print-contracts:kovan": "npm run hardhat:kovan -- print-contracts", @@ -78,7 +89,7 @@ "@nomiclabs/hardhat-ethers": "^2.0.0", "@nomiclabs/hardhat-waffle": "^2.0.0", "@openzeppelin/contracts": "3.1.0", - "@tenderly/hardhat-tenderly": "^1.1.0-beta.3", + "@tenderly/hardhat-tenderly": "^1.1.0-beta.4", "@typechain/ethers-v4": "1.0.0", "@typechain/ethers-v5": "^2.0.0", "@typechain/truffle-v4": "2.0.2", @@ -119,7 +130,7 @@ }, "husky": { "hooks": { - "pre-commit": "pretty-quick --staged --pattern 'contracts/**/*.sol' --pattern 'helpers/**/*.ts' --pattern 'test/**/*.ts' --pattern 'tasks/**/*.ts'" + "pre-commit": "pretty-quick --staged --pattern 'contracts/**/*.sol' --pattern 'helpers/**/*.ts' --pattern 'test-suites/test-aave/**/*.ts' --pattern 'tasks/**/*.ts'" } }, "author": "Aave", diff --git a/tasks/dev/4_oracles.ts b/tasks/dev/4_oracles.ts index dbb6e708..23c24133 100644 --- a/tasks/dev/4_oracles.ts +++ b/tasks/dev/4_oracles.ts @@ -4,7 +4,6 @@ import { deployAaveOracle, deployLendingRateOracle, } from '../../helpers/contracts-deployments'; - import { setInitialAssetPricesInOracle, deployAllMockAggregators, diff --git a/tasks/dev/5_initialize.ts b/tasks/dev/5_initialize.ts index 37744343..a1ad9970 100644 --- a/tasks/dev/5_initialize.ts +++ b/tasks/dev/5_initialize.ts @@ -5,7 +5,10 @@ import { deployWalletBalancerProvider, deployAaveProtocolDataProvider, deployWETHGateway, + authorizeWETHGateway, } from '../../helpers/contracts-deployments'; +import { getParamPerNetwork } from '../../helpers/contracts-helpers'; +import { eNetwork } from '../../helpers/types'; import { ConfigNames, getReservesConfigByPool, @@ -30,8 +33,15 @@ task('dev:initialize-lending-pool', 'Initialize lending pool configuration.') .addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`) .setAction(async ({ verify, pool }, localBRE) => { await localBRE.run('set-DRE'); + const network = localBRE.network.name; const poolConfig = loadPoolConfig(pool); - + const { + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, + WethGateway, + } = poolConfig; const mockTokens = await getAllMockedTokens(); const allTokenAddresses = getAllTokenAddresses(mockTokens); @@ -52,6 +62,10 @@ task('dev:initialize-lending-pool', 'Initialize lending pool configuration.') await initReservesByHelper( reservesParams, protoPoolReservesAddresses, + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, admin, treasuryAddress, ZERO_ADDRESS, @@ -78,6 +92,6 @@ task('dev:initialize-lending-pool', 'Initialize lending pool configuration.') await insertContractAddressInDb(eContractid.AaveProtocolDataProvider, testHelpers.address); const lendingPoolAddress = await addressesProvider.getLendingPool(); - const wethAddress = await getWethAddress(poolConfig); - await deployWETHGateway([wethAddress, lendingPoolAddress]); + const gateWay = await getParamPerNetwork(WethGateway, network); + await authorizeWETHGateway(gateWay, lendingPoolAddress); }); diff --git a/tasks/full/1_address_provider.ts b/tasks/full/1_address_provider.ts index eddc17f8..d9563ebc 100644 --- a/tasks/full/1_address_provider.ts +++ b/tasks/full/1_address_provider.ts @@ -11,15 +11,16 @@ import { getGenesisPoolAdmin, getEmergencyAdmin, } from '../../helpers/configuration'; -import { eEthereumNetwork } from '../../helpers/types'; +import { eNetwork } from '../../helpers/types'; import { getFirstSigner, getLendingPoolAddressesProviderRegistry, } from '../../helpers/contracts-getters'; import { formatEther, isAddress, parseEther } from 'ethers/lib/utils'; import { isZeroAddress } from 'ethereumjs-util'; -import { Signer } from 'ethers'; +import { Signer, BigNumber } from 'ethers'; import { parse } from 'path'; +//import BigNumber from 'bignumber.js'; task( 'full:deploy-address-provider', @@ -30,7 +31,7 @@ task( .setAction(async ({ verify, pool }, DRE) => { await DRE.run('set-DRE'); let signer: Signer; - const network = DRE.network.name; + const network = DRE.network.name; const poolConfig = loadPoolConfig(pool); const { ProviderId, MarketId } = poolConfig; @@ -63,13 +64,7 @@ task( const firstAccount = await getFirstSigner(); await firstAccount.sendTransaction({ value: parseEther('10'), to: providerRegistryOwner }); } else { - signer = await getFirstSigner(); - const deployerAddress = await signer.getAddress(); - if (providerRegistryOwner !== (await signer.getAddress())) { - throw Error( - `Current signer is not provider registry owner. \nCurrent deployer address: ${deployerAddress} \nExpected address: ${poolConfig.ProviderRegistryOwner}` - ); - } + signer = DRE.ethers.provider.getSigner(providerRegistryOwner); } // 1. Address Provider Registry instance const addressesProviderRegistry = ( @@ -81,6 +76,7 @@ task( // 2. Deploy address provider and set genesis manager const addressesProvider = await deployLendingPoolAddressesProvider(MarketId, verify); + // DISABLE SEC. 3 FOR GOVERNANCE USE! // 3. Set the provider at the Registry await waitForTx( await addressesProviderRegistry.registerAddressesProvider( diff --git a/tasks/full/2_lending_pool.ts b/tasks/full/2_lending_pool.ts index db0daabb..5f92b8eb 100644 --- a/tasks/full/2_lending_pool.ts +++ b/tasks/full/2_lending_pool.ts @@ -1,45 +1,63 @@ import { task } from 'hardhat/config'; -import { insertContractAddressInDb } from '../../helpers/contracts-helpers'; +import { getParamPerNetwork, insertContractAddressInDb } from '../../helpers/contracts-helpers'; import { deployATokensAndRatesHelper, deployLendingPool, deployLendingPoolConfigurator, deployStableAndVariableTokensHelper, } from '../../helpers/contracts-deployments'; -import { eContractid } from '../../helpers/types'; -import { waitForTx } from '../../helpers/misc-utils'; +import { eContractid, eNetwork } from '../../helpers/types'; +import { notFalsyOrZeroAddress, waitForTx } from '../../helpers/misc-utils'; import { getLendingPoolAddressesProvider, getLendingPool, getLendingPoolConfiguratorProxy, } from '../../helpers/contracts-getters'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { loadPoolConfig, ConfigNames } from '../../helpers/configuration'; task('full:deploy-lending-pool', 'Deploy lending pool for dev enviroment') .addFlag('verify', 'Verify contracts at Etherscan') - .setAction(async ({ verify }, DRE: HardhatRuntimeEnvironment) => { + .addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`) + .setAction(async ({ verify, pool }, DRE: HardhatRuntimeEnvironment) => { try { await DRE.run('set-DRE'); - + const network = DRE.network.name; + const poolConfig = loadPoolConfig(pool); const addressesProvider = await getLendingPoolAddressesProvider(); - // Deploy lending pool - const lendingPoolImpl = await deployLendingPool(verify); + const { LendingPool, LendingPoolConfigurator } = poolConfig; - // Set lending pool impl to address provider - await waitForTx(await addressesProvider.setLendingPoolImpl(lendingPoolImpl.address)); + // Reuse/deploy lending pool implementation + let lendingPoolImplAddress = getParamPerNetwork(LendingPool, network); + if (!notFalsyOrZeroAddress(lendingPoolImplAddress)) { + console.log('\tDeploying new lending pool implementation & libraries...'); + const lendingPoolImpl = await deployLendingPool(verify); + lendingPoolImplAddress = lendingPoolImpl.address; + } + console.log('\tSetting lending pool implementation with address:', lendingPoolImplAddress); + // Set lending pool impl to Address provider + await waitForTx(await addressesProvider.setLendingPoolImpl(lendingPoolImplAddress)); const address = await addressesProvider.getLendingPool(); const lendingPoolProxy = await getLendingPool(address); await insertContractAddressInDb(eContractid.LendingPool, lendingPoolProxy.address); - // Deploy lending pool configurator - const lendingPoolConfiguratorImpl = await deployLendingPoolConfigurator(verify); - + // Reuse/deploy lending pool configurator + let lendingPoolConfiguratorImplAddress = getParamPerNetwork(LendingPoolConfigurator, network); //await deployLendingPoolConfigurator(verify); + if (!notFalsyOrZeroAddress(lendingPoolConfiguratorImplAddress)) { + console.log('\tDeploying new configurator implementation...'); + const lendingPoolConfiguratorImpl = await deployLendingPoolConfigurator(verify); + lendingPoolConfiguratorImplAddress = lendingPoolConfiguratorImpl.address; + } + console.log( + '\tSetting lending pool configurator implementation with address:', + lendingPoolConfiguratorImplAddress + ); // Set lending pool conf impl to Address Provider await waitForTx( - await addressesProvider.setLendingPoolConfiguratorImpl(lendingPoolConfiguratorImpl.address) + await addressesProvider.setLendingPoolConfiguratorImpl(lendingPoolConfiguratorImplAddress) ); const lendingPoolConfiguratorProxy = await getLendingPoolConfiguratorProxy( diff --git a/tasks/full/3_oracles.ts b/tasks/full/3_oracles.ts index b064328f..e3f8f27f 100644 --- a/tasks/full/3_oracles.ts +++ b/tasks/full/3_oracles.ts @@ -2,7 +2,7 @@ import { task } from 'hardhat/config'; import { getParamPerNetwork } from '../../helpers/contracts-helpers'; import { deployAaveOracle, deployLendingRateOracle } from '../../helpers/contracts-deployments'; import { setInitialMarketRatesInRatesOracleByHelper } from '../../helpers/oracles-helpers'; -import { ICommonConfiguration, eEthereumNetwork, SymbolMap } from '../../helpers/types'; +import { ICommonConfiguration, eNetwork, SymbolMap } from '../../helpers/types'; import { waitForTx, notFalsyOrZeroAddress } from '../../helpers/misc-utils'; import { ConfigNames, @@ -17,6 +17,7 @@ import { getLendingRateOracle, getPairsTokenAggregator, } from '../../helpers/contracts-getters'; +import { AaveOracle } from '../../types'; task('full:deploy-oracles', 'Deploy oracles for dev enviroment') .addFlag('verify', 'Verify contracts at Etherscan') @@ -24,7 +25,7 @@ task('full:deploy-oracles', 'Deploy oracles for dev enviroment') .setAction(async ({ verify, pool }, DRE) => { try { await DRE.run('set-DRE'); - const network = DRE.network.name; + const network = DRE.network.name; const poolConfig = loadPoolConfig(pool); const { ProtocolGlobalParams: { UsdAddress }, @@ -47,26 +48,39 @@ task('full:deploy-oracles', 'Deploy oracles for dev enviroment') }; const [tokens, aggregators] = getPairsTokenAggregator(tokensToWatch, chainlinkAggregators); - const aaveOracle = notFalsyOrZeroAddress(aaveOracleAddress) - ? await getAaveOracle(aaveOracleAddress) - : await deployAaveOracle( - [tokens, aggregators, fallbackOracleAddress, await getWethAddress(poolConfig)], - verify - ); - const lendingRateOracle = notFalsyOrZeroAddress(lendingRateOracleAddress) + let aaveOracle: AaveOracle; + if (notFalsyOrZeroAddress(aaveOracleAddress)) { + aaveOracle = await await getAaveOracle(aaveOracleAddress); + const owner = await aaveOracle.owner(); + const signer = DRE.ethers.provider.getSigner(owner); + + aaveOracle = await (await getAaveOracle(aaveOracleAddress)).connect(signer); + await waitForTx(await aaveOracle.setAssetSources(tokens, aggregators)); + } else { + aaveOracle = await deployAaveOracle( + [tokens, aggregators, fallbackOracleAddress, await getWethAddress(poolConfig)], + verify + ); + } + + let lendingRateOracle = notFalsyOrZeroAddress(lendingRateOracleAddress) ? await getLendingRateOracle(lendingRateOracleAddress) : await deployLendingRateOracle(verify); const { USD, ...tokensAddressesWithoutUsd } = tokensToWatch; - if (!lendingRateOracleAddress) { - await setInitialMarketRatesInRatesOracleByHelper( - lendingRateOracles, - tokensAddressesWithoutUsd, - lendingRateOracle, - admin - ); - } - + lendingRateOracle = lendingRateOracle.connect( + DRE.ethers.provider.getSigner(await lendingRateOracle.owner()) + ); + // This must be done any time a new market is created I believe + //if (!lendingRateOracleAddress) { + await setInitialMarketRatesInRatesOracleByHelper( + lendingRateOracles, + tokensAddressesWithoutUsd, + lendingRateOracle, + admin + ); + //} + console.log('ORACLES: %s and %s', aaveOracle.address, lendingRateOracle.address); // Register the proxy price provider on the addressesProvider await waitForTx(await addressesProvider.setPriceOracle(aaveOracle.address)); await waitForTx(await addressesProvider.setLendingRateOracle(lendingRateOracle.address)); diff --git a/tasks/full/5-deploy-wethGateWay.ts b/tasks/full/5-deploy-wethGateWay.ts new file mode 100644 index 00000000..2774f107 --- /dev/null +++ b/tasks/full/5-deploy-wethGateWay.ts @@ -0,0 +1,32 @@ +import { task } from 'hardhat/config'; +import { AaveConfig } from '../../markets/aave/index'; +import { getParamPerNetwork } from '../../helpers/contracts-helpers'; +import { loadPoolConfig, ConfigNames, getWethAddress } from '../../helpers/configuration'; +import { deployWETHGateway } from '../../helpers/contracts-deployments'; +import { DRE } from '../../helpers/misc-utils'; +import { eNetwork } from '../../helpers/types'; + +const CONTRACT_NAME = 'WETHGateway'; + +task(`full-deploy-weth-gateway`, `Deploys the ${CONTRACT_NAME} contract`) + .addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`) + .addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`) + .setAction(async ({ verify, pool }, localBRE) => { + await localBRE.run('set-DRE'); + const network = localBRE.network.name; + const poolConfig = loadPoolConfig(pool); + const Weth = await getWethAddress(poolConfig); + const { WethGateway } = poolConfig; + + if (!localBRE.network.config.chainId) { + throw new Error('INVALID_CHAIN_ID'); + } + let gateWay = getParamPerNetwork(WethGateway, network); + if (gateWay === '') { + const wethGateWay = await deployWETHGateway([Weth], verify); + console.log(`${CONTRACT_NAME}.address`, wethGateWay.address); + console.log(`\tFinished ${CONTRACT_NAME} deployment`); + } else { + console.log(`Weth gateway already deployed. Address: ${gateWay}`); + } + }); diff --git a/tasks/full/5_initialize.ts b/tasks/full/6-initialize.ts similarity index 58% rename from tasks/full/5_initialize.ts rename to tasks/full/6-initialize.ts index af5484d9..f4384e09 100644 --- a/tasks/full/5_initialize.ts +++ b/tasks/full/6-initialize.ts @@ -3,8 +3,8 @@ import { getParamPerNetwork } from '../../helpers/contracts-helpers'; import { deployLendingPoolCollateralManager, deployWalletBalancerProvider, - deployAaveProtocolDataProvider, deployWETHGateway, + authorizeWETHGateway, } from '../../helpers/contracts-deployments'; import { loadPoolConfig, @@ -12,8 +12,9 @@ import { getWethAddress, getTreasuryAddress, } from '../../helpers/configuration'; -import { eEthereumNetwork, ICommonConfiguration } from '../../helpers/types'; -import { waitForTx } from '../../helpers/misc-utils'; +import { getWETHGateway } from '../../helpers/contracts-getters'; +import { eNetwork, ICommonConfiguration } from '../../helpers/types'; +import { notFalsyOrZeroAddress, waitForTx } from '../../helpers/misc-utils'; import { initReservesByHelper, configureReservesByHelper } from '../../helpers/init-helpers'; import { exit } from 'process'; import { @@ -28,9 +29,18 @@ task('full:initialize-lending-pool', 'Initialize lending pool configuration.') .setAction(async ({ verify, pool }, localBRE) => { try { await localBRE.run('set-DRE'); - const network = localBRE.network.name; + const network = localBRE.network.name; const poolConfig = loadPoolConfig(pool); - const { ReserveAssets, ReservesConfig } = poolConfig as ICommonConfiguration; + const { + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, + ReserveAssets, + ReservesConfig, + LendingPoolCollateralManager, + WethGateway, + } = poolConfig as ICommonConfiguration; const reserveAssets = await getParamPerNetwork(ReserveAssets, network); @@ -48,6 +58,10 @@ task('full:initialize-lending-pool', 'Initialize lending pool configuration.') await initReservesByHelper( ReservesConfig, reserveAssets, + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, admin, treasuryAddress, ZERO_ADDRESS, @@ -55,17 +69,33 @@ task('full:initialize-lending-pool', 'Initialize lending pool configuration.') ); await configureReservesByHelper(ReservesConfig, reserveAssets, testHelpers, admin); - const collateralManager = await deployLendingPoolCollateralManager(verify); + let collateralManagerAddress = await getParamPerNetwork( + LendingPoolCollateralManager, + network + ); + if (!notFalsyOrZeroAddress(collateralManagerAddress)) { + const collateralManager = await deployLendingPoolCollateralManager(verify); + collateralManagerAddress = collateralManager.address; + } + // Seems unnecessary to register the collateral manager in the JSON db + + console.log( + '\tSetting lending pool collateral manager implementation with address', + collateralManagerAddress + ); await waitForTx( - await addressesProvider.setLendingPoolCollateralManager(collateralManager.address) + await addressesProvider.setLendingPoolCollateralManager(collateralManagerAddress) ); await deployWalletBalancerProvider(verify); - const wethAddress = await getWethAddress(poolConfig); const lendingPoolAddress = await addressesProvider.getLendingPool(); - await deployWETHGateway([wethAddress, lendingPoolAddress]); + let gateWay = getParamPerNetwork(WethGateway, network); + if (!notFalsyOrZeroAddress(gateWay)) { + gateWay = (await getWETHGateway()).address; + } + await authorizeWETHGateway(gateWay, lendingPoolAddress); } catch (err) { console.error(err); exit(1); diff --git a/tasks/helpers/deploy-new-asset.ts b/tasks/helpers/deploy-new-asset.ts index 912ebff1..03d102c4 100644 --- a/tasks/helpers/deploy-new-asset.ts +++ b/tasks/helpers/deploy-new-asset.ts @@ -1,5 +1,5 @@ import { task } from 'hardhat/config'; -import { EthereumNetwork } from '../../helpers/types'; +import { eEthereumNetwork } from '../../helpers/types'; import { getTreasuryAddress } from '../../helpers/configuration'; import * as marketConfigs from '../../markets/aave'; import * as reserveConfigs from '../../markets/aave/reservesConfigs'; @@ -18,7 +18,7 @@ const LENDING_POOL_ADDRESS_PROVIDER = { kovan: '0x652B2937Efd0B5beA1c8d54293FC1289672AFC6b', }; -const isSymbolValid = (symbol: string, network: EthereumNetwork) => +const isSymbolValid = (symbol: string, network: eEthereumNetwork) => Object.keys(reserveConfigs).includes('strategy' + symbol) && marketConfigs.AaveConfig.ReserveAssets[network][symbol] && marketConfigs.AaveConfig.ReservesConfig[symbol] === reserveConfigs['strategy' + symbol]; @@ -28,7 +28,7 @@ task('external:deploy-new-asset', 'Deploy A token, Debt Tokens, Risk Parameters' .addFlag('verify', 'Verify contracts at Etherscan') .setAction(async ({ verify, symbol }, localBRE) => { const network = localBRE.network.name; - if (!isSymbolValid(symbol, network as EthereumNetwork)) { + if (!isSymbolValid(symbol, network as eEthereumNetwork)) { throw new Error( ` WRONG RESERVE ASSET SETUP: @@ -53,9 +53,9 @@ WRONG RESERVE ASSET SETUP: poolAddress, reserveAssetAddress, treasuryAddress, + ZERO_ADDRESS, // Incentives Controller `Aave interest bearing ${symbol}`, `a${symbol}`, - ZERO_ADDRESS, ], verify ); @@ -63,9 +63,9 @@ WRONG RESERVE ASSET SETUP: [ poolAddress, reserveAssetAddress, + ZERO_ADDRESS, // Incentives Controller `Aave stable debt bearing ${symbol}`, `stableDebt${symbol}`, - ZERO_ADDRESS, ], verify ); @@ -73,9 +73,9 @@ WRONG RESERVE ASSET SETUP: [ poolAddress, reserveAssetAddress, + ZERO_ADDRESS, // Incentives Controller `Aave variable debt bearing ${symbol}`, `variableDebt${symbol}`, - ZERO_ADDRESS, ], verify ); diff --git a/tasks/migrations/aave.mainnet.ts b/tasks/migrations/aave.mainnet.ts index 96bcbbb8..184f6e47 100644 --- a/tasks/migrations/aave.mainnet.ts +++ b/tasks/migrations/aave.mainnet.ts @@ -21,7 +21,7 @@ task('aave:mainnet', 'Deploy development enviroment') await DRE.run('full:deploy-address-provider', { pool: POOL_NAME }); console.log('2. Deploy lending pool'); - await DRE.run('full:deploy-lending-pool'); + await DRE.run('full:deploy-lending-pool', { pool: POOL_NAME }); console.log('3. Deploy oracles'); await DRE.run('full:deploy-oracles', { pool: POOL_NAME }); @@ -29,21 +29,27 @@ task('aave:mainnet', 'Deploy development enviroment') console.log('4. Deploy Data Provider'); await DRE.run('full:data-provider', { pool: POOL_NAME }); - console.log('5. Initialize lending pool'); + console.log('5. Deploy WETH Gateway'); + await DRE.run('full-deploy-weth-gateway', { pool: POOL_NAME }); + + console.log('6. Initialize lending pool'); await DRE.run('full:initialize-lending-pool', { pool: POOL_NAME }); if (verify) { printContracts(); - console.log('4. Veryfing contracts'); + console.log('7. Veryfing contracts'); await DRE.run('verify:general', { all: true, pool: POOL_NAME }); - console.log('5. Veryfing aTokens and debtTokens'); + console.log('8. Veryfing aTokens and debtTokens'); await DRE.run('verify:tokens', { pool: POOL_NAME }); } if (usingTenderly()) { const postDeployHead = DRE.tenderlyRPC.getHead(); - console.log('Tenderly UUID', postDeployHead); + const postDeployFork = DRE.tenderlyRPC.getFork(); + console.log('Tenderly Info'); + console.log('- Head', postDeployHead); + console.log('- Fork', postDeployFork); } console.log('\nFinished migrations'); printContracts(); diff --git a/tasks/migrations/amm.mainnet.ts b/tasks/migrations/amm.mainnet.ts new file mode 100644 index 00000000..e496fc82 --- /dev/null +++ b/tasks/migrations/amm.mainnet.ts @@ -0,0 +1,56 @@ +import { task } from 'hardhat/config'; +import { checkVerification } from '../../helpers/etherscan-verification'; +import { ConfigNames } from '../../helpers/configuration'; +import { printContracts } from '../../helpers/misc-utils'; +import { usingTenderly } from '../../helpers/tenderly-utils'; + +task('amm:mainnet', 'Deploy development enviroment') + .addFlag('verify', 'Verify contracts at Etherscan') + .setAction(async ({ verify }, DRE) => { + const POOL_NAME = ConfigNames.Amm; + await DRE.run('set-DRE'); + + // Prevent loss of gas verifying all the needed ENVs for Etherscan verification + if (verify) { + checkVerification(); + } + + console.log('Migration started\n'); + + console.log('1. Deploy address provider'); + await DRE.run('full:deploy-address-provider', { pool: POOL_NAME }); + + console.log('2. Deploy lending pool'); + await DRE.run('full:deploy-lending-pool', { pool: POOL_NAME }); + + console.log('3. Deploy oracles'); + await DRE.run('full:deploy-oracles', { pool: POOL_NAME }); + + console.log('4. Deploy Data Provider'); + await DRE.run('full:data-provider', { pool: POOL_NAME }); + + console.log('5. Deploy WETH Gateway'); + await DRE.run('full-deploy-weth-gateway', { pool: POOL_NAME }); + + console.log('6. Initialize lending pool'); + await DRE.run('full:initialize-lending-pool', { pool: POOL_NAME }); + + if (verify) { + printContracts(); + console.log('7. Veryfing contracts'); + await DRE.run('verify:general', { all: true, pool: POOL_NAME }); + + console.log('8. Veryfing aTokens and debtTokens'); + await DRE.run('verify:tokens', { pool: POOL_NAME }); + } + + if (usingTenderly()) { + const postDeployHead = DRE.tenderlyRPC.getHead(); + const postDeployFork = DRE.tenderlyRPC.getFork(); + console.log('Tenderly Info'); + console.log('- Head', postDeployHead); + console.log('- Fork', postDeployFork); + } + console.log('\nFinished migrations'); + printContracts(); + }); diff --git a/tasks/migrations/matic.mainnet.ts b/tasks/migrations/matic.mainnet.ts new file mode 100644 index 00000000..5c2f7f09 --- /dev/null +++ b/tasks/migrations/matic.mainnet.ts @@ -0,0 +1,53 @@ +import { task } from 'hardhat/config'; +import { checkVerification } from '../../helpers/etherscan-verification'; +import { ConfigNames } from '../../helpers/configuration'; +import { printContracts } from '../../helpers/misc-utils'; +import { usingTenderly } from '../../helpers/tenderly-utils'; + +task('matic:mainnet', 'Deploy development enviroment') + .addFlag('verify', 'Verify contracts at Etherscan') + .setAction(async ({ verify }, DRE) => { + const POOL_NAME = ConfigNames.Matic; + await DRE.run('set-DRE'); + + // Prevent loss of gas verifying all the needed ENVs for Etherscan verification + if (verify) { + checkVerification(); + } + + console.log('Migration started\n'); + + console.log('1. Deploy address provider'); + await DRE.run('full:deploy-address-provider', { pool: POOL_NAME }); + + console.log('2. Deploy lending pool'); + await DRE.run('full:deploy-lending-pool', { pool: POOL_NAME }); + + console.log('3. Deploy oracles'); + await DRE.run('full:deploy-oracles', { pool: POOL_NAME }); + + console.log('4. Deploy Data Provider'); + await DRE.run('full:data-provider', { pool: POOL_NAME }); + + console.log('5. Initialize lending pool'); + await DRE.run('full:initialize-lending-pool', { pool: POOL_NAME }); + + if (verify) { + printContracts(); + console.log('4. Veryfing contracts'); + await DRE.run('verify:general', { all: true, pool: POOL_NAME }); + + console.log('5. Veryfing aTokens and debtTokens'); + await DRE.run('verify:tokens', { pool: POOL_NAME }); + } + + if (usingTenderly()) { + const postDeployHead = DRE.tenderlyRPC.getHead(); + const postDeployFork = DRE.tenderlyRPC.getFork(); + console.log('Tenderly Info'); + console.log('- Head', postDeployHead); + console.log('- Fork', postDeployFork); + } + console.log('\nFinished migrations'); + printContracts(); + }); diff --git a/tasks/misc/initialize-tokens.ts b/tasks/misc/initialize-tokens.ts index 91822f84..342e6e1a 100644 --- a/tasks/misc/initialize-tokens.ts +++ b/tasks/misc/initialize-tokens.ts @@ -1,7 +1,7 @@ import { task } from 'hardhat/config'; import { getParamPerNetwork } from '../../helpers/contracts-helpers'; import { loadPoolConfig, ConfigNames, getTreasuryAddress } from '../../helpers/configuration'; -import { eEthereumNetwork, ICommonConfiguration } from '../../helpers/types'; +import { eEthereumNetwork, eNetwork, ICommonConfiguration } from '../../helpers/types'; import { waitForTx } from '../../helpers/misc-utils'; import { initTokenReservesByHelper } from '../../helpers/init-helpers'; import { exit } from 'process'; @@ -23,9 +23,7 @@ task('full:initialize-tokens', 'Initialize lending pool configuration.') await DRE.run('set-DRE'); let signer: Signer; const network = - process.env.MAINNET_FORK === 'true' - ? eEthereumNetwork.main - : DRE.network.name; + process.env.MAINNET_FORK === 'true' ? eEthereumNetwork.main : DRE.network.name; const poolConfig = loadPoolConfig(pool); const { ReserveAssets, ReservesConfig } = poolConfig as ICommonConfiguration; @@ -62,13 +60,7 @@ task('full:initialize-tokens', 'Initialize lending pool configuration.') const balance = await signer.getBalance(); console.log('signer balance', formatEther(balance)); } else { - signer = await getFirstSigner(); - const deployerAddress = await signer.getAddress(); - if (providerRegistryOwner !== (await signer.getAddress())) { - throw Error( - `Current signer is not provider registry owner. \nCurrent deployer address: ${deployerAddress} \nExpected address: ${poolConfig.ProviderRegistryOwner}` - ); - } + signer = DRE.ethers.provider.getSigner(providerRegistryOwner); } // Init unitilialized reserves diff --git a/tasks/misc/print-config.ts b/tasks/misc/print-config.ts index 6a7db42a..722d308c 100644 --- a/tasks/misc/print-config.ts +++ b/tasks/misc/print-config.ts @@ -7,7 +7,7 @@ import { } from '../../helpers/contracts-getters'; import { getParamPerNetwork } from '../../helpers/contracts-helpers'; import { DRE } from '../../helpers/misc-utils'; -import { eEthereumNetwork } from '../../helpers/types'; +import { eEthereumNetwork, eNetwork, ePolygonNetwork, eXDaiNetwork } from '../../helpers/types'; task('print-config', 'Inits the DRE, to have access to all the plugins') .addParam('dataProvider', 'Address of AaveProtocolDataProvider') @@ -17,7 +17,7 @@ task('print-config', 'Inits the DRE, to have access to all the plugins') const network = process.env.MAINNET_FORK === 'true' ? eEthereumNetwork.main - : (localBRE.network.name as eEthereumNetwork); + : (localBRE.network.name as eNetwork); const poolConfig = loadPoolConfig(pool); const providerRegistryAddress = getParamPerNetwork(poolConfig.ProviderRegistry, network); diff --git a/tasks/misc/set-bre.ts b/tasks/misc/set-bre.ts index e2aeba12..63721451 100644 --- a/tasks/misc/set-bre.ts +++ b/tasks/misc/set-bre.ts @@ -3,6 +3,8 @@ import { DRE, setDRE } from '../../helpers/misc-utils'; import { EthereumNetworkNames } from '../../helpers/types'; import { usingTenderly } from '../../helpers/tenderly-utils'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { getFirstSigner } from '../../helpers/contracts-getters'; +import { formatEther } from 'ethers/lib/utils'; task(`set-DRE`, `Inits the DRE, to have access to all the plugins' objects`).setAction( async (_, _DRE) => { @@ -14,10 +16,24 @@ task(`set-DRE`, `Inits the DRE, to have access to all the plugins' objects`).set process.env.TENDERLY === 'true' ) { console.log('- Setting up Tenderly provider'); - await _DRE.tenderlyRPC.initializeFork(); - console.log('- Initialized Tenderly fork'); + if (process.env.TENDERLY_FORK_ID && process.env.TENDERLY_HEAD_ID) { + console.log('- Connecting to a Tenderly Fork'); + _DRE.tenderlyRPC.setFork(process.env.TENDERLY_FORK_ID); + _DRE.tenderlyRPC.setHead(process.env.TENDERLY_HEAD_ID); + } else { + console.log('- Creating a new Tenderly Fork'); + await _DRE.tenderlyRPC.initializeFork(); + } const provider = new _DRE.ethers.providers.Web3Provider(_DRE.tenderlyRPC as any); _DRE.ethers.provider = provider; + console.log('- Initialized Tenderly fork:'); + console.log(' - Fork: ', _DRE.tenderlyRPC.getFork()); + console.log(' - Head: ', _DRE.tenderlyRPC.getHead()); + console.log(' - First account:', await (await _DRE.ethers.getSigners())[0].getAddress()); + console.log( + ' - Balance:', + formatEther(await (await _DRE.ethers.getSigners())[0].getBalance()) + ); } setDRE(_DRE); diff --git a/tasks/verifications/1_general.ts b/tasks/verifications/1_general.ts index 6e42e483..fef0e510 100644 --- a/tasks/verifications/1_general.ts +++ b/tasks/verifications/1_general.ts @@ -25,20 +25,24 @@ import { import { getParamPerNetwork } from '../../helpers/contracts-helpers'; import { verifyContract } from '../../helpers/etherscan-verification'; import { notFalsyOrZeroAddress } from '../../helpers/misc-utils'; -import { eEthereumNetwork, ICommonConfiguration } from '../../helpers/types'; +import { eNetwork, ICommonConfiguration } from '../../helpers/types'; -task('verify:general', 'Deploy oracles for dev enviroment') +task('verify:general', 'Verify contracts at Etherscan') .addFlag('all', 'Verify all contracts at Etherscan') .addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`) .setAction(async ({ all, pool }, localDRE) => { await localDRE.run('set-DRE'); - const network = localDRE.network.name as eEthereumNetwork; + const network = localDRE.network.name as eNetwork; const poolConfig = loadPoolConfig(pool); const { ReserveAssets, ReservesConfig, ProviderRegistry, MarketId, + LendingPoolCollateralManager, + LendingPoolConfigurator, + LendingPool, + WethGateway, } = poolConfig as ICommonConfiguration; const treasuryAddress = await getTreasuryAddress(poolConfig); @@ -47,17 +51,41 @@ task('verify:general', 'Deploy oracles for dev enviroment') const addressesProviderRegistry = notFalsyOrZeroAddress(registryAddress) ? await getLendingPoolAddressesProviderRegistry(registryAddress) : await getLendingPoolAddressesProviderRegistry(); - const lendingPoolProxy = await getLendingPool(); - const lendingPoolConfigurator = await getLendingPoolConfiguratorProxy(); - const lendingPoolCollateralManager = await getLendingPoolCollateralManager(); + const lendingPoolAddress = await addressesProvider.getLendingPool(); + const lendingPoolConfiguratorAddress = await addressesProvider.getLendingPoolConfigurator(); //getLendingPoolConfiguratorProxy(); + const lendingPoolCollateralManagerAddress = await addressesProvider.getLendingPoolCollateralManager(); if (all) { - const lendingPoolImpl = await getLendingPoolImpl(); - const lendingPoolConfiguratorImpl = await getLendingPoolConfiguratorImpl(); - const lendingPoolCollateralManagerImpl = await getLendingPoolCollateralManagerImpl(); + const lendingPoolImplAddress = getParamPerNetwork(LendingPool, network); + const lendingPoolImpl = notFalsyOrZeroAddress(lendingPoolImplAddress) + ? await getLendingPoolImpl(lendingPoolImplAddress) + : await getLendingPoolImpl(); + + const lendingPoolConfiguratorImplAddress = getParamPerNetwork( + LendingPoolConfigurator, + network + ); + const lendingPoolConfiguratorImpl = notFalsyOrZeroAddress(lendingPoolConfiguratorImplAddress) + ? await getLendingPoolConfiguratorImpl(lendingPoolConfiguratorImplAddress) + : await getLendingPoolConfiguratorImpl(); + + const lendingPoolCollateralManagerImplAddress = getParamPerNetwork( + LendingPoolCollateralManager, + network + ); + const lendingPoolCollateralManagerImpl = notFalsyOrZeroAddress( + lendingPoolCollateralManagerImplAddress + ) + ? await getLendingPoolCollateralManagerImpl(lendingPoolCollateralManagerImplAddress) + : await getLendingPoolCollateralManagerImpl(); + const dataProvider = await getAaveProtocolDataProvider(); const walletProvider = await getWalletProvider(); - const wethGateway = await getWETHGateway(); + + const wethGatewayAddress = getParamPerNetwork(WethGateway, network); + const wethGateway = notFalsyOrZeroAddress(wethGatewayAddress) + ? await getWETHGateway(wethGatewayAddress) + : await getWETHGateway(); // Address Provider console.log('\n- Verifying address provider...\n'); @@ -89,22 +117,19 @@ task('verify:general', 'Deploy oracles for dev enviroment') // WETHGateway console.log('\n- Verifying WETHGateway...\n'); - await verifyContract(wethGateway.address, [ - await getWethAddress(poolConfig), - lendingPoolProxy.address, - ]); + await verifyContract(wethGateway.address, [await getWethAddress(poolConfig)]); } // Lending Pool proxy console.log('\n- Verifying Lending Pool Proxy...\n'); - await verifyContract(lendingPoolProxy.address, [addressesProvider.address]); + await verifyContract(lendingPoolAddress, [addressesProvider.address]); // LendingPool Conf proxy console.log('\n- Verifying Lending Pool Configurator Proxy...\n'); - await verifyContract(lendingPoolConfigurator.address, [addressesProvider.address]); + await verifyContract(lendingPoolConfiguratorAddress, [addressesProvider.address]); // Proxy collateral manager console.log('\n- Verifying Lending Pool Collateral Manager Proxy...\n'); - await verifyContract(lendingPoolCollateralManager.address, []); + await verifyContract(lendingPoolCollateralManagerAddress, []); // DelegatedAwareAToken console.log('\n- Verifying DelegatedAwareAToken...\n'); @@ -113,7 +138,7 @@ task('verify:general', 'Deploy oracles for dev enviroment') if (aUNI) { console.log('Verifying aUNI'); await verifyContract(aUNI, [ - lendingPoolProxy.address, + lendingPoolAddress, UNI, treasuryAddress, 'Aave interest bearing UNI', diff --git a/tasks/verifications/2_tokens.ts b/tasks/verifications/2_tokens.ts index 591aa45a..52d77ffd 100644 --- a/tasks/verifications/2_tokens.ts +++ b/tasks/verifications/2_tokens.ts @@ -8,26 +8,35 @@ import { import { ZERO_ADDRESS } from '../../helpers/constants'; import { getAddressById, + getFirstSigner, getLendingPool, getLendingPoolAddressesProvider, getLendingPoolConfiguratorProxy, } from '../../helpers/contracts-getters'; import { getParamPerNetwork } from '../../helpers/contracts-helpers'; import { verifyContract } from '../../helpers/etherscan-verification'; -import { eEthereumNetwork, ICommonConfiguration, IReserveParams } from '../../helpers/types'; +import { eNetwork, ICommonConfiguration, IReserveParams } from '../../helpers/types'; +import { LendingPoolConfiguratorFactory, LendingPoolFactory } from '../../types'; task('verify:tokens', 'Deploy oracles for dev enviroment') .addParam('pool', `Pool name to retrieve configuration, supported: ${Object.values(ConfigNames)}`) .setAction(async ({ verify, all, pool }, localDRE) => { await localDRE.run('set-DRE'); - const network = localDRE.network.name as eEthereumNetwork; + const network = localDRE.network.name as eNetwork; const poolConfig = loadPoolConfig(pool); const { ReserveAssets, ReservesConfig } = poolConfig as ICommonConfiguration; const treasuryAddress = await getTreasuryAddress(poolConfig); const addressesProvider = await getLendingPoolAddressesProvider(); - const lendingPoolProxy = await getLendingPool(); - const lendingPoolConfigurator = await getLendingPoolConfiguratorProxy(); + const lendingPoolProxy = LendingPoolFactory.connect( + await addressesProvider.getLendingPool(), + await getFirstSigner() + ); + + const lendingPoolConfigurator = LendingPoolConfiguratorFactory.connect( + await addressesProvider.getLendingPoolConfigurator(), + await getFirstSigner() + ); const configs = Object.entries(ReservesConfig) as [string, IReserveParams][]; for (const entry of Object.entries(getParamPerNetwork(ReserveAssets, network))) { @@ -52,7 +61,7 @@ task('verify:tokens', 'Deploy oracles for dev enviroment') variableRateSlope2, stableRateSlope1, stableRateSlope2, - } = tokenConfig[1]; + } = tokenConfig[1].strategy; console.log; // Proxy Stable Debt diff --git a/test/__setup.spec.ts b/test-suites/test-aave/__setup.spec.ts similarity index 79% rename from test/__setup.spec.ts rename to test-suites/test-aave/__setup.spec.ts index 37ff9cfc..831b5231 100644 --- a/test/__setup.spec.ts +++ b/test-suites/test-aave/__setup.spec.ts @@ -4,7 +4,7 @@ import { insertContractAddressInDb, getEthersSigners, registerContractInJsonDb, -} from '../helpers/contracts-helpers'; +} from '../../helpers/contracts-helpers'; import { deployLendingPoolAddressesProvider, deployMintableERC20, @@ -26,33 +26,35 @@ import { deployUniswapLiquiditySwapAdapter, deployUniswapRepayAdapter, deployFlashLiquidationAdapter, -} from '../helpers/contracts-deployments'; + authorizeWETHGateway, +} 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'; +import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../../helpers/types'; +import { MintableERC20 } from '../../types/MintableERC20'; import { ConfigNames, getReservesConfigByPool, getTreasuryAddress, loadPoolConfig, -} from '../helpers/configuration'; +} from '../../helpers/configuration'; import { initializeMakeSuite } from './helpers/make-suite'; import { setInitialAssetPricesInOracle, deployAllMockAggregators, setInitialMarketRatesInRatesOracleByHelper, -} from '../helpers/oracles-helpers'; -import { DRE, waitForTx } from '../helpers/misc-utils'; -import { initReservesByHelper, configureReservesByHelper } from '../helpers/init-helpers'; -import AaveConfig from '../markets/aave'; -import { ZERO_ADDRESS } from '../helpers/constants'; +} from '../../helpers/oracles-helpers'; +import { DRE, waitForTx } from '../../helpers/misc-utils'; +import { initReservesByHelper, configureReservesByHelper } from '../../helpers/init-helpers'; +import AaveConfig from '../../markets/aave'; +import { ZERO_ADDRESS } from '../../helpers/constants'; import { getLendingPool, getLendingPoolConfiguratorProxy, getPairsTokenAggregator, -} from '../helpers/contracts-getters'; -import { WETH9Mocked } from '../types/WETH9Mocked'; +} from '../../helpers/contracts-getters'; +import { WETH9Mocked } from '../../types/WETH9Mocked'; const MOCK_USD_PRICE_IN_WEI = AaveConfig.ProtocolGlobalParams.MockUsdPriceInWei; const ALL_ASSETS_INITIAL_PRICES = AaveConfig.Mocks.AllAssetsInitialPrices; @@ -95,7 +97,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const aaveAdmin = await deployer.getAddress(); const mockTokens = await deployAllMockTokens(deployer); - + console.log('Deployed mocks'); const addressesProvider = await deployLendingPoolAddressesProvider(AaveConfig.MarketId); await waitForTx(await addressesProvider.setPoolAdmin(aaveAdmin)); @@ -165,13 +167,37 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { REN: mockTokens.REN.address, UNI: mockTokens.UNI.address, ENJ: mockTokens.ENJ.address, + // DAI: mockTokens.LpDAI.address, + // USDC: mockTokens.LpUSDC.address, + // USDT: mockTokens.LpUSDT.address, + // WBTC: mockTokens.LpWBTC.address, + // WETH: mockTokens.LpWETH.address, + UniDAIWETH: mockTokens.UniDAIWETH.address, + UniWBTCWETH: mockTokens.UniWBTCWETH.address, + UniAAVEWETH: mockTokens.UniAAVEWETH.address, + UniBATWETH: mockTokens.UniBATWETH.address, + UniDAIUSDC: mockTokens.UniDAIUSDC.address, + UniCRVWETH: mockTokens.UniCRVWETH.address, + UniLINKWETH: mockTokens.UniLINKWETH.address, + UniMKRWETH: mockTokens.UniMKRWETH.address, + UniRENWETH: mockTokens.UniRENWETH.address, + UniSNXWETH: mockTokens.UniSNXWETH.address, + UniUNIWETH: mockTokens.UniUNIWETH.address, + UniUSDCWETH: mockTokens.UniUSDCWETH.address, + UniWBTCUSDC: mockTokens.UniWBTCUSDC.address, + UniYFIWETH: mockTokens.UniYFIWETH.address, + BptWBTCWETH: mockTokens.BptWBTCWETH.address, + BptBALWETH: mockTokens.BptBALWETH.address, + WMATIC: mockTokens.WMATIC.address, USD: USD_ADDRESS, + STAKE: mockTokens.STAKE.address, + xSUSHI: mockTokens.xSUSHI.address }, fallbackOracle ); const mockAggregators = await deployAllMockAggregators(MOCK_CHAINLINK_AGGREGATORS_PRICES); - + console.log('Mock aggs deployed'); const allTokenAddresses = Object.entries(mockTokens).reduce( (accum: { [tokenSymbol: string]: tEthereumAddress }, [tokenSymbol, tokenContract]) => ({ ...accum, @@ -217,16 +243,27 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const config = loadPoolConfig(ConfigNames.Aave); + const { + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, + } = config; const treasuryAddress = await getTreasuryAddress(config); await initReservesByHelper( reservesParams, allReservesAddresses, + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, admin, treasuryAddress, ZERO_ADDRESS, false ); + await configureReservesByHelper(reservesParams, allReservesAddresses, testHelpers, admin); const collateralManager = await deployLendingPoolCollateralManager(); @@ -249,7 +286,8 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { await deployWalletBalancerProvider(); - await deployWETHGateway([mockTokens.WETH.address, lendingPoolAddress]); + const gateWay = await deployWETHGateway([mockTokens.WETH.address]); + await authorizeWETHGateway(gateWay.address, lendingPoolAddress); console.timeEnd('setup'); }; diff --git a/test/addresses-provider-registry.spec.ts b/test-suites/test-aave/addresses-provider-registry.spec.ts similarity index 97% rename from test/addresses-provider-registry.spec.ts rename to test-suites/test-aave/addresses-provider-registry.spec.ts index 5ded1413..77198278 100644 --- a/test/addresses-provider-registry.spec.ts +++ b/test-suites/test-aave/addresses-provider-registry.spec.ts @@ -1,6 +1,6 @@ import { TestEnv, makeSuite } from './helpers/make-suite'; -import { ZERO_ADDRESS } from '../helpers/constants'; -import { ProtocolErrors } from '../helpers/types'; +import { ZERO_ADDRESS } from '../../helpers/constants'; +import { ProtocolErrors } from '../../helpers/types'; const { expect } = require('chai'); diff --git a/test/atoken-modifiers.spec.ts b/test-suites/test-aave/atoken-modifiers.spec.ts similarity index 95% rename from test/atoken-modifiers.spec.ts rename to test-suites/test-aave/atoken-modifiers.spec.ts index d379210c..f1ae1199 100644 --- a/test/atoken-modifiers.spec.ts +++ b/test-suites/test-aave/atoken-modifiers.spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { makeSuite, TestEnv } from './helpers/make-suite'; -import { ProtocolErrors } from '../helpers/types'; +import { ProtocolErrors } from '../../helpers/types'; makeSuite('AToken: Modifiers', (testEnv: TestEnv) => { const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors; diff --git a/test/atoken-permit.spec.ts b/test-suites/test-aave/atoken-permit.spec.ts similarity index 90% rename from test/atoken-permit.spec.ts rename to test-suites/test-aave/atoken-permit.spec.ts index bb880fee..24e8b39c 100644 --- a/test/atoken-permit.spec.ts +++ b/test-suites/test-aave/atoken-permit.spec.ts @@ -1,11 +1,11 @@ -import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../helpers/constants'; -import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; -import { buildPermitParams, getSignatureFromTypedData } from '../helpers/contracts-helpers'; +import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../../helpers/constants'; +import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +import { buildPermitParams, getSignatureFromTypedData } from '../../helpers/contracts-helpers'; import { expect } from 'chai'; import { ethers } from 'ethers'; import { makeSuite, TestEnv } from './helpers/make-suite'; -import { DRE } from '../helpers/misc-utils'; -import { waitForTx } from '../helpers/misc-utils'; +import { DRE } from '../../helpers/misc-utils'; +import { waitForTx } from '../../helpers/misc-utils'; import { _TypedDataEncoder } from 'ethers/lib/utils'; const { parseEther } = ethers.utils; @@ -58,7 +58,7 @@ makeSuite('AToken: Permit', (testEnv: TestEnv) => { expiration.toFixed() ); - const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -103,7 +103,7 @@ makeSuite('AToken: Permit', (testEnv: TestEnv) => { permitAmount ); - const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -145,7 +145,7 @@ makeSuite('AToken: Permit', (testEnv: TestEnv) => { permitAmount ); - const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -191,7 +191,7 @@ makeSuite('AToken: Permit', (testEnv: TestEnv) => { permitAmount ); - const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -226,7 +226,7 @@ makeSuite('AToken: Permit', (testEnv: TestEnv) => { permitAmount ); - const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -261,7 +261,7 @@ makeSuite('AToken: Permit', (testEnv: TestEnv) => { permitAmount ); - const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -296,7 +296,7 @@ makeSuite('AToken: Permit', (testEnv: TestEnv) => { permitAmount ); - const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } diff --git a/test/atoken-transfer.spec.ts b/test-suites/test-aave/atoken-transfer.spec.ts similarity index 93% rename from test/atoken-transfer.spec.ts rename to test-suites/test-aave/atoken-transfer.spec.ts index 7414eea9..c6451280 100644 --- a/test/atoken-transfer.spec.ts +++ b/test-suites/test-aave/atoken-transfer.spec.ts @@ -1,10 +1,10 @@ -import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../helpers/constants'; -import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; +import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; import { expect } from 'chai'; import { ethers } from 'ethers'; -import { RateMode, ProtocolErrors } from '../helpers/types'; +import { RateMode, ProtocolErrors } from '../../helpers/types'; import { makeSuite, TestEnv } from './helpers/make-suite'; -import { CommonsConfig } from '../markets/aave/commons'; +import { CommonsConfig } from '../../markets/aave/commons'; const AAVE_REFERRAL = CommonsConfig.ProtocolGlobalParams.AaveReferral; diff --git a/test/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts similarity index 98% rename from test/configurator.spec.ts rename to test-suites/test-aave/configurator.spec.ts index 1d888db0..c94dcdf8 100644 --- a/test/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -1,8 +1,8 @@ import { TestEnv, makeSuite } from './helpers/make-suite'; -import { APPROVAL_AMOUNT_LENDING_POOL, RAY } from '../helpers/constants'; -import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; -import { ProtocolErrors } from '../helpers/types'; -import { strategyWETH } from '../markets/aave/reservesConfigs'; +import { APPROVAL_AMOUNT_LENDING_POOL, RAY } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { ProtocolErrors } from '../../helpers/types'; +import { strategyWETH } from '../../markets/aave/reservesConfigs'; const { expect } = require('chai'); diff --git a/test-suites/test-aave/delegation-aware-atoken.spec.ts b/test-suites/test-aave/delegation-aware-atoken.spec.ts new file mode 100644 index 00000000..7e938172 --- /dev/null +++ b/test-suites/test-aave/delegation-aware-atoken.spec.ts @@ -0,0 +1,71 @@ +import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../../helpers/constants'; +import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +import { buildPermitParams, getSignatureFromTypedData } from '../../helpers/contracts-helpers'; +import { expect } from 'chai'; +import { ethers } from 'ethers'; +import { ProtocolErrors } from '../../helpers/types'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { DRE } from '../../helpers/misc-utils'; +import { + ConfigNames, + getATokenDomainSeparatorPerNetwork, + getTreasuryAddress, + loadPoolConfig, +} from '../../helpers/configuration'; +import { waitForTx } from '../../helpers/misc-utils'; +import { + deployDelegationAwareAToken, + deployMintableDelegationERC20, +} from '../../helpers/contracts-deployments'; +import { DelegationAwareATokenFactory } from '../../types'; +import { DelegationAwareAToken } from '../../types/DelegationAwareAToken'; +import { MintableDelegationERC20 } from '../../types/MintableDelegationERC20'; +import AaveConfig from '../../markets/aave'; + +const { parseEther } = ethers.utils; + +makeSuite('AToken: underlying delegation', (testEnv: TestEnv) => { + const poolConfig = loadPoolConfig(ConfigNames.Commons); + let delegationAToken = {}; + let delegationERC20 = {}; + + it('Deploys a new MintableDelegationERC20 and a DelegationAwareAToken', async () => { + const { pool } = testEnv; + + delegationERC20 = await deployMintableDelegationERC20(['DEL', 'DEL', '18']); + + delegationAToken = await deployDelegationAwareAToken( + [ + pool.address, + delegationERC20.address, + await getTreasuryAddress(AaveConfig), + ZERO_ADDRESS, + 'aDEL', + 'aDEL', + ], + false + ); + + //await delegationAToken.initialize(pool.address, ZERO_ADDRESS, delegationERC20.address, ZERO_ADDRESS, '18', 'aDEL', 'aDEL'); + + console.log((await delegationAToken.decimals()).toString()); + }); + + it('Tries to delegate with the caller not being the Aave admin', async () => { + const { users } = testEnv; + + await expect( + delegationAToken.connect(users[1].signer).delegateUnderlyingTo(users[2].address) + ).to.be.revertedWith(ProtocolErrors.CALLER_NOT_POOL_ADMIN); + }); + + it('Tries to delegate to user 2', async () => { + const { users } = testEnv; + + await delegationAToken.delegateUnderlyingTo(users[2].address); + + const delegateeAddress = await delegationERC20.delegatee(); + + expect(delegateeAddress).to.be.equal(users[2].address); + }); +}); diff --git a/test/flashloan.spec.ts b/test-suites/test-aave/flashloan.spec.ts similarity index 96% rename from test/flashloan.spec.ts rename to test-suites/test-aave/flashloan.spec.ts index c6e5dcff..911c4adc 100644 --- a/test/flashloan.spec.ts +++ b/test-suites/test-aave/flashloan.spec.ts @@ -1,18 +1,18 @@ import BigNumber from 'bignumber.js'; import { TestEnv, makeSuite } from './helpers/make-suite'; -import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../helpers/constants'; -import { convertToCurrencyDecimals, getContract } from '../helpers/contracts-helpers'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../../helpers/constants'; +import { convertToCurrencyDecimals, getContract } from '../../helpers/contracts-helpers'; import { ethers } from 'ethers'; -import { MockFlashLoanReceiver } from '../types/MockFlashLoanReceiver'; -import { ProtocolErrors, eContractid } from '../helpers/types'; -import { VariableDebtToken } from '../types/VariableDebtToken'; -import { StableDebtToken } from '../types/StableDebtToken'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { ProtocolErrors, eContractid } from '../../helpers/types'; +import { VariableDebtToken } from '../../types/VariableDebtToken'; +import { StableDebtToken } from '../../types/StableDebtToken'; import { getMockFlashLoanReceiver, getStableDebtToken, getVariableDebtToken, -} from '../helpers/contracts-getters'; +} from '../../helpers/contracts-getters'; const { expect } = require('chai'); @@ -462,7 +462,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { const reserveData = await pool.getReserveData(weth.address); - const stableDebtToken = await getVariableDebtToken(reserveData.stableDebtTokenAddress); + const stableDebtToken = await getStableDebtToken(reserveData.stableDebtTokenAddress); // Deposited for onBehalfOf user already, delegate borrow allowance await stableDebtToken.connect(onBehalfOf.signer).approveDelegation(caller.address, flashAmount); diff --git a/test-suites/test-aave/helpers/actions.ts b/test-suites/test-aave/helpers/actions.ts new file mode 100644 index 00000000..3000576a --- /dev/null +++ b/test-suites/test-aave/helpers/actions.ts @@ -0,0 +1,770 @@ +import BigNumber from 'bignumber.js'; + +import { + calcExpectedReserveDataAfterBorrow, + calcExpectedReserveDataAfterDeposit, + calcExpectedReserveDataAfterRepay, + calcExpectedReserveDataAfterStableRateRebalance, + calcExpectedReserveDataAfterSwapRateMode, + calcExpectedReserveDataAfterWithdraw, + calcExpectedUserDataAfterBorrow, + calcExpectedUserDataAfterDeposit, + calcExpectedUserDataAfterRepay, + calcExpectedUserDataAfterSetUseAsCollateral, + calcExpectedUserDataAfterStableRateRebalance, + calcExpectedUserDataAfterSwapRateMode, + calcExpectedUserDataAfterWithdraw, +} from './utils/calculations'; +import { getReserveAddressFromSymbol, getReserveData, getUserData } from './utils/helpers'; + +import { convertToCurrencyDecimals } from '../../../helpers/contracts-helpers'; +import { + getAToken, + getMintableERC20, + getStableDebtToken, + getVariableDebtToken, +} from '../../../helpers/contracts-getters'; +import { MAX_UINT_AMOUNT, ONE_YEAR } from '../../../helpers/constants'; +import { SignerWithAddress, TestEnv } from './make-suite'; +import { advanceTimeAndBlock, DRE, timeLatest, waitForTx } from '../../../helpers/misc-utils'; + +import chai from 'chai'; +import { ReserveData, UserReserveData } from './utils/interfaces'; +import { ContractReceipt } from 'ethers'; +import { AToken } from '../../../types/AToken'; +import { RateMode, tEthereumAddress } from '../../../helpers/types'; + +const { expect } = chai; + +const almostEqualOrEqual = function ( + this: any, + expected: ReserveData | UserReserveData, + actual: ReserveData | UserReserveData +) { + const keys = Object.keys(actual); + + keys.forEach((key) => { + if ( + key === 'lastUpdateTimestamp' || + key === 'marketStableRate' || + key === 'symbol' || + key === 'aTokenAddress' || + key === 'decimals' || + key === 'totalStableDebtLastUpdated' + ) { + // skipping consistency check on accessory data + return; + } + + this.assert(actual[key] != undefined, `Property ${key} is undefined in the actual data`); + expect(expected[key] != undefined, `Property ${key} is undefined in the expected data`); + + if (expected[key] == null || actual[key] == null) { + console.log('Found a undefined value for Key ', key, ' value ', expected[key], actual[key]); + } + + if (actual[key] instanceof BigNumber) { + const actualValue = (actual[key]).decimalPlaces(0, BigNumber.ROUND_DOWN); + const expectedValue = (expected[key]).decimalPlaces(0, BigNumber.ROUND_DOWN); + + this.assert( + actualValue.eq(expectedValue) || + actualValue.plus(1).eq(expectedValue) || + actualValue.eq(expectedValue.plus(1)) || + actualValue.plus(2).eq(expectedValue) || + actualValue.eq(expectedValue.plus(2)) || + actualValue.plus(3).eq(expectedValue) || + actualValue.eq(expectedValue.plus(3)), + `expected #{act} to be almost equal or equal #{exp} for property ${key}`, + `expected #{act} to be almost equal or equal #{exp} for property ${key}`, + expectedValue.toFixed(0), + actualValue.toFixed(0) + ); + } else { + this.assert( + actual[key] !== null && + expected[key] !== null && + actual[key].toString() === expected[key].toString(), + `expected #{act} to be equal #{exp} for property ${key}`, + `expected #{act} to be equal #{exp} for property ${key}`, + expected[key], + actual[key] + ); + } + }); +}; + +chai.use(function (chai: any, utils: any) { + chai.Assertion.overwriteMethod('almostEqualOrEqual', function (original: any) { + return function (this: any, expected: ReserveData | UserReserveData) { + const actual = (expected as ReserveData) + ? this._obj + : this._obj; + + almostEqualOrEqual.apply(this, [expected, actual]); + }; + }); +}); + +interface ActionsConfig { + skipIntegrityCheck: boolean; +} + +export const configuration: ActionsConfig = {}; + +export const mint = async (reserveSymbol: string, amount: string, user: SignerWithAddress) => { + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const token = await getMintableERC20(reserve); + + await waitForTx( + await token.connect(user.signer).mint(await convertToCurrencyDecimals(reserve, amount)) + ); +}; + +export const approve = async (reserveSymbol: string, user: SignerWithAddress, testEnv: TestEnv) => { + const { pool } = testEnv; + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const token = await getMintableERC20(reserve); + + await waitForTx( + await token.connect(user.signer).approve(pool.address, '100000000000000000000000000000') + ); +}; + +export const deposit = async ( + reserveSymbol: string, + amount: string, + sender: SignerWithAddress, + onBehalfOf: tEthereumAddress, + sendValue: string, + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string +) => { + const { pool } = testEnv; + + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const amountToDeposit = await convertToCurrencyDecimals(reserve, amount); + + const txOptions: any = {}; + + const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData( + reserve, + onBehalfOf, + testEnv, + sender.address + ); + + if (sendValue) { + txOptions.value = await convertToCurrencyDecimals(reserve, sendValue); + } + + if (expectedResult === 'success') { + const txResult = await waitForTx( + await pool + .connect(sender.signer) + .deposit(reserve, amountToDeposit, onBehalfOf, '0', txOptions) + ); + + const { + reserveData: reserveDataAfter, + userData: userDataAfter, + timestamp, + } = await getContractsData(reserve, onBehalfOf, testEnv, sender.address); + + const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult); + + const expectedReserveData = calcExpectedReserveDataAfterDeposit( + amountToDeposit.toString(), + reserveDataBefore, + txTimestamp + ); + + const expectedUserReserveData = calcExpectedUserDataAfterDeposit( + amountToDeposit.toString(), + reserveDataBefore, + expectedReserveData, + userDataBefore, + txTimestamp, + timestamp, + txCost + ); + + expectEqual(reserveDataAfter, expectedReserveData); + expectEqual(userDataAfter, expectedUserReserveData); + + // truffleAssert.eventEmitted(txResult, "Deposit", (ev: any) => { + // const {_reserve, _user, _amount} = ev; + // return ( + // _reserve === reserve && + // _user === user && + // new BigNumber(_amount).isEqualTo(new BigNumber(amountToDeposit)) + // ); + // }); + } else if (expectedResult === 'revert') { + await expect( + pool.connect(sender.signer).deposit(reserve, amountToDeposit, onBehalfOf, '0', txOptions), + revertMessage + ).to.be.reverted; + } +}; + +export const withdraw = async ( + reserveSymbol: string, + amount: string, + user: SignerWithAddress, + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string +) => { + const { pool } = testEnv; + + const { + aTokenInstance, + reserve, + userData: userDataBefore, + reserveData: reserveDataBefore, + } = await getDataBeforeAction(reserveSymbol, user.address, testEnv); + + let amountToWithdraw = '0'; + + if (amount !== '-1') { + amountToWithdraw = (await convertToCurrencyDecimals(reserve, amount)).toString(); + } else { + amountToWithdraw = MAX_UINT_AMOUNT; + } + + if (expectedResult === 'success') { + const txResult = await waitForTx( + await pool.connect(user.signer).withdraw(reserve, amountToWithdraw, user.address) + ); + + const { + reserveData: reserveDataAfter, + userData: userDataAfter, + timestamp, + } = await getContractsData(reserve, user.address, testEnv); + + const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult); + + const expectedReserveData = calcExpectedReserveDataAfterWithdraw( + amountToWithdraw, + reserveDataBefore, + userDataBefore, + txTimestamp + ); + + const expectedUserData = calcExpectedUserDataAfterWithdraw( + amountToWithdraw, + reserveDataBefore, + expectedReserveData, + userDataBefore, + txTimestamp, + timestamp, + txCost + ); + + expectEqual(reserveDataAfter, expectedReserveData); + expectEqual(userDataAfter, expectedUserData); + + // truffleAssert.eventEmitted(txResult, "Redeem", (ev: any) => { + // const {_from, _value} = ev; + // return ( + // _from === user && new BigNumber(_value).isEqualTo(actualAmountRedeemed) + // ); + // }); + } else if (expectedResult === 'revert') { + await expect( + pool.connect(user.signer).withdraw(reserve, amountToWithdraw, user.address), + revertMessage + ).to.be.reverted; + } +}; + +export const delegateBorrowAllowance = async ( + reserve: string, + amount: string, + interestRateMode: string, + user: SignerWithAddress, + receiver: tEthereumAddress, + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string +) => { + const { pool } = testEnv; + + const reserveAddress: tEthereumAddress = await getReserveAddressFromSymbol(reserve); + + const amountToDelegate: string = await ( + await convertToCurrencyDecimals(reserveAddress, amount) + ).toString(); + + const reserveData = await pool.getReserveData(reserveAddress); + + const debtToken = + interestRateMode === '1' + ? await getStableDebtToken(reserveData.stableDebtTokenAddress) + : await getVariableDebtToken(reserveData.variableDebtTokenAddress); + + const delegateAllowancePromise = debtToken + .connect(user.signer) + .approveDelegation(receiver, amountToDelegate); + + if (expectedResult === 'revert' && revertMessage) { + await expect(delegateAllowancePromise, revertMessage).to.be.revertedWith(revertMessage); + return; + } else { + await waitForTx(await delegateAllowancePromise); + const allowance = await debtToken.borrowAllowance(user.address, receiver); + expect(allowance.toString()).to.be.equal( + amountToDelegate, + 'borrowAllowance is set incorrectly' + ); + } +}; + +export const borrow = async ( + reserveSymbol: string, + amount: string, + interestRateMode: string, + user: SignerWithAddress, + onBehalfOf: tEthereumAddress, + timeTravel: string, + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string +) => { + const { pool } = testEnv; + + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData( + reserve, + onBehalfOf, + testEnv, + user.address + ); + + const amountToBorrow = await convertToCurrencyDecimals(reserve, amount); + + if (expectedResult === 'success') { + const txResult = await waitForTx( + await pool + .connect(user.signer) + .borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf) + ); + + const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult); + + if (timeTravel) { + const secondsToTravel = new BigNumber(timeTravel).multipliedBy(ONE_YEAR).div(365).toNumber(); + + await advanceTimeAndBlock(secondsToTravel); + } + + const { + reserveData: reserveDataAfter, + userData: userDataAfter, + timestamp, + } = await getContractsData(reserve, onBehalfOf, testEnv, user.address); + + const expectedReserveData = calcExpectedReserveDataAfterBorrow( + amountToBorrow.toString(), + interestRateMode, + reserveDataBefore, + userDataBefore, + txTimestamp, + timestamp + ); + + const expectedUserData = calcExpectedUserDataAfterBorrow( + amountToBorrow.toString(), + interestRateMode, + reserveDataBefore, + expectedReserveData, + userDataBefore, + txTimestamp, + timestamp + ); + + expectEqual(reserveDataAfter, expectedReserveData); + expectEqual(userDataAfter, expectedUserData); + + // truffleAssert.eventEmitted(txResult, "Borrow", (ev: any) => { + // const { + // _reserve, + // _user, + // _amount, + // _borrowRateMode, + // _borrowRate, + // _originationFee, + // } = ev; + // return ( + // _reserve.toLowerCase() === reserve.toLowerCase() && + // _user.toLowerCase() === user.toLowerCase() && + // new BigNumber(_amount).eq(amountToBorrow) && + // new BigNumber(_borrowRateMode).eq(expectedUserData.borrowRateMode) && + // new BigNumber(_borrowRate).eq(expectedUserData.borrowRate) && + // new BigNumber(_originationFee).eq( + // expectedUserData.originationFee.minus(userDataBefore.originationFee) + // ) + // ); + // }); + } else if (expectedResult === 'revert') { + await expect( + pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf), + revertMessage + ).to.be.reverted; + } +}; + +export const repay = async ( + reserveSymbol: string, + amount: string, + rateMode: string, + user: SignerWithAddress, + onBehalfOf: SignerWithAddress, + sendValue: string, + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string +) => { + const { pool } = testEnv; + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData( + reserve, + onBehalfOf.address, + testEnv + ); + + let amountToRepay = '0'; + + if (amount !== '-1') { + amountToRepay = (await convertToCurrencyDecimals(reserve, amount)).toString(); + } else { + amountToRepay = MAX_UINT_AMOUNT; + } + amountToRepay = '0x' + new BigNumber(amountToRepay).toString(16); + + const txOptions: any = {}; + + if (sendValue) { + const valueToSend = await convertToCurrencyDecimals(reserve, sendValue); + txOptions.value = '0x' + new BigNumber(valueToSend.toString()).toString(16); + } + + if (expectedResult === 'success') { + const txResult = await waitForTx( + await pool + .connect(user.signer) + .repay(reserve, amountToRepay, rateMode, onBehalfOf.address, txOptions) + ); + + const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult); + + const { + reserveData: reserveDataAfter, + userData: userDataAfter, + timestamp, + } = await getContractsData(reserve, onBehalfOf.address, testEnv); + + const expectedReserveData = calcExpectedReserveDataAfterRepay( + amountToRepay, + rateMode, + reserveDataBefore, + userDataBefore, + txTimestamp, + timestamp + ); + + const expectedUserData = calcExpectedUserDataAfterRepay( + amountToRepay, + rateMode, + reserveDataBefore, + expectedReserveData, + userDataBefore, + user.address, + onBehalfOf.address, + txTimestamp, + timestamp + ); + + expectEqual(reserveDataAfter, expectedReserveData); + expectEqual(userDataAfter, expectedUserData); + + // truffleAssert.eventEmitted(txResult, "Repay", (ev: any) => { + // const {_reserve, _user, _repayer} = ev; + + // return ( + // _reserve.toLowerCase() === reserve.toLowerCase() && + // _user.toLowerCase() === onBehalfOf.toLowerCase() && + // _repayer.toLowerCase() === user.toLowerCase() + // ); + // }); + } else if (expectedResult === 'revert') { + await expect( + pool + .connect(user.signer) + .repay(reserve, amountToRepay, rateMode, onBehalfOf.address, txOptions), + revertMessage + ).to.be.reverted; + } +}; + +export const setUseAsCollateral = async ( + reserveSymbol: string, + user: SignerWithAddress, + useAsCollateral: string, + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string +) => { + const { pool } = testEnv; + + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData( + reserve, + user.address, + testEnv + ); + + const useAsCollateralBool = useAsCollateral.toLowerCase() === 'true'; + + if (expectedResult === 'success') { + const txResult = await waitForTx( + await pool.connect(user.signer).setUserUseReserveAsCollateral(reserve, useAsCollateralBool) + ); + + const { txCost } = await getTxCostAndTimestamp(txResult); + + const { userData: userDataAfter } = await getContractsData(reserve, user.address, testEnv); + + const expectedUserData = calcExpectedUserDataAfterSetUseAsCollateral( + useAsCollateral.toLocaleLowerCase() === 'true', + reserveDataBefore, + userDataBefore, + txCost + ); + + expectEqual(userDataAfter, expectedUserData); + // if (useAsCollateralBool) { + // truffleAssert.eventEmitted(txResult, 'ReserveUsedAsCollateralEnabled', (ev: any) => { + // const {_reserve, _user} = ev; + // return _reserve === reserve && _user === user; + // }); + // } else { + // truffleAssert.eventEmitted(txResult, 'ReserveUsedAsCollateralDisabled', (ev: any) => { + // const {_reserve, _user} = ev; + // return _reserve === reserve && _user === user; + // }); + // } + } else if (expectedResult === 'revert') { + await expect( + pool.connect(user.signer).setUserUseReserveAsCollateral(reserve, useAsCollateralBool), + revertMessage + ).to.be.reverted; + } +}; + +export const swapBorrowRateMode = async ( + reserveSymbol: string, + user: SignerWithAddress, + rateMode: string, + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string +) => { + const { pool } = testEnv; + + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData( + reserve, + user.address, + testEnv + ); + + if (expectedResult === 'success') { + const txResult = await waitForTx( + await pool.connect(user.signer).swapBorrowRateMode(reserve, rateMode) + ); + + const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult); + + const { reserveData: reserveDataAfter, userData: userDataAfter } = await getContractsData( + reserve, + user.address, + testEnv + ); + + const expectedReserveData = calcExpectedReserveDataAfterSwapRateMode( + reserveDataBefore, + userDataBefore, + rateMode, + txTimestamp + ); + + const expectedUserData = calcExpectedUserDataAfterSwapRateMode( + reserveDataBefore, + expectedReserveData, + userDataBefore, + rateMode, + txCost, + txTimestamp + ); + + expectEqual(reserveDataAfter, expectedReserveData); + expectEqual(userDataAfter, expectedUserData); + + // truffleAssert.eventEmitted(txResult, "Swap", (ev: any) => { + // const {_user, _reserve, _newRateMode, _newRate} = ev; + // return ( + // _user === user && + // _reserve == reserve && + // new BigNumber(_newRateMode).eq(expectedUserData.borrowRateMode) && + // new BigNumber(_newRate).eq(expectedUserData.borrowRate) + // ); + // }); + } else if (expectedResult === 'revert') { + await expect(pool.connect(user.signer).swapBorrowRateMode(reserve, rateMode), revertMessage).to + .be.reverted; + } +}; + +export const rebalanceStableBorrowRate = async ( + reserveSymbol: string, + user: SignerWithAddress, + target: SignerWithAddress, + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string +) => { + const { pool } = testEnv; + + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData( + reserve, + target.address, + testEnv + ); + + if (expectedResult === 'success') { + const txResult = await waitForTx( + await pool.connect(user.signer).rebalanceStableBorrowRate(reserve, target.address) + ); + + const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult); + + const { reserveData: reserveDataAfter, userData: userDataAfter } = await getContractsData( + reserve, + target.address, + testEnv + ); + + const expectedReserveData = calcExpectedReserveDataAfterStableRateRebalance( + reserveDataBefore, + userDataBefore, + txTimestamp + ); + + const expectedUserData = calcExpectedUserDataAfterStableRateRebalance( + reserveDataBefore, + expectedReserveData, + userDataBefore, + txCost, + txTimestamp + ); + + expectEqual(reserveDataAfter, expectedReserveData); + expectEqual(userDataAfter, expectedUserData); + + // truffleAssert.eventEmitted(txResult, 'RebalanceStableBorrowRate', (ev: any) => { + // const {_user, _reserve, _newStableRate} = ev; + // return ( + // _user.toLowerCase() === target.toLowerCase() && + // _reserve.toLowerCase() === reserve.toLowerCase() && + // new BigNumber(_newStableRate).eq(expectedUserData.borrowRate) + // ); + // }); + } else if (expectedResult === 'revert') { + await expect( + pool.connect(user.signer).rebalanceStableBorrowRate(reserve, target.address), + revertMessage + ).to.be.reverted; + } +}; + +const expectEqual = ( + actual: UserReserveData | ReserveData, + expected: UserReserveData | ReserveData +) => { + if (!configuration.skipIntegrityCheck) { + // @ts-ignore + expect(actual).to.be.almostEqualOrEqual(expected); + } +}; + +interface ActionData { + reserve: string; + reserveData: ReserveData; + userData: UserReserveData; + aTokenInstance: AToken; +} + +const getDataBeforeAction = async ( + reserveSymbol: string, + user: tEthereumAddress, + testEnv: TestEnv +): Promise => { + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + + const { reserveData, userData } = await getContractsData(reserve, user, testEnv); + const aTokenInstance = await getAToken(reserveData.aTokenAddress); + return { + reserve, + reserveData, + userData, + aTokenInstance, + }; +}; + +export const getTxCostAndTimestamp = async (tx: ContractReceipt) => { + if (!tx.blockNumber || !tx.transactionHash || !tx.cumulativeGasUsed) { + throw new Error('No tx blocknumber'); + } + const txTimestamp = new BigNumber((await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp); + + const txInfo = await DRE.ethers.provider.getTransaction(tx.transactionHash); + const txCost = new BigNumber(tx.cumulativeGasUsed.toString()).multipliedBy( + txInfo.gasPrice.toString() + ); + + return { txCost, txTimestamp }; +}; + +export const getContractsData = async ( + reserve: string, + user: string, + testEnv: TestEnv, + sender?: string +) => { + const { pool, helpersContract } = testEnv; + + const [userData, reserveData, timestamp] = await Promise.all([ + getUserData(pool, helpersContract, reserve, user, sender || user), + getReserveData(helpersContract, reserve), + timeLatest(), + ]); + + return { + reserveData, + userData, + timestamp: new BigNumber(timestamp), + }; +}; diff --git a/test/helpers/almost-equal.ts b/test-suites/test-aave/helpers/almost-equal.ts similarity index 100% rename from test/helpers/almost-equal.ts rename to test-suites/test-aave/helpers/almost-equal.ts diff --git a/test/helpers/make-suite.ts b/test-suites/test-aave/helpers/make-suite.ts similarity index 79% rename from test/helpers/make-suite.ts rename to test-suites/test-aave/helpers/make-suite.ts index 4a75e54d..e56358be 100644 --- a/test/helpers/make-suite.ts +++ b/test-suites/test-aave/helpers/make-suite.ts @@ -1,4 +1,4 @@ -import { evmRevert, evmSnapshot, DRE } from '../../helpers/misc-utils'; +import { evmRevert, evmSnapshot, DRE } from '../../../helpers/misc-utils'; import { Signer } from 'ethers'; import { getLendingPool, @@ -14,32 +14,32 @@ import { getUniswapLiquiditySwapAdapter, getUniswapRepayAdapter, getFlashLiquidationAdapter, -} from '../../helpers/contracts-getters'; -import { eEthereumNetwork, tEthereumAddress } from '../../helpers/types'; -import { LendingPool } from '../../types/LendingPool'; -import { AaveProtocolDataProvider } from '../../types/AaveProtocolDataProvider'; -import { MintableERC20 } from '../../types/MintableERC20'; -import { AToken } from '../../types/AToken'; -import { LendingPoolConfigurator } from '../../types/LendingPoolConfigurator'; +} from '../../../helpers/contracts-getters'; +import { eEthereumNetwork, tEthereumAddress } from '../../../helpers/types'; +import { LendingPool } from '../../../types/LendingPool'; +import { AaveProtocolDataProvider } from '../../../types/AaveProtocolDataProvider'; +import { MintableERC20 } from '../../../types/MintableERC20'; +import { AToken } from '../../../types/AToken'; +import { LendingPoolConfigurator } from '../../../types/LendingPoolConfigurator'; import chai from 'chai'; // @ts-ignore import bignumberChai from 'chai-bignumber'; import { almostEqual } from './almost-equal'; -import { PriceOracle } from '../../types/PriceOracle'; -import { LendingPoolAddressesProvider } from '../../types/LendingPoolAddressesProvider'; -import { LendingPoolAddressesProviderRegistry } from '../../types/LendingPoolAddressesProviderRegistry'; -import { getEthersSigners } from '../../helpers/contracts-helpers'; -import { UniswapLiquiditySwapAdapter } from '../../types/UniswapLiquiditySwapAdapter'; -import { UniswapRepayAdapter } from '../../types/UniswapRepayAdapter'; -import { getParamPerNetwork } from '../../helpers/contracts-helpers'; -import { WETH9Mocked } from '../../types/WETH9Mocked'; -import { WETHGateway } from '../../types/WETHGateway'; +import { PriceOracle } from '../../../types/PriceOracle'; +import { LendingPoolAddressesProvider } from '../../../types/LendingPoolAddressesProvider'; +import { LendingPoolAddressesProviderRegistry } from '../../../types/LendingPoolAddressesProviderRegistry'; +import { getEthersSigners } from '../../../helpers/contracts-helpers'; +import { UniswapLiquiditySwapAdapter } from '../../../types/UniswapLiquiditySwapAdapter'; +import { UniswapRepayAdapter } from '../../../types/UniswapRepayAdapter'; +import { getParamPerNetwork } from '../../../helpers/contracts-helpers'; +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 { AaveConfig } from '../../../markets/aave'; +import { FlashLiquidationAdapter } from '../../../types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; -import { usingTenderly } from '../../helpers/tenderly-utils'; +import { usingTenderly } from '../../../helpers/tenderly-utils'; chai.use(bignumberChai()); chai.use(almostEqual()); diff --git a/test/helpers/scenario-engine.ts b/test-suites/test-aave/helpers/scenario-engine.ts similarity index 99% rename from test/helpers/scenario-engine.ts rename to test-suites/test-aave/helpers/scenario-engine.ts index 92c686f4..492fa8b2 100644 --- a/test/helpers/scenario-engine.ts +++ b/test-suites/test-aave/helpers/scenario-engine.ts @@ -11,7 +11,7 @@ import { rebalanceStableBorrowRate, delegateBorrowAllowance, } from './actions'; -import { RateMode } from '../../helpers/types'; +import { RateMode } from '../../../helpers/types'; export interface Action { name: string; diff --git a/test/helpers/scenarios/borrow-negatives.json b/test-suites/test-aave/helpers/scenarios/borrow-negatives.json similarity index 99% rename from test/helpers/scenarios/borrow-negatives.json rename to test-suites/test-aave/helpers/scenarios/borrow-negatives.json index 6a8ceb66..03f4d005 100644 --- a/test/helpers/scenarios/borrow-negatives.json +++ b/test-suites/test-aave/helpers/scenarios/borrow-negatives.json @@ -52,7 +52,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "1" }, @@ -121,7 +120,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "1" }, diff --git a/test/helpers/scenarios/borrow-repay-stable.json b/test-suites/test-aave/helpers/scenarios/borrow-repay-stable.json similarity index 99% rename from test/helpers/scenarios/borrow-repay-stable.json rename to test-suites/test-aave/helpers/scenarios/borrow-repay-stable.json index 3f472387..bdbf1aff 100644 --- a/test/helpers/scenarios/borrow-repay-stable.json +++ b/test-suites/test-aave/helpers/scenarios/borrow-repay-stable.json @@ -88,7 +88,7 @@ ] }, { - "description": "User 1 repays the half of the DAI borrow after one year", + "description": "User 1 repays half of the DAI borrow after one year", "actions": [ { "name": "mint", @@ -170,7 +170,7 @@ ] }, { - "description": "User 1 deposits 1000 DAI, user 2 tries to borrow 1000 DAI at a stable rate without any collateral (revert expected)", + "description": "User 1 deposits 1000 DAI, user 2 tries to borrow 1000 DAI at a stable rate without any collateral (revert expected) User 1 withdrawws", "actions": [ { "name": "mint", @@ -270,7 +270,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "1" }, @@ -308,7 +307,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "2" }, @@ -346,7 +344,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "3" }, @@ -384,7 +381,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "4" }, @@ -578,7 +574,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "2", "user": "1" }, diff --git a/test/helpers/scenarios/borrow-repay-variable.json b/test-suites/test-aave/helpers/scenarios/borrow-repay-variable.json similarity index 97% rename from test/helpers/scenarios/borrow-repay-variable.json rename to test-suites/test-aave/helpers/scenarios/borrow-repay-variable.json index 46c17610..aa269fd7 100644 --- a/test/helpers/scenarios/borrow-repay-variable.json +++ b/test-suites/test-aave/helpers/scenarios/borrow-repay-variable.json @@ -83,7 +83,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "1" }, @@ -205,7 +204,6 @@ "name": "withdraw", "args": { "reserve": "WETH", - "amount": "-1", "user": "1" }, @@ -237,7 +235,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "0.001", "user": "2" }, @@ -246,7 +243,7 @@ ] }, { - "description": "User 0 deposits 1 WETH, user 1 deposits 100 LINK as collateral and borrows 0.5 ETH at variable rate", + "description": "User 0 deposits 1 WETH, user 1 deposits 100 LINK as collateral and borrows 0.5 WETH at variable rate", "actions": [ { "name": "mint", @@ -269,7 +266,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "0" }, @@ -306,7 +302,6 @@ "name": "borrow", "args": { "reserve": "WETH", - "amount": "0.5", "borrowRateMode": "variable", "user": "1", @@ -317,7 +312,7 @@ ] }, { - "description": "User 1 tries to repay 0 ETH", + "description": "User 1 tries to repay 0 WETH", "actions": [ { "name": "repay", @@ -340,7 +335,6 @@ "name": "repay", "args": { "reserve": "WETH", - "amount": "-1", "user": "2", "borrowRateMode": "variable", @@ -375,7 +369,6 @@ "name": "repay", "args": { "reserve": "WETH", - "amount": "0.2", "user": "3", "borrowRateMode": "variable", @@ -409,7 +402,6 @@ "name": "repay", "args": { "reserve": "WETH", - "amount": "-1", "borrowRateMode": "variable", "user": "1", @@ -426,7 +418,6 @@ "name": "withdraw", "args": { "reserve": "WETH", - "amount": "-1", "user": "0" }, @@ -530,7 +521,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "1" }, @@ -670,7 +660,7 @@ ] }, { - "description": "user 3 deposits 0.1 ETH collateral to borrow 100 DAI; 0.1 ETH is not enough to borrow 100 DAI (revert expected)", + "description": "user 3 deposits 0.1 WETH collateral to borrow 100 DAI; 0.1 WETH is not enough to borrow 100 DAI (revert expected)", "actions": [ { "name": "mint", @@ -693,7 +683,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "0.1", "user": "3" }, @@ -713,13 +702,12 @@ ] }, { - "description": "user 3 withdraws the 0.1 ETH", + "description": "user 3 withdraws the 0.1 WETH", "actions": [ { "name": "withdraw", "args": { "reserve": "WETH", - "amount": "-1", "user": "3" }, @@ -770,7 +758,7 @@ ] }, { - "description": "user 3 deposits 0.1 ETH collateral to borrow 100 USDC; 0.1 ETH is not enough to borrow 100 USDC (revert expected)", + "description": "user 3 deposits 0.1 WETH collateral to borrow 100 USDC; 0.1 WETH is not enough to borrow 100 USDC (revert expected)", "actions": [ { "name": "mint", @@ -793,7 +781,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "0.1", "user": "3" }, @@ -813,13 +800,12 @@ ] }, { - "description": "user 3 withdraws the 0.1 ETH", + "description": "user 3 withdraws the 0.1 WETH", "actions": [ { "name": "withdraw", "args": { "reserve": "WETH", - "amount": "-1", "user": "3" }, @@ -877,7 +863,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "2", "user": "6" }, diff --git a/test/helpers/scenarios/credit-delegation.json b/test-suites/test-aave/helpers/scenarios/credit-delegation.json similarity index 95% rename from test/helpers/scenarios/credit-delegation.json rename to test-suites/test-aave/helpers/scenarios/credit-delegation.json index 0d15d7f1..2efabeb1 100644 --- a/test/helpers/scenarios/credit-delegation.json +++ b/test-suites/test-aave/helpers/scenarios/credit-delegation.json @@ -3,7 +3,7 @@ "description": "Test cases for the credit delegation related functions.", "stories": [ { - "description": "User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH on variable to user 4, user 4 borrows 1 WETH variable on behalf of user 0", + "description": "User 3 deposits 1000 WETH. User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH on variable to user 4, user 4 borrows 1 WETH variable on behalf of user 0", "actions": [ { "name": "mint", diff --git a/test/helpers/scenarios/deposit.json b/test-suites/test-aave/helpers/scenarios/deposit.json similarity index 97% rename from test/helpers/scenarios/deposit.json rename to test-suites/test-aave/helpers/scenarios/deposit.json index 2456d931..b4b73879 100644 --- a/test/helpers/scenarios/deposit.json +++ b/test-suites/test-aave/helpers/scenarios/deposit.json @@ -34,7 +34,7 @@ ] }, { - "description": "User 1 deposits 1000 DAI after user 1", + "description": "User 1 deposits 1000 DAI after user 0", "actions": [ { "name": "mint", @@ -150,7 +150,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "0" }, @@ -191,7 +190,7 @@ ] }, { - "description": "User 1 deposits 0 ETH (revert expected)", + "description": "User 1 deposits 0 WETH (revert expected)", "actions": [ { "name": "mint", diff --git a/test/helpers/scenarios/rebalance-stable-rate.json b/test-suites/test-aave/helpers/scenarios/rebalance-stable-rate.json similarity index 87% rename from test/helpers/scenarios/rebalance-stable-rate.json rename to test-suites/test-aave/helpers/scenarios/rebalance-stable-rate.json index 1da9a376..79c224f8 100644 --- a/test/helpers/scenarios/rebalance-stable-rate.json +++ b/test-suites/test-aave/helpers/scenarios/rebalance-stable-rate.json @@ -10,8 +10,7 @@ "args": { "reserve": "USDC", "user": "0", - "target": "1", - "borrowRateMode": "variable" + "target": "1" }, "expected": "revert", "revertMessage": "User does not have any stable rate loan for this reserve" @@ -19,7 +18,7 @@ ] }, { - "description": "User 0 deposits 1000 USDC, user 1 deposits 5 ETH, borrows 250 USDC at a stable rate, user 0 rebalances user 1 (revert expected)", + "description": "User 0 deposits 1000 USDC, user 1 deposits 7 WETH, borrows 250 USDC at a stable rate, user 0 rebalances user 1 (revert expected)", "actions": [ { "name": "mint", @@ -68,7 +67,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "7", "user": "1" }, @@ -97,7 +95,7 @@ ] }, { - "description": "User 1 borrows another 200 at stable, user 0 tries to rebalance but the conditions are not met (revert expected)", + "description": "User 1 borrows another 200 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)", "actions": [ { "name": "borrow", @@ -122,7 +120,7 @@ ] }, { - "description": "User 1 borrows another 200 at stable, user 0 tries to rebalance but the conditions are not met (revert expected)", + "description": "User 1 borrows another 200 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)", "actions": [ { "name": "borrow", @@ -147,7 +145,7 @@ ] }, { - "description": "User 1 borrows another 100 at stable, user 0 tries to rebalance but the conditions are not met (revert expected)", + "description": "User 1 borrows another 100 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)", "actions": [ { "name": "borrow", @@ -173,7 +171,7 @@ }, { - "description": "User 0 borrows the remaining USDC (usage ratio = 100%). User 0 rebalances user 1", + "description": "User 1 borrows the remaining USDC (usage ratio = 100%) at variable. User 0 rebalances user 1", "actions": [ { "name": "borrow", diff --git a/test/helpers/scenarios/set-use-as-collateral.json b/test-suites/test-aave/helpers/scenarios/set-use-as-collateral.json similarity index 97% rename from test/helpers/scenarios/set-use-as-collateral.json rename to test-suites/test-aave/helpers/scenarios/set-use-as-collateral.json index 53b5128e..2a206bb9 100644 --- a/test/helpers/scenarios/set-use-as-collateral.json +++ b/test-suites/test-aave/helpers/scenarios/set-use-as-collateral.json @@ -66,7 +66,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "2", "user": "1" }, @@ -76,7 +75,6 @@ "name": "setUseAsCollateral", "args": { "reserve": "WETH", - "user": "1", "useAsCollateral": "false" }, @@ -126,7 +124,6 @@ "name": "setUseAsCollateral", "args": { "reserve": "WETH", - "user": "1", "useAsCollateral": "false" }, @@ -136,7 +133,7 @@ ] }, { - "description": "User 1 Deposits 1000 AAVE, disables WETH as collateral. Should revert as 1000 AAVE are not enough to cover the debt (revert expected)", + "description": "User 1 Deposits 10 AAVE, disables WETH as collateral. Should revert as 10 AAVE are not enough to cover the debt (revert expected)", "actions": [ { "name": "mint", @@ -159,7 +156,6 @@ "name": "deposit", "args": { "reserve": "AAVE", - "amount": "10", "user": "1" }, @@ -169,7 +165,6 @@ "name": "setUseAsCollateral", "args": { "reserve": "WETH", - "user": "1", "useAsCollateral": "false" }, @@ -193,7 +188,6 @@ "name": "deposit", "args": { "reserve": "AAVE", - "amount": "640", "user": "1" }, @@ -203,7 +197,6 @@ "name": "setUseAsCollateral", "args": { "reserve": "WETH", - "user": "1", "useAsCollateral": "false" }, @@ -218,7 +211,6 @@ "name": "setUseAsCollateral", "args": { "reserve": "AAVE", - "user": "1", "useAsCollateral": "false" }, @@ -233,7 +225,6 @@ "name": "setUseAsCollateral", "args": { "reserve": "WETH", - "user": "1", "useAsCollateral": "true" }, diff --git a/test/helpers/scenarios/swap-rate-mode.json b/test-suites/test-aave/helpers/scenarios/swap-rate-mode.json similarity index 97% rename from test/helpers/scenarios/swap-rate-mode.json rename to test-suites/test-aave/helpers/scenarios/swap-rate-mode.json index e2612586..137196cf 100644 --- a/test/helpers/scenarios/swap-rate-mode.json +++ b/test-suites/test-aave/helpers/scenarios/swap-rate-mode.json @@ -33,7 +33,7 @@ ] }, { - "description": "User 0 deposits 1000 DAI, user 1 deposits 2 ETH as collateral, borrows 100 DAI at variable rate and swaps to stable after one year", + "description": "User 0 deposits 1000 DAI, user 1 deposits 2 WETH as collateral, borrows 100 DAI at variable rate and swaps to stable after one year", "actions": [ { "name": "mint", @@ -82,7 +82,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "2", "user": "1" }, diff --git a/test/helpers/scenarios/withdraw-negatives.json b/test-suites/test-aave/helpers/scenarios/withdraw-negatives.json similarity index 99% rename from test/helpers/scenarios/withdraw-negatives.json rename to test-suites/test-aave/helpers/scenarios/withdraw-negatives.json index 1fb070f5..fbc5c308 100644 --- a/test/helpers/scenarios/withdraw-negatives.json +++ b/test-suites/test-aave/helpers/scenarios/withdraw-negatives.json @@ -83,7 +83,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "1" }, @@ -103,7 +102,6 @@ "name": "withdraw", "args": { "reserve": "WETH", - "amount": "-1", "user": "1" }, diff --git a/test/helpers/scenarios/withdraw.json b/test-suites/test-aave/helpers/scenarios/withdraw.json similarity index 94% rename from test/helpers/scenarios/withdraw.json rename to test-suites/test-aave/helpers/scenarios/withdraw.json index 436984fc..19968ccc 100644 --- a/test/helpers/scenarios/withdraw.json +++ b/test-suites/test-aave/helpers/scenarios/withdraw.json @@ -144,7 +144,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "0" }, @@ -153,13 +152,12 @@ ] }, { - "description": "User 0 withdraws half of the deposited ETH", + "description": "User 0 withdraws half of the deposited WETH", "actions": [ { "name": "withdraw", "args": { "reserve": "WETH", - "amount": "0.5", "user": "0" }, @@ -168,13 +166,12 @@ ] }, { - "description": "User 0 withdraws remaining half of the deposited ETH", + "description": "User 0 withdraws remaining half of the deposited WETH", "actions": [ { "name": "withdraw", "args": { "reserve": "WETH", - "amount": "-1", "user": "0" }, @@ -243,23 +240,6 @@ { "description": "Users 0 deposits 1000 DAI, user 1 Deposit 1000 USDC and 1 WETH, borrows 100 DAI. User 1 tries to withdraw all the USDC", "actions": [ - { - "name": "mint", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "1" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "DAI", - "user": "1" - }, - "expected": "success" - }, { "name": "deposit", "args": { @@ -316,7 +296,6 @@ "name": "deposit", "args": { "reserve": "WETH", - "amount": "1", "user": "1" }, diff --git a/test/helpers/utils/calculations.ts b/test-suites/test-aave/helpers/utils/calculations.ts similarity index 98% rename from test/helpers/utils/calculations.ts rename to test-suites/test-aave/helpers/utils/calculations.ts index a4db008f..de3d31f9 100644 --- a/test/helpers/utils/calculations.ts +++ b/test-suites/test-aave/helpers/utils/calculations.ts @@ -1,13 +1,14 @@ import BigNumber from 'bignumber.js'; -import { ONE_YEAR, RAY, MAX_UINT_AMOUNT, PERCENTAGE_FACTOR } from '../../../helpers/constants'; +import { ONE_YEAR, RAY, MAX_UINT_AMOUNT, PERCENTAGE_FACTOR } from '../../../../helpers/constants'; import { IReserveParams, iAavePoolAssets, RateMode, tEthereumAddress, -} from '../../../helpers/types'; +} from '../../../../helpers/types'; import './math'; import { ReserveData, UserReserveData } from './interfaces'; +import { expect } from 'chai'; export const strToBN = (amount: string): BigNumber => new BigNumber(amount); @@ -1243,29 +1244,29 @@ export const calcExpectedInterestRates = ( ]; let stableBorrowRate: BigNumber = marketStableRate; - let variableBorrowRate: BigNumber = new BigNumber(reserveConfiguration.baseVariableBorrowRate); + let variableBorrowRate: BigNumber = new BigNumber(reserveConfiguration.strategy.baseVariableBorrowRate); - const optimalRate = new BigNumber(reserveConfiguration.optimalUtilizationRate); + const optimalRate = new BigNumber(reserveConfiguration.strategy.optimalUtilizationRate); const excessRate = new BigNumber(RAY).minus(optimalRate); if (utilizationRate.gt(optimalRate)) { const excessUtilizationRateRatio = utilizationRate - .minus(reserveConfiguration.optimalUtilizationRate) + .minus(reserveConfiguration.strategy.optimalUtilizationRate) .rayDiv(excessRate); stableBorrowRate = stableBorrowRate - .plus(reserveConfiguration.stableRateSlope1) + .plus(reserveConfiguration.strategy.stableRateSlope1) .plus( - new BigNumber(reserveConfiguration.stableRateSlope2).rayMul(excessUtilizationRateRatio) + new BigNumber(reserveConfiguration.strategy.stableRateSlope2).rayMul(excessUtilizationRateRatio) ); variableBorrowRate = variableBorrowRate - .plus(reserveConfiguration.variableRateSlope1) + .plus(reserveConfiguration.strategy.variableRateSlope1) .plus( - new BigNumber(reserveConfiguration.variableRateSlope2).rayMul(excessUtilizationRateRatio) + new BigNumber(reserveConfiguration.strategy.variableRateSlope2).rayMul(excessUtilizationRateRatio) ); } else { stableBorrowRate = stableBorrowRate.plus( - new BigNumber(reserveConfiguration.stableRateSlope1).rayMul( + new BigNumber(reserveConfiguration.strategy.stableRateSlope1).rayMul( utilizationRate.rayDiv(new BigNumber(optimalRate)) ) ); @@ -1273,7 +1274,7 @@ export const calcExpectedInterestRates = ( variableBorrowRate = variableBorrowRate.plus( utilizationRate .rayDiv(optimalRate) - .rayMul(new BigNumber(reserveConfiguration.variableRateSlope1)) + .rayMul(new BigNumber(reserveConfiguration.strategy.variableRateSlope1)) ); } diff --git a/test/helpers/utils/helpers.ts b/test-suites/test-aave/helpers/utils/helpers.ts similarity index 93% rename from test/helpers/utils/helpers.ts rename to test-suites/test-aave/helpers/utils/helpers.ts index fc7a9709..c38ce8e6 100644 --- a/test/helpers/utils/helpers.ts +++ b/test-suites/test-aave/helpers/utils/helpers.ts @@ -1,4 +1,4 @@ -import { LendingPool } from '../../../types/LendingPool'; +import { LendingPool } from '../../../../types/LendingPool'; import { ReserveData, UserReserveData } from './interfaces'; import { getLendingRateOracle, @@ -7,11 +7,11 @@ import { getAToken, getStableDebtToken, getVariableDebtToken, -} from '../../../helpers/contracts-getters'; -import { tEthereumAddress } from '../../../helpers/types'; +} from '../../../../helpers/contracts-getters'; +import { tEthereumAddress } from '../../../../helpers/types'; import BigNumber from 'bignumber.js'; -import { getDb, DRE } from '../../../helpers/misc-utils'; -import { AaveProtocolDataProvider } from '../../../types/AaveProtocolDataProvider'; +import { getDb, DRE } from '../../../../helpers/misc-utils'; +import { AaveProtocolDataProvider } from '../../../../types/AaveProtocolDataProvider'; export const getReserveData = async ( helper: AaveProtocolDataProvider, diff --git a/test/helpers/utils/interfaces/index.ts b/test-suites/test-aave/helpers/utils/interfaces/index.ts similarity index 100% rename from test/helpers/utils/interfaces/index.ts rename to test-suites/test-aave/helpers/utils/interfaces/index.ts diff --git a/test/helpers/utils/math.ts b/test-suites/test-aave/helpers/utils/math.ts similarity index 98% rename from test/helpers/utils/math.ts rename to test-suites/test-aave/helpers/utils/math.ts index efd296e0..27ea0531 100644 --- a/test/helpers/utils/math.ts +++ b/test-suites/test-aave/helpers/utils/math.ts @@ -7,7 +7,7 @@ import { WAD_RAY_RATIO, HALF_PERCENTAGE, PERCENTAGE_FACTOR, -} from '../../../helpers/constants'; +} from '../../../../helpers/constants'; declare module 'bignumber.js' { interface BigNumber { diff --git a/test/lending-pool-addresses-provider.spec.ts b/test-suites/test-aave/lending-pool-addresses-provider.spec.ts similarity index 92% rename from test/lending-pool-addresses-provider.spec.ts rename to test-suites/test-aave/lending-pool-addresses-provider.spec.ts index 48570458..0f7630f9 100644 --- a/test/lending-pool-addresses-provider.spec.ts +++ b/test-suites/test-aave/lending-pool-addresses-provider.spec.ts @@ -1,11 +1,11 @@ import { expect } from 'chai'; -import { createRandomAddress } from '../helpers/misc-utils'; +import { createRandomAddress } from '../../helpers/misc-utils'; import { makeSuite, TestEnv } from './helpers/make-suite'; -import { ProtocolErrors } from '../helpers/types'; +import { ProtocolErrors } from '../../helpers/types'; import { ethers } from 'ethers'; -import { ZERO_ADDRESS } from '../helpers/constants'; -import { waitForTx } from '../helpers/misc-utils'; -import { deployLendingPool } from '../helpers/contracts-deployments'; +import { ZERO_ADDRESS } from '../../helpers/constants'; +import { waitForTx } from '../../helpers/misc-utils'; +import { deployLendingPool } from '../../helpers/contracts-deployments'; const { utils } = ethers; diff --git a/test/liquidation-atoken.spec.ts b/test-suites/test-aave/liquidation-atoken.spec.ts similarity index 98% rename from test/liquidation-atoken.spec.ts rename to test-suites/test-aave/liquidation-atoken.spec.ts index 6483162c..1b6f9911 100644 --- a/test/liquidation-atoken.spec.ts +++ b/test-suites/test-aave/liquidation-atoken.spec.ts @@ -1,10 +1,10 @@ import BigNumber from 'bignumber.js'; -import { DRE } from '../helpers/misc-utils'; -import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../helpers/constants'; -import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; +import { DRE } from '../../helpers/misc-utils'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; import { makeSuite } from './helpers/make-suite'; -import { ProtocolErrors, RateMode } from '../helpers/types'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; import { calcExpectedVariableDebtTokenBalance } from './helpers/utils/calculations'; import { getUserData, getReserveData } from './helpers/utils/helpers'; diff --git a/test/liquidation-underlying.spec.ts b/test-suites/test-aave/liquidation-underlying.spec.ts similarity index 98% rename from test/liquidation-underlying.spec.ts rename to test-suites/test-aave/liquidation-underlying.spec.ts index 0e37d331..b0951ce3 100644 --- a/test/liquidation-underlying.spec.ts +++ b/test-suites/test-aave/liquidation-underlying.spec.ts @@ -1,13 +1,13 @@ import BigNumber from 'bignumber.js'; -import { DRE, increaseTime } from '../helpers/misc-utils'; -import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../helpers/constants'; -import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; +import { DRE, increaseTime } from '../../helpers/misc-utils'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; import { makeSuite } from './helpers/make-suite'; -import { ProtocolErrors, RateMode } from '../helpers/types'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; import { calcExpectedStableDebtTokenBalance } from './helpers/utils/calculations'; import { getUserData } from './helpers/utils/helpers'; -import { CommonsConfig } from '../markets/aave/commons'; +import { CommonsConfig } from '../../markets/aave/commons'; import { parseEther } from 'ethers/lib/utils'; diff --git a/test/mainnet/check-list.spec.ts b/test-suites/test-aave/mainnet/check-list.spec.ts similarity index 97% rename from test/mainnet/check-list.spec.ts rename to test-suites/test-aave/mainnet/check-list.spec.ts index 99020fa2..a741dab4 100644 --- a/test/mainnet/check-list.spec.ts +++ b/test-suites/test-aave/mainnet/check-list.spec.ts @@ -1,12 +1,12 @@ -import { MAX_UINT_AMOUNT } from '../../helpers/constants'; -import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { MAX_UINT_AMOUNT } from '../../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../../helpers/contracts-helpers'; import { makeSuite, TestEnv } from '../helpers/make-suite'; import { parseEther } from 'ethers/lib/utils'; -import { DRE, waitForTx } from '../../helpers/misc-utils'; +import { DRE, waitForTx } from '../../../helpers/misc-utils'; import { BigNumber } from 'ethers'; -import { getStableDebtToken, getVariableDebtToken } from '../../helpers/contracts-getters'; -import { deploySelfdestructTransferMock } from '../../helpers/contracts-deployments'; -import { IUniswapV2Router02Factory } from '../../types/IUniswapV2Router02Factory'; +import { getStableDebtToken, getVariableDebtToken } from '../../../helpers/contracts-getters'; +import { deploySelfdestructTransferMock } from '../../../helpers/contracts-deployments'; +import { IUniswapV2Router02Factory } from '../../../types/IUniswapV2Router02Factory'; const { expect } = require('chai'); diff --git a/test/pausable-functions.spec.ts b/test-suites/test-aave/pausable-functions.spec.ts similarity index 96% rename from test/pausable-functions.spec.ts rename to test-suites/test-aave/pausable-functions.spec.ts index 2ac975ac..c95aaf3c 100644 --- a/test/pausable-functions.spec.ts +++ b/test-suites/test-aave/pausable-functions.spec.ts @@ -1,11 +1,11 @@ import { makeSuite, TestEnv } from './helpers/make-suite'; -import { ProtocolErrors, RateMode } from '../helpers/types'; -import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../helpers/constants'; -import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; import { parseEther, parseUnits } from 'ethers/lib/utils'; import { BigNumber } from 'bignumber.js'; -import { MockFlashLoanReceiver } from '../types/MockFlashLoanReceiver'; -import { getMockFlashLoanReceiver } from '../helpers/contracts-getters'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; const { expect } = require('chai'); diff --git a/test-suites/test-aave/rate-strategy.spec.ts b/test-suites/test-aave/rate-strategy.spec.ts new file mode 100644 index 00000000..6961f8b5 --- /dev/null +++ b/test-suites/test-aave/rate-strategy.spec.ts @@ -0,0 +1,193 @@ +import { TestEnv, makeSuite } from './helpers/make-suite'; +import { deployDefaultReserveInterestRateStrategy } from '../../helpers/contracts-deployments'; + +import { APPROVAL_AMOUNT_LENDING_POOL, PERCENTAGE_FACTOR, RAY } from '../../helpers/constants'; + +import { rateStrategyStableOne } from '../../markets/aave/rateStrategies'; + +import { strategyDAI } from '../../markets/aave/reservesConfigs'; +import { AToken, DefaultReserveInterestRateStrategy, MintableERC20 } from '../../types'; +import BigNumber from 'bignumber.js'; +import './helpers/utils/math'; + +const { expect } = require('chai'); + +makeSuite('Interest rate strategy tests', (testEnv: TestEnv) => { + let strategyInstance: DefaultReserveInterestRateStrategy; + let dai: MintableERC20; + let aDai: AToken; + + before(async () => { + dai = testEnv.dai; + aDai = testEnv.aDai; + + const { addressesProvider } = testEnv; + + strategyInstance = await deployDefaultReserveInterestRateStrategy( + [ + addressesProvider.address, + rateStrategyStableOne.optimalUtilizationRate, + rateStrategyStableOne.baseVariableBorrowRate, + rateStrategyStableOne.variableRateSlope1, + rateStrategyStableOne.variableRateSlope2, + rateStrategyStableOne.stableRateSlope1, + rateStrategyStableOne.stableRateSlope2, + ], + false + ); + }); + + it('Checks rates at 0% utilization rate, empty reserve', async () => { + const { + 0: currentLiquidityRate, + 1: currentStableBorrowRate, + 2: currentVariableBorrowRate, + } = await strategyInstance['calculateInterestRates(address,address,uint256,uint256,uint256,uint256,uint256,uint256)']( + dai.address, + aDai.address, + 0, + 0, + 0, + 0, + 0, + strategyDAI.reserveFactor + ); + + expect(currentLiquidityRate.toString()).to.be.equal('0', 'Invalid liquidity rate'); + expect(currentStableBorrowRate.toString()).to.be.equal( + new BigNumber(0.039).times(RAY).toFixed(0), + 'Invalid stable rate' + ); + expect(currentVariableBorrowRate.toString()).to.be.equal( + rateStrategyStableOne.baseVariableBorrowRate, + 'Invalid variable rate' + ); + }); + + it('Checks rates at 80% utilization rate', async () => { + const { + 0: currentLiquidityRate, + 1: currentStableBorrowRate, + 2: currentVariableBorrowRate, + } = await strategyInstance['calculateInterestRates(address,address,uint256,uint256,uint256,uint256,uint256,uint256)']( + dai.address, + aDai.address, + '200000000000000000', + '0', + '0', + '800000000000000000', + '0', + strategyDAI.reserveFactor + ); + + const expectedVariableRate = new BigNumber(rateStrategyStableOne.baseVariableBorrowRate).plus( + rateStrategyStableOne.variableRateSlope1 + ); + + expect(currentLiquidityRate.toString()).to.be.equal( + expectedVariableRate + .times(0.8) + .percentMul(new BigNumber(PERCENTAGE_FACTOR).minus(strategyDAI.reserveFactor)) + .toFixed(0), + 'Invalid liquidity rate' + ); + + expect(currentVariableBorrowRate.toString()).to.be.equal( + expectedVariableRate.toFixed(0), + 'Invalid variable rate' + ); + + expect(currentStableBorrowRate.toString()).to.be.equal( + new BigNumber(0.039).times(RAY).plus(rateStrategyStableOne.stableRateSlope1).toFixed(0), + 'Invalid stable rate' + ); + }); + + it('Checks rates at 100% utilization rate', async () => { + const { + 0: currentLiquidityRate, + 1: currentStableBorrowRate, + 2: currentVariableBorrowRate, + } = await strategyInstance['calculateInterestRates(address,address,uint256,uint256,uint256,uint256,uint256,uint256)']( + dai.address, + aDai.address, + '0', + '0', + '0', + '800000000000000000', + '0', + strategyDAI.reserveFactor + ); + + const expectedVariableRate = new BigNumber(rateStrategyStableOne.baseVariableBorrowRate) + .plus(rateStrategyStableOne.variableRateSlope1) + .plus(rateStrategyStableOne.variableRateSlope2); + + expect(currentLiquidityRate.toString()).to.be.equal( + expectedVariableRate + .percentMul(new BigNumber(PERCENTAGE_FACTOR).minus(strategyDAI.reserveFactor)) + .toFixed(0), + 'Invalid liquidity rate' + ); + + expect(currentVariableBorrowRate.toString()).to.be.equal( + expectedVariableRate.toFixed(0), + 'Invalid variable rate' + ); + + expect(currentStableBorrowRate.toString()).to.be.equal( + new BigNumber(0.039) + .times(RAY) + .plus(rateStrategyStableOne.stableRateSlope1) + .plus(rateStrategyStableOne.stableRateSlope2) + .toFixed(0), + 'Invalid stable rate' + ); + }); + + it('Checks rates at 100% utilization rate, 50% stable debt and 50% variable debt, with a 10% avg stable rate', async () => { + const { + 0: currentLiquidityRate, + 1: currentStableBorrowRate, + 2: currentVariableBorrowRate, + } = await strategyInstance['calculateInterestRates(address,address,uint256,uint256,uint256,uint256,uint256,uint256)']( + dai.address, + aDai.address, + '0', + '0', + '400000000000000000', + '400000000000000000', + '100000000000000000000000000', + strategyDAI.reserveFactor + ); + + const expectedVariableRate = new BigNumber(rateStrategyStableOne.baseVariableBorrowRate) + .plus(rateStrategyStableOne.variableRateSlope1) + .plus(rateStrategyStableOne.variableRateSlope2); + + const expectedLiquidityRate = new BigNumber( + currentVariableBorrowRate.add('100000000000000000000000000').div(2).toString() + ) + .percentMul(new BigNumber(PERCENTAGE_FACTOR).minus(strategyDAI.reserveFactor)) + .toFixed(0); + + expect(currentLiquidityRate.toString()).to.be.equal( + expectedLiquidityRate, + 'Invalid liquidity rate' + ); + + expect(currentVariableBorrowRate.toString()).to.be.equal( + expectedVariableRate.toFixed(0), + 'Invalid variable rate' + ); + + expect(currentStableBorrowRate.toString()).to.be.equal( + new BigNumber(0.039) + .times(RAY) + .plus(rateStrategyStableOne.stableRateSlope1) + .plus(rateStrategyStableOne.stableRateSlope2) + .toFixed(0), + 'Invalid stable rate' + ); + }); +}); diff --git a/test/scenario.spec.ts b/test-suites/test-aave/scenario.spec.ts similarity index 87% rename from test/scenario.spec.ts rename to test-suites/test-aave/scenario.spec.ts index b7b3c318..68b792d5 100644 --- a/test/scenario.spec.ts +++ b/test-suites/test-aave/scenario.spec.ts @@ -4,11 +4,11 @@ import { configuration as calculationsConfiguration } from './helpers/utils/calc import fs from 'fs'; import BigNumber from 'bignumber.js'; import { makeSuite } from './helpers/make-suite'; -import { getReservesConfigByPool } from '../helpers/configuration'; -import { AavePools, iAavePoolAssets, IReserveParams } from '../helpers/types'; +import { getReservesConfigByPool } from '../../helpers/configuration'; +import { AavePools, iAavePoolAssets, IReserveParams } from '../../helpers/types'; import { executeStory } from './helpers/scenario-engine'; -const scenarioFolder = './test/helpers/scenarios/'; +const scenarioFolder = './test-suites/test-aave/helpers/scenarios/'; const selectedScenarios: string[] = []; diff --git a/test/stable-rate-economy.spec.ts b/test-suites/test-aave/stable-rate-economy.spec.ts similarity index 100% rename from test/stable-rate-economy.spec.ts rename to test-suites/test-aave/stable-rate-economy.spec.ts diff --git a/test/stable-token.spec.ts b/test-suites/test-aave/stable-token.spec.ts similarity index 91% rename from test/stable-token.spec.ts rename to test-suites/test-aave/stable-token.spec.ts index ab588a81..1e3ad27b 100644 --- a/test/stable-token.spec.ts +++ b/test-suites/test-aave/stable-token.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { makeSuite, TestEnv } from './helpers/make-suite'; -import { ProtocolErrors } from '../helpers/types'; -import { getStableDebtToken } from '../helpers/contracts-getters'; +import { ProtocolErrors } from '../../helpers/types'; +import { getStableDebtToken } from '../../helpers/contracts-getters'; makeSuite('Stable debt token tests', (testEnv: TestEnv) => { const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors; diff --git a/test/subgraph-scenarios.spec.ts b/test-suites/test-aave/subgraph-scenarios.spec.ts similarity index 88% rename from test/subgraph-scenarios.spec.ts rename to test-suites/test-aave/subgraph-scenarios.spec.ts index 27f0b994..24959a41 100644 --- a/test/subgraph-scenarios.spec.ts +++ b/test-suites/test-aave/subgraph-scenarios.spec.ts @@ -3,8 +3,8 @@ import { configuration as calculationsConfiguration } from './helpers/utils/calc import BigNumber from 'bignumber.js'; import { makeSuite } from './helpers/make-suite'; -import { getReservesConfigByPool } from '../helpers/configuration'; -import { AavePools, iAavePoolAssets, IReserveParams } from '../helpers/types'; +import { getReservesConfigByPool } from '../../helpers/configuration'; +import { AavePools, iAavePoolAssets, IReserveParams } from '../../helpers/types'; import { executeStory } from './helpers/scenario-engine'; makeSuite('Subgraph scenario tests', async (testEnv) => { diff --git a/test/uniswapAdapters.base.spec.ts b/test-suites/test-aave/uniswapAdapters.base.spec.ts similarity index 95% rename from test/uniswapAdapters.base.spec.ts rename to test-suites/test-aave/uniswapAdapters.base.spec.ts index 80f76725..1fe88370 100644 --- a/test/uniswapAdapters.base.spec.ts +++ b/test-suites/test-aave/uniswapAdapters.base.spec.ts @@ -1,11 +1,11 @@ import { makeSuite, TestEnv } from './helpers/make-suite'; -import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; -import { getMockUniswapRouter } from '../helpers/contracts-getters'; -import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { getMockUniswapRouter } from '../../helpers/contracts-getters'; +import { MockUniswapV2Router02 } from '../../types/MockUniswapV2Router02'; import BigNumber from 'bignumber.js'; -import { evmRevert, evmSnapshot } from '../helpers/misc-utils'; +import { evmRevert, evmSnapshot } from '../../helpers/misc-utils'; import { ethers } from 'ethers'; -import { USD_ADDRESS } from '../helpers/constants'; +import { USD_ADDRESS } from '../../helpers/constants'; const { parseEther } = ethers.utils; const { expect } = require('chai'); diff --git a/test/uniswapAdapters.flashLiquidation.spec.ts b/test-suites/test-aave/uniswapAdapters.flashLiquidation.spec.ts similarity index 98% rename from test/uniswapAdapters.flashLiquidation.spec.ts rename to test-suites/test-aave/uniswapAdapters.flashLiquidation.spec.ts index 063c6930..cc161b4b 100644 --- a/test/uniswapAdapters.flashLiquidation.spec.ts +++ b/test-suites/test-aave/uniswapAdapters.flashLiquidation.spec.ts @@ -2,15 +2,15 @@ import { makeSuite, TestEnv } from './helpers/make-suite'; import { convertToCurrencyDecimals, buildFlashLiquidationAdapterParams, -} from '../helpers/contracts-helpers'; -import { getMockUniswapRouter } from '../helpers/contracts-getters'; -import { deployFlashLiquidationAdapter } from '../helpers/contracts-deployments'; -import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +} from '../../helpers/contracts-helpers'; +import { getMockUniswapRouter } from '../../helpers/contracts-getters'; +import { deployFlashLiquidationAdapter } from '../../helpers/contracts-deployments'; +import { MockUniswapV2Router02 } from '../../types/MockUniswapV2Router02'; import BigNumber from 'bignumber.js'; -import { DRE, evmRevert, evmSnapshot, increaseTime, waitForTx } from '../helpers/misc-utils'; +import { DRE, evmRevert, evmSnapshot, increaseTime, waitForTx } from '../../helpers/misc-utils'; import { ethers } from 'ethers'; -import { ProtocolErrors, RateMode } from '../helpers/types'; -import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, oneEther } from '../helpers/constants'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, oneEther } from '../../helpers/constants'; import { getUserData } from './helpers/utils/helpers'; import { calcExpectedStableDebtTokenBalance } from './helpers/utils/calculations'; const { expect } = require('chai'); diff --git a/test/uniswapAdapters.liquiditySwap.spec.ts b/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts similarity index 98% rename from test/uniswapAdapters.liquiditySwap.spec.ts rename to test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts index 1e30b2b3..42224c52 100644 --- a/test/uniswapAdapters.liquiditySwap.spec.ts +++ b/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts @@ -5,18 +5,18 @@ import { buildPermitParams, getSignatureFromTypedData, buildLiquiditySwapParams, -} from '../helpers/contracts-helpers'; -import { getMockUniswapRouter } from '../helpers/contracts-getters'; -import { deployUniswapLiquiditySwapAdapter } from '../helpers/contracts-deployments'; -import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +} from '../../helpers/contracts-helpers'; +import { getMockUniswapRouter } from '../../helpers/contracts-getters'; +import { deployUniswapLiquiditySwapAdapter } from '../../helpers/contracts-deployments'; +import { MockUniswapV2Router02 } from '../../types/MockUniswapV2Router02'; import { Zero } from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; -import { DRE, evmRevert, evmSnapshot } from '../helpers/misc-utils'; +import { DRE, evmRevert, evmSnapshot } from '../../helpers/misc-utils'; import { ethers } from 'ethers'; -import { eContractid } from '../helpers/types'; -import { AToken } from '../types/AToken'; -import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; -import { MAX_UINT_AMOUNT } from '../helpers/constants'; +import { eContractid } from '../../helpers/types'; +import { AToken } from '../../types/AToken'; +import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +import { MAX_UINT_AMOUNT } from '../../helpers/constants'; const { parseEther } = ethers.utils; const { expect } = require('chai'); @@ -290,7 +290,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -466,7 +466,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { liquidityToSwap.toString() ); - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -1104,7 +1104,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { liquidityToSwap.toString() ); - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -1270,7 +1270,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { liquidityToSwap.toString() ); - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -1573,7 +1573,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -1793,7 +1793,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } diff --git a/test/uniswapAdapters.repay.spec.ts b/test-suites/test-aave/uniswapAdapters.repay.spec.ts similarity index 98% rename from test/uniswapAdapters.repay.spec.ts rename to test-suites/test-aave/uniswapAdapters.repay.spec.ts index c271917e..c2046cbd 100644 --- a/test/uniswapAdapters.repay.spec.ts +++ b/test-suites/test-aave/uniswapAdapters.repay.spec.ts @@ -5,19 +5,19 @@ import { buildPermitParams, getSignatureFromTypedData, buildRepayAdapterParams, -} from '../helpers/contracts-helpers'; -import { getMockUniswapRouter } from '../helpers/contracts-getters'; -import { deployUniswapRepayAdapter } from '../helpers/contracts-deployments'; -import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +} from '../../helpers/contracts-helpers'; +import { getMockUniswapRouter } from '../../helpers/contracts-getters'; +import { deployUniswapRepayAdapter } from '../../helpers/contracts-deployments'; +import { MockUniswapV2Router02 } from '../../types/MockUniswapV2Router02'; import { Zero } from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; -import { DRE, evmRevert, evmSnapshot } from '../helpers/misc-utils'; +import { DRE, evmRevert, evmSnapshot } from '../../helpers/misc-utils'; import { ethers } from 'ethers'; -import { eContractid } from '../helpers/types'; -import { StableDebtToken } from '../types/StableDebtToken'; -import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; -import { MAX_UINT_AMOUNT } from '../helpers/constants'; -import { VariableDebtToken } from '../types'; +import { eContractid } from '../../helpers/types'; +import { StableDebtToken } from '../../types/StableDebtToken'; +import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +import { MAX_UINT_AMOUNT } from '../../helpers/constants'; +import { VariableDebtToken } from '../../types'; const { parseEther } = ethers.utils; const { expect } = require('chai'); @@ -246,7 +246,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { liquidityToSwap.toString() ); - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } @@ -1019,7 +1019,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { liquidityToSwap.toString() ); - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; if (!ownerPrivateKey) { throw new Error('INVALID_OWNER_PK'); } diff --git a/test-suites/test-aave/upgradeability.spec.ts b/test-suites/test-aave/upgradeability.spec.ts new file mode 100644 index 00000000..8e27a350 --- /dev/null +++ b/test-suites/test-aave/upgradeability.spec.ts @@ -0,0 +1,249 @@ +import { expect } from 'chai'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, eContractid } from '../../helpers/types'; +import { deployContract, getContract } from '../../helpers/contracts-helpers'; +import { MockAToken } from '../../types/MockAToken'; +import { MockStableDebtToken } from '../../types/MockStableDebtToken'; +import { MockVariableDebtToken } from '../../types/MockVariableDebtToken'; +import { ZERO_ADDRESS } from '../../helpers/constants'; +import { + getAToken, + getMockStableDebtToken, + getMockVariableDebtToken, + getStableDebtToken, + getVariableDebtToken, +} from '../../helpers/contracts-getters'; +import { + deployMockAToken, + deployMockStableDebtToken, + deployMockVariableDebtToken, +} from '../../helpers/contracts-deployments'; + +makeSuite('Upgradeability', (testEnv: TestEnv) => { + const { CALLER_NOT_POOL_ADMIN } = ProtocolErrors; + let newATokenAddress: string; + let newStableTokenAddress: string; + let newVariableTokenAddress: string; + + before('deploying instances', async () => { + const { dai, pool } = testEnv; + const aTokenInstance = await deployMockAToken([ + pool.address, + dai.address, + ZERO_ADDRESS, + ZERO_ADDRESS, + 'Aave Interest bearing DAI updated', + 'aDAI', + '0x10' + ]); + + const stableDebtTokenInstance = await deployMockStableDebtToken([ + pool.address, + dai.address, + ZERO_ADDRESS, + 'Aave stable debt bearing DAI updated', + 'stableDebtDAI', + '0x10' + ]); + + const variableDebtTokenInstance = await deployMockVariableDebtToken([ + pool.address, + dai.address, + ZERO_ADDRESS, + 'Aave variable debt bearing DAI updated', + 'variableDebtDAI', + '0x10' + ]); + + newATokenAddress = aTokenInstance.address; + newVariableTokenAddress = variableDebtTokenInstance.address; + newStableTokenAddress = stableDebtTokenInstance.address; + }); + + it('Tries to update the DAI Atoken implementation with a different address than the lendingPoolManager', async () => { + const { dai, configurator, users } = testEnv; + + const name = await (await getAToken(newATokenAddress)).name(); + const symbol = await (await getAToken(newATokenAddress)).symbol(); + + const updateATokenInputParams: { + asset: string; + treasury: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + params: string + } = { + asset: dai.address, + treasury: ZERO_ADDRESS, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newATokenAddress, + params: "0x10" + }; + await expect( + configurator.connect(users[1].signer).updateAToken(updateATokenInputParams) + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Upgrades the DAI Atoken implementation ', async () => { + const { dai, configurator, aDai } = testEnv; + + const name = await (await getAToken(newATokenAddress)).name(); + const symbol = await (await getAToken(newATokenAddress)).symbol(); + + const updateATokenInputParams: { + asset: string; + treasury: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + params: string + } = { + asset: dai.address, + treasury: ZERO_ADDRESS, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newATokenAddress, + params: "0x10" + }; + await configurator.updateAToken(updateATokenInputParams); + + const tokenName = await aDai.name(); + + expect(tokenName).to.be.eq('Aave Interest bearing DAI updated', 'Invalid token name'); + }); + + it('Tries to update the DAI Stable debt token implementation with a different address than the lendingPoolManager', async () => { + const { dai, configurator, users } = testEnv; + + const name = await (await getStableDebtToken(newStableTokenAddress)).name(); + const symbol = await (await getStableDebtToken(newStableTokenAddress)).symbol(); + + + const updateDebtTokenInput: { + asset: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + params: string; + } = { + asset: dai.address, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newStableTokenAddress, + params: '0x10' + } + + await expect( + configurator + .connect(users[1].signer) + .updateStableDebtToken(updateDebtTokenInput) + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Upgrades the DAI stable debt token implementation ', async () => { + const { dai, configurator, pool, helpersContract } = testEnv; + + const name = await (await getStableDebtToken(newStableTokenAddress)).name(); + const symbol = await (await getStableDebtToken(newStableTokenAddress)).symbol(); + + + const updateDebtTokenInput: { + asset: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + params: string; + } = { + asset: dai.address, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newStableTokenAddress, + params: '0x10' + } + + await configurator.updateStableDebtToken(updateDebtTokenInput); + + const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(dai.address); + + const debtToken = await getMockStableDebtToken(stableDebtTokenAddress); + + const tokenName = await debtToken.name(); + + expect(tokenName).to.be.eq('Aave stable debt bearing DAI updated', 'Invalid token name'); + }); + + it('Tries to update the DAI variable debt token implementation with a different address than the lendingPoolManager', async () => { + const {dai, configurator, users} = testEnv; + + const name = await (await getVariableDebtToken(newVariableTokenAddress)).name(); + const symbol = await (await getVariableDebtToken(newVariableTokenAddress)).symbol(); + + const updateDebtTokenInput: { + asset: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + params: string; + } = { + asset: dai.address, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newVariableTokenAddress, + params: '0x10' + } + + await expect( + configurator + .connect(users[1].signer) + .updateVariableDebtToken(updateDebtTokenInput) + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Upgrades the DAI variable debt token implementation ', async () => { + const {dai, configurator, pool, helpersContract} = testEnv; + + const name = await (await getVariableDebtToken(newVariableTokenAddress)).name(); + const symbol = await (await getVariableDebtToken(newVariableTokenAddress)).symbol(); + + const updateDebtTokenInput: { + asset: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + params: string; + } = { + asset: dai.address, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newVariableTokenAddress, + params: '0x10' + } + //const name = await (await getAToken(newATokenAddress)).name(); + + await configurator.updateVariableDebtToken(updateDebtTokenInput); + + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + dai.address + ); + + const debtToken = await getMockVariableDebtToken(variableDebtTokenAddress); + + const tokenName = await debtToken.name(); + + expect(tokenName).to.be.eq('Aave variable debt bearing DAI updated', 'Invalid token name'); + }); +}); diff --git a/test/variable-debt-token.spec.ts b/test-suites/test-aave/variable-debt-token.spec.ts similarity index 93% rename from test/variable-debt-token.spec.ts rename to test-suites/test-aave/variable-debt-token.spec.ts index 0faf7087..d4afbbf0 100644 --- a/test/variable-debt-token.spec.ts +++ b/test-suites/test-aave/variable-debt-token.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { makeSuite, TestEnv } from './helpers/make-suite'; -import { ProtocolErrors, TokenContractId, eContractid } from '../helpers/types'; -import { getVariableDebtToken } from '../helpers/contracts-getters'; +import { ProtocolErrors, TokenContractId, eContractid } from '../../helpers/types'; +import { getVariableDebtToken } from '../../helpers/contracts-getters'; makeSuite('Variable debt token tests', (testEnv: TestEnv) => { const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors; diff --git a/test-suites/test-aave/weth-gateway.spec.ts b/test-suites/test-aave/weth-gateway.spec.ts new file mode 100644 index 00000000..b1980d68 --- /dev/null +++ b/test-suites/test-aave/weth-gateway.spec.ts @@ -0,0 +1,370 @@ +import { MAX_UINT_AMOUNT } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { parseEther } from 'ethers/lib/utils'; +import { DRE, waitForTx } from '../../helpers/misc-utils'; +import { BigNumber } from 'ethers'; +import { getStableDebtToken, getVariableDebtToken } from '../../helpers/contracts-getters'; +import { deploySelfdestructTransferMock } from '../../helpers/contracts-deployments'; + +const { expect } = require('chai'); + +makeSuite('Use native ETH at LendingPool via WETHGateway', (testEnv: TestEnv) => { + const zero = BigNumber.from('0'); + const depositSize = parseEther('5'); + const daiSize = parseEther('10000'); + it('Deposit WETH via WethGateway and DAI', async () => { + const { users, wethGateway, aWETH, pool } = testEnv; + + const user = users[1]; + const depositor = users[0]; + + // Deposit liquidity with native ETH + await wethGateway + .connect(depositor.signer) + .depositETH(pool.address, depositor.address, '0', { value: depositSize }); + + // Deposit with native ETH + await wethGateway + .connect(user.signer) + .depositETH(pool.address, user.address, '0', { value: depositSize }); + + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero); + expect(aTokensBalance).to.be.gte(depositSize); + }); + + it('Withdraw WETH - Partial', async () => { + const { users, wethGateway, aWETH, pool } = testEnv; + + const user = users[1]; + const priorEthersBalance = await user.signer.getBalance(); + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero, 'User should have aTokens.'); + + // Partially withdraw native ETH + const partialWithdraw = await convertToCurrencyDecimals(aWETH.address, '2'); + + // Approve the aTokens to Gateway so Gateway can withdraw and convert to Ether + const approveTx = await aWETH + .connect(user.signer) + .approve(wethGateway.address, MAX_UINT_AMOUNT); + const { gasUsed: approveGas } = await waitForTx(approveTx); + + // Partial Withdraw and send native Ether to user + const { gasUsed: withdrawGas } = await waitForTx( + await wethGateway + .connect(user.signer) + .withdrawETH(pool.address, partialWithdraw, user.address) + ); + + const afterPartialEtherBalance = await user.signer.getBalance(); + const afterPartialATokensBalance = await aWETH.balanceOf(user.address); + const gasCosts = approveGas.add(withdrawGas).mul(approveTx.gasPrice); + + expect(afterPartialEtherBalance).to.be.equal( + priorEthersBalance.add(partialWithdraw).sub(gasCosts), + 'User ETHER balance should contain the partial withdraw' + ); + expect(afterPartialATokensBalance).to.be.equal( + aTokensBalance.sub(partialWithdraw), + 'User aWETH balance should be substracted' + ); + }); + + it('Withdraw WETH - Full', async () => { + const { users, aWETH, wethGateway, pool } = testEnv; + + const user = users[1]; + const priorEthersBalance = await user.signer.getBalance(); + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero, 'User should have aTokens.'); + + // Approve the aTokens to Gateway so Gateway can withdraw and convert to Ether + const approveTx = await aWETH + .connect(user.signer) + .approve(wethGateway.address, MAX_UINT_AMOUNT); + const { gasUsed: approveGas } = await waitForTx(approveTx); + + // Full withdraw + const { gasUsed: withdrawGas } = await waitForTx( + await wethGateway + .connect(user.signer) + .withdrawETH(pool.address, MAX_UINT_AMOUNT, user.address) + ); + + const afterFullEtherBalance = await user.signer.getBalance(); + const afterFullATokensBalance = await aWETH.balanceOf(user.address); + const gasCosts = approveGas.add(withdrawGas).mul(approveTx.gasPrice); + + expect(afterFullEtherBalance).to.be.eq( + priorEthersBalance.add(aTokensBalance).sub(gasCosts), + 'User ETHER balance should contain the full withdraw' + ); + expect(afterFullATokensBalance).to.be.eq(0, 'User aWETH balance should be zero'); + }); + + it('Borrow stable WETH and Full Repay with ETH', async () => { + const { users, wethGateway, aDai, weth, dai, pool, helpersContract } = testEnv; + const borrowSize = parseEther('1'); + const repaySize = borrowSize.add(borrowSize.mul(5).div(100)); + const user = users[1]; + const depositor = users[0]; + + // Deposit with native ETH + await wethGateway + .connect(depositor.signer) + .depositETH(pool.address, depositor.address, '0', { value: depositSize }); + + const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const stableDebtToken = await getStableDebtToken(stableDebtTokenAddress); + + // Deposit 10000 DAI + await dai.connect(user.signer).mint(daiSize); + await dai.connect(user.signer).approve(pool.address, daiSize); + await pool.connect(user.signer).deposit(dai.address, daiSize, user.address, '0'); + + const aTokensBalance = await aDai.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero); + expect(aTokensBalance).to.be.gte(daiSize); + + // Borrow WETH with WETH as collateral + await waitForTx( + await pool.connect(user.signer).borrow(weth.address, borrowSize, '1', '0', user.address) + ); + + const debtBalance = await stableDebtToken.balanceOf(user.address); + + expect(debtBalance).to.be.gt(zero); + + // Full Repay WETH with native ETH + await waitForTx( + await wethGateway + .connect(user.signer) + .repayETH(pool.address, MAX_UINT_AMOUNT, '1', user.address, { value: repaySize }) + ); + + const debtBalanceAfterRepay = await stableDebtToken.balanceOf(user.address); + expect(debtBalanceAfterRepay).to.be.eq(zero); + + // Withdraw DAI + await aDai.connect(user.signer).approve(pool.address, MAX_UINT_AMOUNT); + await pool.connect(user.signer).withdraw(dai.address, MAX_UINT_AMOUNT, user.address); + }); + + it('Borrow variable WETH and Full Repay with ETH', async () => { + const { users, wethGateway, aWETH, weth, pool, helpersContract } = testEnv; + const borrowSize = parseEther('1'); + const repaySize = borrowSize.add(borrowSize.mul(5).div(100)); + const user = users[1]; + + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const varDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + // Deposit with native ETH + await wethGateway + .connect(user.signer) + .depositETH(pool.address, user.address, '0', { value: depositSize }); + + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero); + expect(aTokensBalance).to.be.gte(depositSize); + + // Borrow WETH with WETH as collateral + await waitForTx( + await pool.connect(user.signer).borrow(weth.address, borrowSize, '2', '0', user.address) + ); + + const debtBalance = await varDebtToken.balanceOf(user.address); + + expect(debtBalance).to.be.gt(zero); + + // Partial Repay WETH loan with native ETH + const partialPayment = repaySize.div(2); + await waitForTx( + await wethGateway + .connect(user.signer) + .repayETH(pool.address, partialPayment, '2', user.address, { value: partialPayment }) + ); + + const debtBalanceAfterPartialRepay = await varDebtToken.balanceOf(user.address); + expect(debtBalanceAfterPartialRepay).to.be.lt(debtBalance); + + // Full Repay WETH loan with native ETH + await waitForTx( + await wethGateway + .connect(user.signer) + .repayETH(pool.address, MAX_UINT_AMOUNT, '2', user.address, { value: repaySize }) + ); + const debtBalanceAfterFullRepay = await varDebtToken.balanceOf(user.address); + expect(debtBalanceAfterFullRepay).to.be.eq(zero); + }); + + it('Borrow ETH via delegateApprove ETH and repays back', async () => { + const { users, wethGateway, aWETH, weth, helpersContract, pool } = testEnv; + const borrowSize = parseEther('1'); + const user = users[2]; + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + const varDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + const priorDebtBalance = await varDebtToken.balanceOf(user.address); + expect(priorDebtBalance).to.be.eq(zero); + + // Deposit WETH with native ETH + await wethGateway + .connect(user.signer) + .depositETH(pool.address, user.address, '0', { value: depositSize }); + + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero); + expect(aTokensBalance).to.be.gte(depositSize); + + // Delegates borrowing power of WETH to WETHGateway + await waitForTx( + await varDebtToken.connect(user.signer).approveDelegation(wethGateway.address, borrowSize) + ); + + // Borrows ETH with WETH as collateral + await waitForTx( + await wethGateway.connect(user.signer).borrowETH(pool.address, borrowSize, '2', '0') + ); + + const debtBalance = await varDebtToken.balanceOf(user.address); + + expect(debtBalance).to.be.gt(zero); + + // Full Repay WETH loan with native ETH + await waitForTx( + await wethGateway + .connect(user.signer) + .repayETH(pool.address, MAX_UINT_AMOUNT, '2', user.address, { value: borrowSize.mul(2) }) + ); + const debtBalanceAfterFullRepay = await varDebtToken.balanceOf(user.address); + expect(debtBalanceAfterFullRepay).to.be.eq(zero); + }); + + it('Should revert if receiver function receives Ether if not WETH', async () => { + const { users, wethGateway } = testEnv; + const user = users[0]; + const amount = parseEther('1'); + + // Call receiver function (empty data + value) + await expect( + user.signer.sendTransaction({ + to: wethGateway.address, + value: amount, + gasLimit: DRE.network.config.gas, + }) + ).to.be.revertedWith('Receive not allowed'); + }); + + it('Should revert if fallback functions is called with Ether', async () => { + const { users, wethGateway } = testEnv; + const user = users[0]; + const amount = parseEther('1'); + const fakeABI = ['function wantToCallFallback()']; + const abiCoder = new DRE.ethers.utils.Interface(fakeABI); + const fakeMethodEncoded = abiCoder.encodeFunctionData('wantToCallFallback', []); + + // Call fallback function with value + await expect( + user.signer.sendTransaction({ + to: wethGateway.address, + data: fakeMethodEncoded, + value: amount, + gasLimit: DRE.network.config.gas, + }) + ).to.be.revertedWith('Fallback not allowed'); + }); + + it('Should revert if fallback functions is called', async () => { + const { users, wethGateway } = testEnv; + const user = users[0]; + + const fakeABI = ['function wantToCallFallback()']; + const abiCoder = new DRE.ethers.utils.Interface(fakeABI); + const fakeMethodEncoded = abiCoder.encodeFunctionData('wantToCallFallback', []); + + // Call fallback function without value + await expect( + user.signer.sendTransaction({ + to: wethGateway.address, + data: fakeMethodEncoded, + gasLimit: DRE.network.config.gas, + }) + ).to.be.revertedWith('Fallback not allowed'); + }); + + it('Owner can do emergency token recovery', async () => { + const { users, dai, wethGateway, deployer } = testEnv; + const user = users[0]; + const amount = parseEther('1'); + + await dai.connect(user.signer).mint(amount); + const daiBalanceAfterMint = await dai.balanceOf(user.address); + + await dai.connect(user.signer).transfer(wethGateway.address, amount); + const daiBalanceAfterBadTransfer = await dai.balanceOf(user.address); + expect(daiBalanceAfterBadTransfer).to.be.eq( + daiBalanceAfterMint.sub(amount), + 'User should have lost the funds here.' + ); + + await wethGateway + .connect(deployer.signer) + .emergencyTokenTransfer(dai.address, user.address, amount); + const daiBalanceAfterRecovery = await dai.balanceOf(user.address); + + expect(daiBalanceAfterRecovery).to.be.eq( + daiBalanceAfterMint, + 'User should recover the funds due emergency token transfer' + ); + }); + + it('Owner can do emergency native ETH recovery', async () => { + const { users, wethGateway, deployer } = testEnv; + const user = users[0]; + const amount = parseEther('1'); + const userBalancePriorCall = await user.signer.getBalance(); + + // Deploy contract with payable selfdestruct contract + const selfdestructContract = await deploySelfdestructTransferMock(); + + // Selfdestruct the mock, pointing to WETHGateway address + const callTx = await selfdestructContract + .connect(user.signer) + .destroyAndTransfer(wethGateway.address, { value: amount }); + const { gasUsed } = await waitForTx(callTx); + const gasFees = gasUsed.mul(callTx.gasPrice); + const userBalanceAfterCall = await user.signer.getBalance(); + + expect(userBalanceAfterCall).to.be.eq(userBalancePriorCall.sub(amount).sub(gasFees), ''); + ('User should have lost the funds'); + + // Recover the funds from the contract and sends back to the user + await wethGateway.connect(deployer.signer).emergencyEtherTransfer(user.address, amount); + + const userBalanceAfterRecovery = await user.signer.getBalance(); + const wethGatewayAfterRecovery = await DRE.ethers.provider.getBalance(wethGateway.address); + + expect(userBalanceAfterRecovery).to.be.eq( + userBalancePriorCall.sub(gasFees), + 'User should recover the funds due emergency eth transfer.' + ); + expect(wethGatewayAfterRecovery).to.be.eq('0', 'WETHGateway ether balance should be zero.'); + }); +}); diff --git a/test-suites/test-amm/__setup.spec.ts b/test-suites/test-amm/__setup.spec.ts new file mode 100644 index 00000000..277998f4 --- /dev/null +++ b/test-suites/test-amm/__setup.spec.ts @@ -0,0 +1,308 @@ +import rawBRE from 'hardhat'; +import { MockContract } from 'ethereum-waffle'; +import { + insertContractAddressInDb, + getEthersSigners, + registerContractInJsonDb, +} from '../../helpers/contracts-helpers'; +import { + deployLendingPoolAddressesProvider, + deployMintableERC20, + deployLendingPoolAddressesProviderRegistry, + deployLendingPoolConfigurator, + deployLendingPool, + deployPriceOracle, + deployAaveOracle, + deployLendingPoolCollateralManager, + deployMockFlashLoanReceiver, + deployWalletBalancerProvider, + deployAaveProtocolDataProvider, + deployLendingRateOracle, + deployStableAndVariableTokensHelper, + deployATokensAndRatesHelper, + deployWETHGateway, + deployWETHMocked, + deployMockUniswapRouter, + deployUniswapLiquiditySwapAdapter, + deployUniswapRepayAdapter, + deployFlashLiquidationAdapter, + authorizeWETHGateway +} from '../../helpers/contracts-deployments'; +import { Signer } from 'ethers'; +import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../../helpers/types'; +import { MintableERC20 } from '../../types/MintableERC20'; +import { + ConfigNames, + getReservesConfigByPool, + getTreasuryAddress, + loadPoolConfig, +} from '../../helpers/configuration'; +import { initializeMakeSuite } from './helpers/make-suite'; + +import { + setInitialAssetPricesInOracle, + deployAllMockAggregators, + setInitialMarketRatesInRatesOracleByHelper, +} from '../../helpers/oracles-helpers'; +import { DRE, waitForTx } from '../../helpers/misc-utils'; +import { initReservesByHelper, configureReservesByHelper } from '../../helpers/init-helpers'; +import AmmConfig from '../../markets/amm'; +import { ZERO_ADDRESS } from '../../helpers/constants'; +import { + getLendingPool, + getLendingPoolConfiguratorProxy, + getPairsTokenAggregator, +} from '../../helpers/contracts-getters'; +import { WETH9Mocked } from '../../types/WETH9Mocked'; + +const MOCK_USD_PRICE_IN_WEI = AmmConfig.ProtocolGlobalParams.MockUsdPriceInWei; +const ALL_ASSETS_INITIAL_PRICES = AmmConfig.Mocks.AllAssetsInitialPrices; +const USD_ADDRESS = AmmConfig.ProtocolGlobalParams.UsdAddress; +const MOCK_CHAINLINK_AGGREGATORS_PRICES = AmmConfig.Mocks.AllAssetsInitialPrices; +const LENDING_RATE_ORACLE_RATES_COMMON = AmmConfig.LendingRateOracleRatesCommon; + +const deployAllMockTokens = async (deployer: Signer) => { + const tokens: { [symbol: string]: MockContract | MintableERC20 | WETH9Mocked } = {}; + + const ammConfigData = getReservesConfigByPool(AavePools.amm); + + for (const tokenSymbol of Object.keys(TokenContractId)) { + if (tokenSymbol === 'WETH') { + tokens[tokenSymbol] = await deployWETHMocked(); + await registerContractInJsonDb('WETH', tokens[tokenSymbol]); + continue; + } + let decimals = 18; + + let configData = (ammConfigData)[tokenSymbol]; + + if (!configData) { + decimals = 18; + } + + tokens[tokenSymbol] = await deployMintableERC20([ + tokenSymbol, + tokenSymbol, + configData ? configData.reserveDecimals : 18, + ]); + await registerContractInJsonDb(tokenSymbol.toUpperCase(), tokens[tokenSymbol]); + } + + return tokens; +}; + +const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { + console.time('setup'); + const aaveAdmin = await deployer.getAddress(); + + const mockTokens = await deployAllMockTokens(deployer); + + const addressesProvider = await deployLendingPoolAddressesProvider(AmmConfig.MarketId); + await waitForTx(await addressesProvider.setPoolAdmin(aaveAdmin)); + + //setting users[1] as emergency admin, which is in position 2 in the DRE addresses list + const addressList = await Promise.all( + (await DRE.ethers.getSigners()).map((signer) => signer.getAddress()) + ); + + await waitForTx(await addressesProvider.setEmergencyAdmin(addressList[2])); + + const addressesProviderRegistry = await deployLendingPoolAddressesProviderRegistry(); + await waitForTx( + await addressesProviderRegistry.registerAddressesProvider(addressesProvider.address, 1) + ); + + const lendingPoolImpl = await deployLendingPool(); + + await waitForTx(await addressesProvider.setLendingPoolImpl(lendingPoolImpl.address)); + + const lendingPoolAddress = await addressesProvider.getLendingPool(); + const lendingPoolProxy = await getLendingPool(lendingPoolAddress); + + await insertContractAddressInDb(eContractid.LendingPool, lendingPoolProxy.address); + + const lendingPoolConfiguratorImpl = await deployLendingPoolConfigurator(); + await waitForTx( + await addressesProvider.setLendingPoolConfiguratorImpl(lendingPoolConfiguratorImpl.address) + ); + const lendingPoolConfiguratorProxy = await getLendingPoolConfiguratorProxy( + await addressesProvider.getLendingPoolConfigurator() + ); + await insertContractAddressInDb( + eContractid.LendingPoolConfigurator, + lendingPoolConfiguratorProxy.address + ); + + // Deploy deployment helpers + await deployStableAndVariableTokensHelper([lendingPoolProxy.address, addressesProvider.address]); + await deployATokensAndRatesHelper([ + lendingPoolProxy.address, + addressesProvider.address, + lendingPoolConfiguratorProxy.address, + ]); + + const fallbackOracle = await deployPriceOracle(); + 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, + AAVE: mockTokens.AAVE.address, + BAT: mockTokens.BAT.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, + YFI: mockTokens.BUSD.address, + REN: mockTokens.REN.address, + UNI: mockTokens.UNI.address, + ENJ: mockTokens.ENJ.address, + // DAI: mockTokens.LpDAI.address, + // USDC: mockTokens.LpUSDC.address, + // USDT: mockTokens.LpUSDT.address, + // WBTC: mockTokens.LpWBTC.address, + // WETH: mockTokens.LpWETH.address, + UniDAIWETH: mockTokens.UniDAIWETH.address, + UniWBTCWETH: mockTokens.UniWBTCWETH.address, + UniAAVEWETH: mockTokens.UniAAVEWETH.address, + UniBATWETH: mockTokens.UniBATWETH.address, + UniDAIUSDC: mockTokens.UniDAIUSDC.address, + UniCRVWETH: mockTokens.UniCRVWETH.address, + UniLINKWETH: mockTokens.UniLINKWETH.address, + UniMKRWETH: mockTokens.UniMKRWETH.address, + UniRENWETH: mockTokens.UniRENWETH.address, + UniSNXWETH: mockTokens.UniSNXWETH.address, + UniUNIWETH: mockTokens.UniUNIWETH.address, + UniUSDCWETH: mockTokens.UniUSDCWETH.address, + UniWBTCUSDC: mockTokens.UniWBTCUSDC.address, + UniYFIWETH: mockTokens.UniYFIWETH.address, + BptWBTCWETH: mockTokens.BptWBTCWETH.address, + BptBALWETH: mockTokens.BptBALWETH.address, + WMATIC: mockTokens.WMATIC.address, + USD: USD_ADDRESS, + STAKE: mockTokens.STAKE.address, + }, + fallbackOracle + ); + + 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); + + await deployAaveOracle([tokens, aggregators, fallbackOracle.address, mockTokens.WETH.address]); + await waitForTx(await addressesProvider.setPriceOracle(fallbackOracle.address)); + + const lendingRateOracle = await deployLendingRateOracle(); + await waitForTx(await addressesProvider.setLendingRateOracle(lendingRateOracle.address)); + + const { USD, ...tokensAddressesWithoutUsd } = allTokenAddresses; + const allReservesAddresses = { + ...tokensAddressesWithoutUsd, + }; + await setInitialMarketRatesInRatesOracleByHelper( + LENDING_RATE_ORACLE_RATES_COMMON, + allReservesAddresses, + lendingRateOracle, + aaveAdmin + ); + + const reservesParams = getReservesConfigByPool(AavePools.amm); + + const testHelpers = await deployAaveProtocolDataProvider(addressesProvider.address); + + await insertContractAddressInDb(eContractid.AaveProtocolDataProvider, testHelpers.address); + const admin = await deployer.getAddress(); + + console.log('Initialize configuration'); + + const config = loadPoolConfig(ConfigNames.Amm); + + const { + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, + } = config; + const treasuryAddress = await getTreasuryAddress(config); + + await initReservesByHelper( + reservesParams, + allReservesAddresses, + ATokenNamePrefix, + StableDebtTokenNamePrefix, + VariableDebtTokenNamePrefix, + SymbolPrefix, + admin, + treasuryAddress, + ZERO_ADDRESS, + false + ); + await configureReservesByHelper(reservesParams, allReservesAddresses, testHelpers, admin); + + const collateralManager = await deployLendingPoolCollateralManager(); + await waitForTx( + await addressesProvider.setLendingPoolCollateralManager(collateralManager.address) + ); + await deployMockFlashLoanReceiver(addressesProvider.address); + + const mockUniswapRouter = await deployMockUniswapRouter(); + + const adapterParams: [string, string, string] = [ + addressesProvider.address, + mockUniswapRouter.address, + mockTokens.WETH.address, + ]; + + await deployUniswapLiquiditySwapAdapter(adapterParams); + await deployUniswapRepayAdapter(adapterParams); + await deployFlashLiquidationAdapter(adapterParams); + + await deployWalletBalancerProvider(); + + const gateWay = await deployWETHGateway([mockTokens.WETH.address]); + await authorizeWETHGateway(gateWay.address, lendingPoolAddress); + + console.timeEnd('setup'); +}; + +before(async () => { + await rawBRE.run('set-DRE'); + const [deployer, secondaryWallet] = await getEthersSigners(); + const MAINNET_FORK = process.env.MAINNET_FORK === 'true'; + + if (MAINNET_FORK) { + await rawBRE.run('amm:mainnet'); + } else { + console.log('-> Deploying test environment...'); + await buildTestEnv(deployer, secondaryWallet); + } + + await initializeMakeSuite(); + console.log('\n***************'); + console.log('Setup and snapshot finished'); + console.log('***************\n'); +}); diff --git a/test-suites/test-amm/addresses-provider-registry.spec.ts b/test-suites/test-amm/addresses-provider-registry.spec.ts new file mode 100644 index 00000000..77198278 --- /dev/null +++ b/test-suites/test-amm/addresses-provider-registry.spec.ts @@ -0,0 +1,100 @@ +import { TestEnv, makeSuite } from './helpers/make-suite'; +import { ZERO_ADDRESS } from '../../helpers/constants'; +import { ProtocolErrors } from '../../helpers/types'; + +const { expect } = require('chai'); + +makeSuite('AddressesProviderRegistry', (testEnv: TestEnv) => { + it('Checks the addresses provider is added to the registry', async () => { + const { addressesProvider, registry } = testEnv; + + const providers = await registry.getAddressesProvidersList(); + + expect(providers.length).to.be.equal(1, 'Invalid length of the addresses providers list'); + expect(providers[0].toString()).to.be.equal( + addressesProvider.address, + ' Invalid addresses provider added to the list' + ); + }); + + it('tries to register an addresses provider with id 0', async () => { + const { users, registry } = testEnv; + const { LPAPR_INVALID_ADDRESSES_PROVIDER_ID } = ProtocolErrors; + + await expect(registry.registerAddressesProvider(users[2].address, '0')).to.be.revertedWith( + LPAPR_INVALID_ADDRESSES_PROVIDER_ID + ); + }); + + it('Registers a new mock addresses provider', async () => { + const { users, registry } = testEnv; + + //simulating an addresses provider using the users[1] wallet address + await registry.registerAddressesProvider(users[1].address, '2'); + + const providers = await registry.getAddressesProvidersList(); + + expect(providers.length).to.be.equal(2, 'Invalid length of the addresses providers list'); + expect(providers[1].toString()).to.be.equal( + users[1].address, + ' Invalid addresses provider added to the list' + ); + }); + + it('Removes the mock addresses provider', async () => { + const { users, registry, addressesProvider } = testEnv; + + const id = await registry.getAddressesProviderIdByAddress(users[1].address); + + expect(id).to.be.equal('2', 'Invalid isRegistered return value'); + + await registry.unregisterAddressesProvider(users[1].address); + + const providers = await registry.getAddressesProvidersList(); + + expect(providers.length).to.be.equal(2, 'Invalid length of the addresses providers list'); + expect(providers[0].toString()).to.be.equal( + addressesProvider.address, + ' Invalid addresses provider added to the list' + ); + expect(providers[1].toString()).to.be.equal(ZERO_ADDRESS, ' Invalid addresses'); + }); + + it('Tries to remove a unregistered addressesProvider', async () => { + const { LPAPR_PROVIDER_NOT_REGISTERED } = ProtocolErrors; + + const { users, registry } = testEnv; + + await expect(registry.unregisterAddressesProvider(users[2].address)).to.be.revertedWith( + LPAPR_PROVIDER_NOT_REGISTERED + ); + }); + + it('Tries to remove a unregistered addressesProvider', async () => { + const { LPAPR_PROVIDER_NOT_REGISTERED } = ProtocolErrors; + + const { users, registry } = testEnv; + + await expect(registry.unregisterAddressesProvider(users[2].address)).to.be.revertedWith( + LPAPR_PROVIDER_NOT_REGISTERED + ); + }); + + it('Tries to add an already added addressesProvider with a different id. Should overwrite the previous id', async () => { + const { users, registry, addressesProvider } = testEnv; + + await registry.registerAddressesProvider(addressesProvider.address, '2'); + + const providers = await registry.getAddressesProvidersList(); + + const id = await registry.getAddressesProviderIdByAddress(addressesProvider.address); + + expect(providers.length).to.be.equal(2, 'Invalid length of the addresses providers list'); + + expect(providers[0].toString()).to.be.equal( + addressesProvider.address, + ' Invalid addresses provider added to the list' + ); + expect(providers[1].toString()).to.be.equal(ZERO_ADDRESS, ' Invalid addresses'); + }); +}); diff --git a/test-suites/test-amm/atoken-modifiers.spec.ts b/test-suites/test-amm/atoken-modifiers.spec.ts new file mode 100644 index 00000000..f1ae1199 --- /dev/null +++ b/test-suites/test-amm/atoken-modifiers.spec.ts @@ -0,0 +1,35 @@ +import { expect } from 'chai'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors } from '../../helpers/types'; + +makeSuite('AToken: Modifiers', (testEnv: TestEnv) => { + const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors; + + it('Tries to invoke mint not being the LendingPool', async () => { + const { deployer, aDai } = testEnv; + await expect(aDai.mint(deployer.address, '1', '1')).to.be.revertedWith( + CT_CALLER_MUST_BE_LENDING_POOL + ); + }); + + it('Tries to invoke burn not being the LendingPool', async () => { + const { deployer, aDai } = testEnv; + await expect(aDai.burn(deployer.address, deployer.address, '1', '1')).to.be.revertedWith( + CT_CALLER_MUST_BE_LENDING_POOL + ); + }); + + it('Tries to invoke transferOnLiquidation not being the LendingPool', async () => { + const { deployer, users, aDai } = testEnv; + await expect( + aDai.transferOnLiquidation(deployer.address, users[0].address, '1') + ).to.be.revertedWith(CT_CALLER_MUST_BE_LENDING_POOL); + }); + + it('Tries to invoke transferUnderlyingTo not being the LendingPool', async () => { + const { deployer, aDai } = testEnv; + await expect(aDai.transferUnderlyingTo(deployer.address, '1')).to.be.revertedWith( + CT_CALLER_MUST_BE_LENDING_POOL + ); + }); +}); diff --git a/test-suites/test-amm/atoken-permit.spec.ts b/test-suites/test-amm/atoken-permit.spec.ts new file mode 100644 index 00000000..24e8b39c --- /dev/null +++ b/test-suites/test-amm/atoken-permit.spec.ts @@ -0,0 +1,312 @@ +import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../../helpers/constants'; +import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +import { buildPermitParams, getSignatureFromTypedData } from '../../helpers/contracts-helpers'; +import { expect } from 'chai'; +import { ethers } from 'ethers'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { DRE } from '../../helpers/misc-utils'; +import { waitForTx } from '../../helpers/misc-utils'; +import { _TypedDataEncoder } from 'ethers/lib/utils'; + +const { parseEther } = ethers.utils; + +makeSuite('AToken: Permit', (testEnv: TestEnv) => { + it('Checks the domain separator', async () => { + const { aDai } = testEnv; + const separator = await aDai.DOMAIN_SEPARATOR(); + + const domain = { + name: await aDai.name(), + version: '1', + chainId: DRE.network.config.chainId, + verifyingContract: aDai.address, + }; + const domainSeparator = _TypedDataEncoder.hashDomain(domain); + + expect(separator).to.be.equal(domainSeparator, 'Invalid domain separator'); + }); + + it('Get aDAI for tests', async () => { + const { dai, pool, deployer } = testEnv; + + await dai.mint(parseEther('20000')); + await dai.approve(pool.address, parseEther('20000')); + + await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + }); + + it('Reverts submitting a permit with 0 expiration', async () => { + const { aDai, deployer, users } = testEnv; + const owner = deployer; + const spender = users[1]; + + const tokenName = await aDai.name(); + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const expiration = 0; + const nonce = (await aDai._nonces(owner.address)).toNumber(); + const permitAmount = ethers.utils.parseEther('2').toString(); + const msgParams = buildPermitParams( + chainId, + aDai.address, + '1', + tokenName, + owner.address, + spender.address, + nonce, + permitAmount, + expiration.toFixed() + ); + + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal( + '0', + 'INVALID_ALLOWANCE_BEFORE_PERMIT' + ); + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await expect( + aDai + .connect(spender.signer) + .permit(owner.address, spender.address, permitAmount, expiration, v, r, s) + ).to.be.revertedWith('INVALID_EXPIRATION'); + + expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal( + '0', + 'INVALID_ALLOWANCE_AFTER_PERMIT' + ); + }); + + it('Submits a permit with maximum expiration length', async () => { + const { aDai, deployer, users } = testEnv; + const owner = deployer; + const spender = users[1]; + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aDai._nonces(owner.address)).toNumber(); + const permitAmount = parseEther('2').toString(); + const msgParams = buildPermitParams( + chainId, + aDai.address, + '1', + await aDai.name(), + owner.address, + spender.address, + nonce, + deadline, + permitAmount + ); + + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal( + '0', + 'INVALID_ALLOWANCE_BEFORE_PERMIT' + ); + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await waitForTx( + await aDai + .connect(spender.signer) + .permit(owner.address, spender.address, permitAmount, deadline, v, r, s) + ); + + expect((await aDai._nonces(owner.address)).toNumber()).to.be.equal(1); + }); + + it('Cancels the previous permit', async () => { + const { aDai, deployer, users } = testEnv; + const owner = deployer; + const spender = users[1]; + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aDai._nonces(owner.address)).toNumber(); + const permitAmount = '0'; + const msgParams = buildPermitParams( + chainId, + aDai.address, + '1', + await aDai.name(), + owner.address, + spender.address, + nonce, + deadline, + permitAmount + ); + + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal( + ethers.utils.parseEther('2'), + 'INVALID_ALLOWANCE_BEFORE_PERMIT' + ); + + await waitForTx( + await aDai + .connect(spender.signer) + .permit(owner.address, spender.address, permitAmount, deadline, v, r, s) + ); + expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal( + permitAmount, + 'INVALID_ALLOWANCE_AFTER_PERMIT' + ); + + expect((await aDai._nonces(owner.address)).toNumber()).to.be.equal(2); + }); + + it('Tries to submit a permit with invalid nonce', async () => { + const { aDai, deployer, users } = testEnv; + const owner = deployer; + const spender = users[1]; + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = 1000; + const permitAmount = '0'; + const msgParams = buildPermitParams( + chainId, + aDai.address, + '1', + await aDai.name(), + owner.address, + spender.address, + nonce, + deadline, + permitAmount + ); + + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await expect( + aDai + .connect(spender.signer) + .permit(owner.address, spender.address, permitAmount, deadline, v, r, s) + ).to.be.revertedWith('INVALID_SIGNATURE'); + }); + + it('Tries to submit a permit with invalid expiration (previous to the current block)', async () => { + const { aDai, deployer, users } = testEnv; + const owner = deployer; + const spender = users[1]; + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const expiration = '1'; + const nonce = (await aDai._nonces(owner.address)).toNumber(); + const permitAmount = '0'; + const msgParams = buildPermitParams( + chainId, + aDai.address, + '1', + await aDai.name(), + owner.address, + spender.address, + nonce, + expiration, + permitAmount + ); + + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await expect( + aDai + .connect(spender.signer) + .permit(owner.address, spender.address, expiration, permitAmount, v, r, s) + ).to.be.revertedWith('INVALID_EXPIRATION'); + }); + + it('Tries to submit a permit with invalid signature', async () => { + const { aDai, deployer, users } = testEnv; + const owner = deployer; + const spender = users[1]; + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aDai._nonces(owner.address)).toNumber(); + const permitAmount = '0'; + const msgParams = buildPermitParams( + chainId, + aDai.address, + '1', + await aDai.name(), + owner.address, + spender.address, + nonce, + deadline, + permitAmount + ); + + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await expect( + aDai + .connect(spender.signer) + .permit(owner.address, ZERO_ADDRESS, permitAmount, deadline, v, r, s) + ).to.be.revertedWith('INVALID_SIGNATURE'); + }); + + it('Tries to submit a permit with invalid owner', async () => { + const { aDai, deployer, users } = testEnv; + const owner = deployer; + const spender = users[1]; + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const expiration = MAX_UINT_AMOUNT; + const nonce = (await aDai._nonces(owner.address)).toNumber(); + const permitAmount = '0'; + const msgParams = buildPermitParams( + chainId, + aDai.address, + '1', + await aDai.name(), + owner.address, + spender.address, + nonce, + expiration, + permitAmount + ); + + const ownerPrivateKey = require('../../test-wallets.js').accounts[0].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await expect( + aDai + .connect(spender.signer) + .permit(ZERO_ADDRESS, spender.address, expiration, permitAmount, v, r, s) + ).to.be.revertedWith('INVALID_OWNER'); + }); +}); diff --git a/test-suites/test-amm/atoken-transfer.spec.ts b/test-suites/test-amm/atoken-transfer.spec.ts new file mode 100644 index 00000000..0290f941 --- /dev/null +++ b/test-suites/test-amm/atoken-transfer.spec.ts @@ -0,0 +1,99 @@ +import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { expect } from 'chai'; +import { ethers } from 'ethers'; +import { RateMode, ProtocolErrors } from '../../helpers/types'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { CommonsConfig } from '../../markets/amm/commons'; + +const AAVE_REFERRAL = CommonsConfig.ProtocolGlobalParams.AaveReferral; + +makeSuite('AToken: Transfer', (testEnv: TestEnv) => { + const { + INVALID_FROM_BALANCE_AFTER_TRANSFER, + INVALID_TO_BALANCE_AFTER_TRANSFER, + VL_TRANSFER_NOT_ALLOWED, + } = ProtocolErrors; + + it('User 0 deposits 1000 DAI, transfers to user 1', async () => { + const { users, pool, dai, aDai } = testEnv; + + await dai.connect(users[0].signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //user 1 deposits 1000 DAI + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + await aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit); + + const name = await aDai.name(); + + expect(name).to.be.equal('Aave AMM Market DAI'); + + const fromBalance = await aDai.balanceOf(users[0].address); + const toBalance = await aDai.balanceOf(users[1].address); + + expect(fromBalance.toString()).to.be.equal('0', INVALID_FROM_BALANCE_AFTER_TRANSFER); + expect(toBalance.toString()).to.be.equal( + amountDAItoDeposit.toString(), + INVALID_TO_BALANCE_AFTER_TRANSFER + ); + }); + + it('User 0 deposits 1 WETH and user 1 tries to borrow the WETH variable with the received DAI as collateral', async () => { + const { users, pool, weth, helpersContract } = testEnv; + const userAddress = await pool.signer.getAddress(); + + await weth.connect(users[0].signer).mint(await convertToCurrencyDecimals(weth.address, '1')); + + await weth.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool + .connect(users[0].signer) + .deposit(weth.address, ethers.utils.parseEther('1.0'), userAddress, '0'); + await pool + .connect(users[1].signer) + .borrow( + weth.address, + ethers.utils.parseEther('0.1'), + RateMode.Variable, + AAVE_REFERRAL, + users[1].address + ); + + const userReserveData = await helpersContract.getUserReserveData( + weth.address, + users[1].address + ); + + expect(userReserveData.currentVariableDebt.toString()).to.be.eq(ethers.utils.parseEther('0.1')); + }); + + it('User 1 tries to transfer all the DAI used as collateral back to user 0 (revert expected)', async () => { + const { users, pool, aDai, dai, weth } = testEnv; + + const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '1000'); + + await expect( + aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer), + VL_TRANSFER_NOT_ALLOWED + ).to.be.revertedWith(VL_TRANSFER_NOT_ALLOWED); + }); + + it('User 1 tries to transfer a small amount of DAI used as collateral back to user 0', async () => { + const { users, pool, aDai, dai, weth } = testEnv; + + const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '100'); + + await aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer); + + const user0Balance = await aDai.balanceOf(users[0].address); + + expect(user0Balance.toString()).to.be.eq(aDAItoTransfer.toString()); + }); +}); diff --git a/test-suites/test-amm/configurator.spec.ts b/test-suites/test-amm/configurator.spec.ts new file mode 100644 index 00000000..e4e3f4fa --- /dev/null +++ b/test-suites/test-amm/configurator.spec.ts @@ -0,0 +1,412 @@ +import { TestEnv, makeSuite } from './helpers/make-suite'; +import { APPROVAL_AMOUNT_LENDING_POOL, RAY } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { ProtocolErrors } from '../../helpers/types'; +import { strategyWETH } from '../../markets/amm/reservesConfigs'; + +const { expect } = require('chai'); + +makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { + const { + CALLER_NOT_POOL_ADMIN, + LPC_RESERVE_LIQUIDITY_NOT_0, + RC_INVALID_LTV, + RC_INVALID_LIQ_THRESHOLD, + RC_INVALID_LIQ_BONUS, + RC_INVALID_DECIMALS, + RC_INVALID_RESERVE_FACTOR, + } = ProtocolErrors; + + it('Reverts trying to set an invalid reserve factor', async () => { + const { configurator, weth } = testEnv; + + const invalidReserveFactor = 65536; + + await expect( + configurator.setReserveFactor(weth.address, invalidReserveFactor) + ).to.be.revertedWith(RC_INVALID_RESERVE_FACTOR); + }); + + it('Deactivates the ETH reserve', async () => { + const { configurator, weth, helpersContract } = testEnv; + await configurator.deactivateReserve(weth.address); + const { isActive } = await helpersContract.getReserveConfigurationData(weth.address); + expect(isActive).to.be.equal(false); + }); + + it('Rectivates the ETH reserve', async () => { + const { configurator, weth, helpersContract } = testEnv; + await configurator.activateReserve(weth.address); + + const { isActive } = await helpersContract.getReserveConfigurationData(weth.address); + expect(isActive).to.be.equal(true); + }); + + it('Check the onlyAaveAdmin on deactivateReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).deactivateReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Check the onlyAaveAdmin on activateReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).activateReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Freezes the ETH reserve', async () => { + const { configurator, weth, helpersContract } = testEnv; + + await configurator.freezeReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(true); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Unfreezes the ETH reserve', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.unfreezeReserve(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Check the onlyAaveAdmin on freezeReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).freezeReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Check the onlyAaveAdmin on unfreezeReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).unfreezeReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Deactivates the ETH reserve for borrowing', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.disableBorrowingOnReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(false); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Activates the ETH reserve for borrowing', async () => { + const { configurator, weth, helpersContract } = testEnv; + await configurator.enableBorrowingOnReserve(weth.address, true); + const { variableBorrowIndex } = await helpersContract.getReserveData(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(true/*strategyWETH.stableBorrowRateEnabled*/); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + + expect(variableBorrowIndex.toString()).to.be.equal(RAY); + }); + + it('Check the onlyAaveAdmin on disableBorrowingOnReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).disableBorrowingOnReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Check the onlyAaveAdmin on enableBorrowingOnReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).enableBorrowingOnReserve(weth.address, true), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Deactivates the ETH reserve as collateral', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.configureReserveAsCollateral(weth.address, 0, 0, 0); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(18); + expect(ltv).to.be.equal(0); + expect(liquidationThreshold).to.be.equal(0); + expect(liquidationBonus).to.be.equal(0); + expect(stableBorrowRateEnabled).to.be.equal(true); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Activates the ETH reserve as collateral', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.configureReserveAsCollateral(weth.address, '8000', '8250', '10500'); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(true/*strategyWETH.stableBorrowRateEnabled*/); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Check the onlyAaveAdmin on configureReserveAsCollateral ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator + .connect(users[2].signer) + .configureReserveAsCollateral(weth.address, '7500', '8000', '10500'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Disable stable borrow rate on the ETH reserve', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.disableReserveStableRate(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(false); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Enables stable borrow rate on the ETH reserve', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.enableReserveStableRate(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(true); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Disable stable borrow rate to return to the original state on the ETH reserve', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.disableReserveStableRate(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Check the onlyAaveAdmin on disableReserveStableRate', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).disableReserveStableRate(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Check the onlyAaveAdmin on enableReserveStableRate', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).enableReserveStableRate(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Changes the reserve factor of WETH', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.setReserveFactor(weth.address, '1000'); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(1000); + }); + + it('Check the onlyLendingPoolManager on setReserveFactor', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).setReserveFactor(weth.address, '2000'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Reverts when trying to disable the DAI reserve with liquidity on it', async () => { + const { dai, pool, configurator } = testEnv; + const userAddress = await pool.signer.getAddress(); + await dai.mint(await convertToCurrencyDecimals(dai.address, '1000')); + + //approve protocol to access depositor wallet + await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + //user 1 deposits 1000 DAI + await pool.deposit(dai.address, amountDAItoDeposit, userAddress, '0'); + + await expect( + configurator.deactivateReserve(dai.address), + LPC_RESERVE_LIQUIDITY_NOT_0 + ).to.be.revertedWith(LPC_RESERVE_LIQUIDITY_NOT_0); + }); +}); diff --git a/test/delegation-aware-atoken.spec.ts b/test-suites/test-amm/delegation-aware-atoken.spec.ts similarity index 59% rename from test/delegation-aware-atoken.spec.ts rename to test-suites/test-amm/delegation-aware-atoken.spec.ts index 0e782495..73ca6883 100644 --- a/test/delegation-aware-atoken.spec.ts +++ b/test-suites/test-amm/delegation-aware-atoken.spec.ts @@ -1,24 +1,26 @@ -import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../helpers/constants'; -import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; -import { buildPermitParams, getSignatureFromTypedData } from '../helpers/contracts-helpers'; +import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../../helpers/constants'; +import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +import { buildPermitParams, getSignatureFromTypedData } from '../../helpers/contracts-helpers'; import { expect } from 'chai'; import { ethers } from 'ethers'; -import { eEthereumNetwork, ProtocolErrors } from '../helpers/types'; +import { eEthereumNetwork, ProtocolErrors } from '../../helpers/types'; import { makeSuite, TestEnv } from './helpers/make-suite'; -import { DRE } from '../helpers/misc-utils'; +import { DRE } from '../../helpers/misc-utils'; import { ConfigNames, getATokenDomainSeparatorPerNetwork, + getTreasuryAddress, loadPoolConfig, -} from '../helpers/configuration'; -import { waitForTx } from '../helpers/misc-utils'; +} from '../../helpers/configuration'; +import { waitForTx } from '../../helpers/misc-utils'; import { deployDelegationAwareAToken, deployMintableDelegationERC20, -} from '../helpers/contracts-deployments'; -import { DelegationAwareATokenFactory } from '../types'; -import { DelegationAwareAToken } from '../types/DelegationAwareAToken'; -import { MintableDelegationERC20 } from '../types/MintableDelegationERC20'; +} from '../../helpers/contracts-deployments'; +import { DelegationAwareATokenFactory } from '../../types'; +import { DelegationAwareAToken } from '../../types/DelegationAwareAToken'; +import { MintableDelegationERC20 } from '../../types/MintableDelegationERC20'; +import AmmConfig from '../../markets/amm'; const { parseEther } = ethers.utils; @@ -33,9 +35,13 @@ makeSuite('AToken: underlying delegation', (testEnv: TestEnv) => { delegationERC20 = await deployMintableDelegationERC20(['DEL', 'DEL', '18']); delegationAToken = await deployDelegationAwareAToken( - [pool.address, delegationERC20.address, ZERO_ADDRESS, 'aDEL', 'aDEL', ZERO_ADDRESS], + [pool.address, delegationERC20.address, await getTreasuryAddress(AmmConfig), ZERO_ADDRESS, 'aDEL', 'aDEL'], false ); + + //await delegationAToken.initialize(pool.address, ZERO_ADDRESS, delegationERC20.address, ZERO_ADDRESS, '18', 'aDEL', 'aDEL'); + + console.log((await delegationAToken.decimals()).toString()); }); it('Tries to delegate with the caller not being the Aave admin', async () => { diff --git a/test-suites/test-amm/flashloan.spec.ts b/test-suites/test-amm/flashloan.spec.ts new file mode 100644 index 00000000..22e9ade2 --- /dev/null +++ b/test-suites/test-amm/flashloan.spec.ts @@ -0,0 +1,609 @@ +import BigNumber from 'bignumber.js'; + +import { TestEnv, makeSuite } from './helpers/make-suite'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../../helpers/constants'; +import { convertToCurrencyDecimals, getContract } from '../../helpers/contracts-helpers'; +import { ethers } from 'ethers'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { ProtocolErrors, eContractid } from '../../helpers/types'; +import { VariableDebtToken } from '../../types/VariableDebtToken'; +import { StableDebtToken } from '../../types/StableDebtToken'; +import { + getMockFlashLoanReceiver, + getStableDebtToken, + getVariableDebtToken, +} from '../../helpers/contracts-getters'; + +const { expect } = require('chai'); + +makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { + let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; + const { + VL_COLLATERAL_BALANCE_IS_0, + TRANSFER_AMOUNT_EXCEEDS_BALANCE, + LP_INVALID_FLASHLOAN_MODE, + VL_STABLE_BORROWING_NOT_ENABLED, + SAFEERC20_LOWLEVEL_CALL, + LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN, + LP_BORROW_ALLOWANCE_NOT_ENOUGH, + } = ProtocolErrors; + + before(async () => { + _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); + }); + + it('Deposits WETH into the reserve', async () => { + const { pool, weth } = testEnv; + const userAddress = await pool.signer.getAddress(); + const amountToDeposit = ethers.utils.parseEther('1'); + + await weth.mint(amountToDeposit); + + await weth.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool.deposit(weth.address, amountToDeposit, userAddress, '0'); + }); + + it('Takes WETH flashloan with mode = 0, returns the funds correctly', async () => { + const { pool, helpersContract, weth } = testEnv; + + await pool.flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [0], + _mockFlashLoanReceiver.address, + '0x10', + '0' + ); + + ethers.utils.parseUnits('10000'); + + const reserveData = await helpersContract.getReserveData(weth.address); + + const currentLiquidityRate = reserveData.liquidityRate; + const currentLiquidityIndex = reserveData.liquidityIndex; + + const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + expect(totalLiquidity.toString()).to.be.equal('1000720000000000000'); + expect(currentLiquidityRate.toString()).to.be.equal('0'); + expect(currentLiquidityIndex.toString()).to.be.equal('1000720000000000000000000000'); + }); + + it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => { + const { pool, helpersContract, weth } = testEnv; + + const reserveDataBefore = await helpersContract.getReserveData(weth.address); + const txResult = await pool.flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + ['1000720000000000000'], + [0], + _mockFlashLoanReceiver.address, + '0x10', + '0' + ); + + const reserveData = await helpersContract.getReserveData(weth.address); + + const currentLiqudityRate = reserveData.liquidityRate; + const currentLiquidityIndex = reserveData.liquidityIndex; + + const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + expect(totalLiquidity.toString()).to.be.equal('1001620648000000000'); + expect(currentLiqudityRate.toString()).to.be.equal('0'); + expect(currentLiquidityIndex.toString()).to.be.equal('1001620648000000000000000000'); + }); + + it('Takes WETH flashloan, does not return the funds with mode = 0. (revert expected)', async () => { + const { pool, weth, users } = testEnv; + const caller = users[1]; + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [0], + caller.address, + '0x10', + '0' + ) + ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); + }); + + it('Takes WETH flashloan, simulating a receiver as EOA (revert expected)', async () => { + const { pool, weth, users } = testEnv; + const caller = users[1]; + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + await _mockFlashLoanReceiver.setSimulateEOA(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [0], + caller.address, + '0x10', + '0' + ) + ).to.be.revertedWith(LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN); + }); + + it('Takes a WETH flashloan with an invalid mode. (revert expected)', async () => { + const { pool, weth, users } = testEnv; + const caller = users[1]; + await _mockFlashLoanReceiver.setSimulateEOA(false); + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [4], + caller.address, + '0x10', + '0' + ) + ).to.be.reverted; + }); + + it('Caller deposits 1000 DAI as collateral, Takes WETH flashloan with mode = 2, does not return the funds. A variable loan for caller is created', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[1]; + + await dai.connect(caller.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + await dai.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool.connect(caller.signer).deposit(dai.address, amountToDeposit, caller.address, '0'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [2], + caller.address, + '0x10', + '0' + ); + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const wethDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + const callerDebt = await wethDebtToken.balanceOf(caller.address); + + expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt'); + }); + + it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => { + const { pool, weth, users } = testEnv; + const caller = users[1]; + + await expect( + pool.connect(caller.signer).flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + ['1004415000000000000'], //slightly higher than the available liquidity + [2], + caller.address, + '0x10', + '0' + ), + TRANSFER_AMOUNT_EXCEEDS_BALANCE + ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); + }); + + it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => { + const { pool, deployer, weth, users } = testEnv; + const caller = users[1]; + + await expect( + pool.flashLoan( + deployer.address, + [weth.address], + ['1000000000000000000'], + [2], + caller.address, + '0x10', + '0' + ) + ).to.be.reverted; + }); + + it('Deposits USDC into the reserve', async () => { + const { usdc, pool } = testEnv; + const userAddress = await pool.signer.getAddress(); + + await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(usdc.address, '1000'); + + await pool.deposit(usdc.address, amountToDeposit, userAddress, '0'); + }); + + it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => { + const { usdc, pool, helpersContract, deployer: depositor } = testEnv; + + await _mockFlashLoanReceiver.setFailExecutionTransfer(false); + + const reserveDataBefore = await helpersContract.getReserveData(usdc.address); + + const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500'); + + await pool.flashLoan( + _mockFlashLoanReceiver.address, + [usdc.address], + [flashloanAmount], + [0], + _mockFlashLoanReceiver.address, + '0x10', + '0' + ); + + const reserveDataAfter = helpersContract.getReserveData(usdc.address); + + const reserveData = await helpersContract.getReserveData(usdc.address); + const userData = await helpersContract.getUserReserveData(usdc.address, depositor.address); + + const totalLiquidity = reserveData.availableLiquidity + .add(reserveData.totalStableDebt) + .add(reserveData.totalVariableDebt) + .toString(); + const currentLiqudityRate = reserveData.liquidityRate.toString(); + const currentLiquidityIndex = reserveData.liquidityIndex.toString(); + const currentUserBalance = userData.currentATokenBalance.toString(); + + const expectedLiquidity = await convertToCurrencyDecimals(usdc.address, '1000.450'); + + expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity'); + expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate'); + expect(currentLiquidityIndex).to.be.equal( + new BigNumber('1.00045').multipliedBy(oneRay).toFixed(), + 'Invalid liquidity index' + ); + expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance'); + }); + + it('Takes out a 500 USDC flashloan with mode = 0, does not return the funds. (revert expected)', async () => { + const { usdc, pool, users } = testEnv; + const caller = users[2]; + + const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [usdc.address], + [flashloanAmount], + [2], + caller.address, + '0x10', + '0' + ) + ).to.be.revertedWith(VL_COLLATERAL_BALANCE_IS_0); + }); + + it('Caller deposits 5 WETH as collateral, Takes a USDC flashloan with mode = 2, does not return the funds. A loan for caller is created', async () => { + const { usdc, pool, weth, users, helpersContract } = testEnv; + + const caller = users[2]; + + await weth.connect(caller.signer).mint(await convertToCurrencyDecimals(weth.address, '5')); + + await weth.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(weth.address, '5'); + + await pool.connect(caller.signer).deposit(weth.address, amountToDeposit, caller.address, '0'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500'); + + await pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [usdc.address], + [flashloanAmount], + [2], + caller.address, + '0x10', + '0' + ); + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + usdc.address + ); + + const usdcDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + const callerDebt = await usdcDebtToken.balanceOf(caller.address); + + expect(callerDebt.toString()).to.be.equal('500000000', 'Invalid user debt'); + }); + + it('Caller deposits 1000 DAI as collateral, Takes a WETH flashloan with mode = 0, does not approve the transfer of the funds', async () => { + const { dai, pool, weth, users } = testEnv; + const caller = users[3]; + + await dai.connect(caller.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + await dai.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool.connect(caller.signer).deposit(dai.address, amountToDeposit, caller.address, '0'); + + const flashAmount = ethers.utils.parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(false); + await _mockFlashLoanReceiver.setAmountToApprove(flashAmount.div(2)); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [0], + caller.address, + '0x10', + '0' + ) + ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); + }); + + it('Caller takes a WETH flashloan with mode = 1, should revert since stable borrowing is disabled', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[3]; + + const flashAmount = ethers.utils.parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect(pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [1], + caller.address, + '0x10', + '0' + )).to.be.revertedWith(VL_STABLE_BORROWING_NOT_ENABLED); + + const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const wethDebtToken = await getStableDebtToken(stableDebtTokenAddress); + + const callerDebt = await wethDebtToken.balanceOf(caller.address); + + expect(callerDebt.toString()).to.be.equal('0', 'Invalid user debt'); + }); + + it('Caller takes a WETH flashloan with mode = 2', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[3]; + + const flashAmount = ethers.utils.parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [2], + caller.address, + '0x10', + '0' + ); + + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const wethDebtToken = await getStableDebtToken(variableDebtTokenAddress); + + const callerDebt = await wethDebtToken.balanceOf(caller.address); + + expect(callerDebt.toString()).to.be.equal(ethers.utils.parseEther('0.8'), 'Invalid user debt'); + }); + + it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user without allowance, should revert since stable borrowing is disabled', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[5]; + const onBehalfOf = users[4]; + + // Deposit 1000 dai for onBehalfOf user + await dai.connect(onBehalfOf.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + await dai.connect(onBehalfOf.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool + .connect(onBehalfOf.signer) + .deposit(dai.address, amountToDeposit, onBehalfOf.address, '0'); + + const flashAmount = ethers.utils.parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [1], + onBehalfOf.address, + '0x10', + '0' + ) + ).to.be.revertedWith(VL_STABLE_BORROWING_NOT_ENABLED); + }); + + it('Caller takes a WETH flashloan with mode = 2 onBehalfOf user without allowance, should revert since allowance is 0', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[5]; + const onBehalfOf = users[4]; + + // Deposit 1000 dai for onBehalfOf user + await dai.connect(onBehalfOf.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + await dai.connect(onBehalfOf.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool + .connect(onBehalfOf.signer) + .deposit(dai.address, amountToDeposit, onBehalfOf.address, '0'); + + const flashAmount = ethers.utils.parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [2], + onBehalfOf.address, + '0x10', + '0' + ) + ).to.be.revertedWith(LP_BORROW_ALLOWANCE_NOT_ENOUGH); + }); + + it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user with allowance. Should revert since stable borrowing is disabled.', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[5]; + const onBehalfOf = users[4]; + + const flashAmount = ethers.utils.parseEther('0.8'); + + const reserveData = await pool.getReserveData(weth.address); + + const stableDebtToken = await getStableDebtToken(reserveData.stableDebtTokenAddress); + + // Deposited for onBehalfOf user already, delegate borrow allowance + await stableDebtToken.connect(onBehalfOf.signer).approveDelegation(caller.address, flashAmount); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect(pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [1], + onBehalfOf.address, + '0x10', + '0' + )).to.be.revertedWith(VL_STABLE_BORROWING_NOT_ENABLED); + + const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const wethDebtToken = await getStableDebtToken(stableDebtTokenAddress); + + const onBehalfOfDebt = await wethDebtToken.balanceOf(onBehalfOf.address); + + expect(onBehalfOfDebt.toString()).to.be.equal( + '0', + 'Invalid onBehalfOf user debt' + ); + }); + + it('Caller takes a WETH flashloan with mode = 2 onBehalfOf user with allowance. A loan for onBehalfOf is created.', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[5]; + const onBehalfOf = users[4]; + + const flashAmount = ethers.utils.parseEther('0.8'); + + const reserveData = await pool.getReserveData(weth.address); + + const variableDebtToken = await getVariableDebtToken(reserveData.variableDebtTokenAddress); + + // Deposited for onBehalfOf user already, delegate borrow allowance + await variableDebtToken.connect(onBehalfOf.signer).approveDelegation(caller.address, flashAmount); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect(pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [2], + onBehalfOf.address, + '0x10', + '0' + )).to.not.be.reverted; + + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const wethDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + const onBehalfOfDebt = await wethDebtToken.balanceOf(onBehalfOf.address); + + expect(onBehalfOfDebt.toString()).to.be.equal( + ethers.utils.parseEther('0.8'), + 'Invalid onBehalfOf user debt' + ); + }); +}); diff --git a/test/helpers/actions.ts b/test-suites/test-amm/helpers/actions.ts similarity index 98% rename from test/helpers/actions.ts rename to test-suites/test-amm/helpers/actions.ts index c41c92e5..62f9f8e3 100644 --- a/test/helpers/actions.ts +++ b/test-suites/test-amm/helpers/actions.ts @@ -17,22 +17,22 @@ import { } from './utils/calculations'; import { getReserveAddressFromSymbol, getReserveData, getUserData } from './utils/helpers'; -import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { convertToCurrencyDecimals } from '../../../helpers/contracts-helpers'; import { getAToken, getMintableERC20, getStableDebtToken, getVariableDebtToken, -} from '../../helpers/contracts-getters'; -import { MAX_UINT_AMOUNT, ONE_YEAR } from '../../helpers/constants'; +} from '../../../helpers/contracts-getters'; +import { MAX_UINT_AMOUNT, ONE_YEAR } from '../../../helpers/constants'; import { SignerWithAddress, TestEnv } from './make-suite'; -import { advanceTimeAndBlock, DRE, timeLatest, waitForTx } from '../../helpers/misc-utils'; +import { advanceTimeAndBlock, DRE, timeLatest, waitForTx } from '../../../helpers/misc-utils'; import chai from 'chai'; import { ReserveData, UserReserveData } from './utils/interfaces'; import { ContractReceipt } from 'ethers'; -import { AToken } from '../../types/AToken'; -import { RateMode, tEthereumAddress } from '../../helpers/types'; +import { AToken } from '../../../types/AToken'; +import { RateMode, tEthereumAddress } from '../../../helpers/types'; const { expect } = chai; @@ -161,6 +161,7 @@ export const deposit = async ( if (sendValue) { txOptions.value = await convertToCurrencyDecimals(reserve, sendValue); } + if (expectedResult === 'success') { const txResult = await waitForTx( await pool diff --git a/test-suites/test-amm/helpers/almost-equal.ts b/test-suites/test-amm/helpers/almost-equal.ts new file mode 100644 index 00000000..e0017056 --- /dev/null +++ b/test-suites/test-amm/helpers/almost-equal.ts @@ -0,0 +1,31 @@ +import BigNumber from 'bignumber.js'; + +function almostEqualAssertion(this: any, expected: any, actual: any, message: string): any { + this.assert( + expected.plus(new BigNumber(1)).eq(actual) || + expected.plus(new BigNumber(2)).eq(actual) || + actual.plus(new BigNumber(1)).eq(expected) || + actual.plus(new BigNumber(2)).eq(expected) || + expected.eq(actual), + `${message} expected #{act} to be almost equal #{exp}`, + `${message} expected #{act} to be different from #{exp}`, + expected.toString(), + actual.toString() + ); +} + +export function almostEqual() { + return function (chai: any, utils: any) { + chai.Assertion.overwriteMethod('almostEqual', function (original: any) { + return function (this: any, value: any, message: string) { + if (utils.flag(this, 'bignumber')) { + var expected = new BigNumber(value); + var actual = new BigNumber(this._obj); + almostEqualAssertion.apply(this, [expected, actual, message]); + } else { + original.apply(this, arguments); + } + }; + }); + }; +} diff --git a/test-suites/test-amm/helpers/make-suite.ts b/test-suites/test-amm/helpers/make-suite.ts new file mode 100644 index 00000000..71d96bf5 --- /dev/null +++ b/test-suites/test-amm/helpers/make-suite.ts @@ -0,0 +1,191 @@ +import { evmRevert, evmSnapshot, DRE } from '../../../helpers/misc-utils'; +import { Signer } from 'ethers'; +import { + getLendingPool, + getLendingPoolAddressesProvider, + getAaveProtocolDataProvider, + getAToken, + getMintableERC20, + getLendingPoolConfiguratorProxy, + getPriceOracle, + getLendingPoolAddressesProviderRegistry, + getWETHMocked, + getWETHGateway, + getUniswapLiquiditySwapAdapter, + getUniswapRepayAdapter, + getFlashLiquidationAdapter, +} from '../../../helpers/contracts-getters'; +import { eEthereumNetwork, tEthereumAddress } from '../../../helpers/types'; +import { LendingPool } from '../../../types/LendingPool'; +import { AaveProtocolDataProvider } from '../../../types/AaveProtocolDataProvider'; +import { MintableERC20 } from '../../../types/MintableERC20'; +import { AToken } from '../../../types/AToken'; +import { LendingPoolConfigurator } from '../../../types/LendingPoolConfigurator'; + +import chai from 'chai'; +// @ts-ignore +import bignumberChai from 'chai-bignumber'; +import { almostEqual } from './almost-equal'; +import { PriceOracle } from '../../../types/PriceOracle'; +import { LendingPoolAddressesProvider } from '../../../types/LendingPoolAddressesProvider'; +import { LendingPoolAddressesProviderRegistry } from '../../../types/LendingPoolAddressesProviderRegistry'; +import { getEthersSigners } from '../../../helpers/contracts-helpers'; +import { UniswapLiquiditySwapAdapter } from '../../../types/UniswapLiquiditySwapAdapter'; +import { UniswapRepayAdapter } from '../../../types/UniswapRepayAdapter'; +import { getParamPerNetwork } from '../../../helpers/contracts-helpers'; +import { WETH9Mocked } from '../../../types/WETH9Mocked'; +import { WETHGateway } from '../../../types/WETHGateway'; +import { solidity } from 'ethereum-waffle'; +import { AmmConfig } from '../../../markets/amm'; +import { FlashLiquidationAdapter } from '../../../types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { usingTenderly } from '../../../helpers/tenderly-utils'; + +chai.use(bignumberChai()); +chai.use(almostEqual()); +chai.use(solidity); + +export interface SignerWithAddress { + signer: Signer; + address: tEthereumAddress; +} +export interface TestEnv { + deployer: SignerWithAddress; + users: SignerWithAddress[]; + pool: LendingPool; + configurator: LendingPoolConfigurator; + oracle: PriceOracle; + helpersContract: AaveProtocolDataProvider; + weth: WETH9Mocked; + aWETH: AToken; + dai: MintableERC20; + aDai: AToken; + usdc: MintableERC20; + aave: MintableERC20; + addressesProvider: LendingPoolAddressesProvider; + uniswapLiquiditySwapAdapter: UniswapLiquiditySwapAdapter; + uniswapRepayAdapter: UniswapRepayAdapter; + registry: LendingPoolAddressesProviderRegistry; + wethGateway: WETHGateway; + flashLiquidationAdapter: FlashLiquidationAdapter; +} + +let buidlerevmSnapshotId: string = '0x1'; +const setBuidlerevmSnapshotId = (id: string) => { + buidlerevmSnapshotId = id; +}; + +const testEnv: TestEnv = { + deployer: {} as SignerWithAddress, + users: [] as SignerWithAddress[], + pool: {} as LendingPool, + configurator: {} as LendingPoolConfigurator, + helpersContract: {} as AaveProtocolDataProvider, + oracle: {} as PriceOracle, + weth: {} as WETH9Mocked, + aWETH: {} as AToken, + dai: {} as MintableERC20, + aDai: {} as AToken, + usdc: {} as MintableERC20, + aave: {} as MintableERC20, + addressesProvider: {} as LendingPoolAddressesProvider, + uniswapLiquiditySwapAdapter: {} as UniswapLiquiditySwapAdapter, + uniswapRepayAdapter: {} as UniswapRepayAdapter, + flashLiquidationAdapter: {} as FlashLiquidationAdapter, + registry: {} as LendingPoolAddressesProviderRegistry, + wethGateway: {} as WETHGateway, +} as TestEnv; + +export async function initializeMakeSuite() { + const [_deployer, ...restSigners] = await getEthersSigners(); + const deployer: SignerWithAddress = { + address: await _deployer.getAddress(), + signer: _deployer, + }; + + for (const signer of restSigners) { + testEnv.users.push({ + signer, + address: await signer.getAddress(), + }); + } + testEnv.deployer = deployer; + testEnv.pool = await getLendingPool(); + + testEnv.configurator = await getLendingPoolConfiguratorProxy(); + + testEnv.addressesProvider = await getLendingPoolAddressesProvider(); + + if (process.env.MAINNET_FORK === 'true') { + testEnv.registry = await getLendingPoolAddressesProviderRegistry( + getParamPerNetwork(AmmConfig.ProviderRegistry, eEthereumNetwork.main) + ); + } else { + testEnv.registry = await getLendingPoolAddressesProviderRegistry(); + testEnv.oracle = await getPriceOracle(); + } + + testEnv.helpersContract = await getAaveProtocolDataProvider(); + + const allTokens = await testEnv.helpersContract.getAllATokens(); + const aDaiAddress = allTokens.find((aToken) => aToken.symbol === 'aAmmDAI')?.tokenAddress; + + const aWEthAddress = allTokens.find((aToken) => aToken.symbol === 'aAmmWETH')?.tokenAddress; + + const reservesTokens = await testEnv.helpersContract.getAllReservesTokens(); + + const daiAddress = reservesTokens.find((token) => token.symbol === 'DAI')?.tokenAddress; + const usdcAddress = reservesTokens.find((token) => token.symbol === 'USDC')?.tokenAddress; + const aaveAddress = reservesTokens.find((token) => token.symbol === 'UniAAVEWETH')?.tokenAddress; + const wethAddress = reservesTokens.find((token) => token.symbol === 'WETH')?.tokenAddress; + + if (!aDaiAddress || !aWEthAddress) { + process.exit(1); + } + if (!daiAddress || !usdcAddress || !aaveAddress || !wethAddress) { + process.exit(1); + } + + testEnv.aDai = await getAToken(aDaiAddress); + testEnv.aWETH = await getAToken(aWEthAddress); + + testEnv.dai = await getMintableERC20(daiAddress); + testEnv.usdc = await getMintableERC20(usdcAddress); + testEnv.aave = await getMintableERC20(aaveAddress); + testEnv.weth = await getWETHMocked(wethAddress); + testEnv.wethGateway = await getWETHGateway(); + + testEnv.uniswapLiquiditySwapAdapter = await getUniswapLiquiditySwapAdapter(); + testEnv.uniswapRepayAdapter = await getUniswapRepayAdapter(); + testEnv.flashLiquidationAdapter = await getFlashLiquidationAdapter(); +} + +const setSnapshot = async () => { + const hre = DRE as HardhatRuntimeEnvironment; + if (usingTenderly()) { + setBuidlerevmSnapshotId((await hre.tenderlyRPC.getHead()) || '0x1'); + return; + } + setBuidlerevmSnapshotId(await evmSnapshot()); +}; + +const revertHead = async () => { + const hre = DRE as HardhatRuntimeEnvironment; + if (usingTenderly()) { + await hre.tenderlyRPC.setHead(buidlerevmSnapshotId); + return; + } + await evmRevert(buidlerevmSnapshotId); +}; + +export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) { + describe(name, () => { + before(async () => { + await setSnapshot(); + }); + tests(testEnv); + after(async () => { + await revertHead(); + }); + }); +} diff --git a/test-suites/test-amm/helpers/scenario-engine.ts b/test-suites/test-amm/helpers/scenario-engine.ts new file mode 100644 index 00000000..492fa8b2 --- /dev/null +++ b/test-suites/test-amm/helpers/scenario-engine.ts @@ -0,0 +1,237 @@ +import { TestEnv, SignerWithAddress } from './make-suite'; +import { + mint, + approve, + deposit, + borrow, + withdraw, + repay, + setUseAsCollateral, + swapBorrowRateMode, + rebalanceStableBorrowRate, + delegateBorrowAllowance, +} from './actions'; +import { RateMode } from '../../../helpers/types'; + +export interface Action { + name: string; + args?: any; + expected: string; + revertMessage?: string; +} + +export interface Story { + description: string; + actions: Action[]; +} + +export interface Scenario { + title: string; + description: string; + stories: Story[]; +} + +export const executeStory = async (story: Story, testEnv: TestEnv) => { + for (const action of story.actions) { + const { users } = testEnv; + await executeAction(action, users, testEnv); + } +}; + +const executeAction = async (action: Action, users: SignerWithAddress[], testEnv: TestEnv) => { + const { reserve, user: userIndex, borrowRateMode } = action.args; + const { name, expected, revertMessage } = action; + + if (!name || name === '') { + throw 'Action name is missing'; + } + if (!reserve || reserve === '') { + throw 'Invalid reserve selected for deposit'; + } + if (!userIndex || userIndex === '') { + throw `Invalid user selected to deposit into the ${reserve} reserve`; + } + + if (!expected || expected === '') { + throw `An expected resut for action ${name} is required`; + } + + let rateMode: string = RateMode.None; + + if (borrowRateMode) { + if (borrowRateMode === 'none') { + rateMode = RateMode.None; + } else if (borrowRateMode === 'stable') { + rateMode = RateMode.Stable; + } else if (borrowRateMode === 'variable') { + rateMode = RateMode.Variable; + } else { + //random value, to test improper selection of the parameter + rateMode = '4'; + } + } + + const user = users[parseInt(userIndex)]; + + switch (name) { + case 'mint': + const { amount } = action.args; + + if (!amount || amount === '') { + throw `Invalid amount of ${reserve} to mint`; + } + + await mint(reserve, amount, user); + break; + + case 'approve': + await approve(reserve, user, testEnv); + break; + + case 'deposit': + { + const { amount, sendValue, onBehalfOf: onBehalfOfIndex } = action.args; + const onBehalfOf = onBehalfOfIndex + ? users[parseInt(onBehalfOfIndex)].address + : user.address; + + if (!amount || amount === '') { + throw `Invalid amount to deposit into the ${reserve} reserve`; + } + + await deposit( + reserve, + amount, + user, + onBehalfOf, + sendValue, + expected, + testEnv, + revertMessage + ); + } + break; + + case 'delegateBorrowAllowance': + { + const { amount, toUser: toUserIndex } = action.args; + const toUser = users[parseInt(toUserIndex, 10)].address; + if (!amount || amount === '') { + throw `Invalid amount to deposit into the ${reserve} reserve`; + } + + await delegateBorrowAllowance( + reserve, + amount, + rateMode, + user, + toUser, + expected, + testEnv, + revertMessage + ); + } + break; + + case 'withdraw': + { + const { amount } = action.args; + + if (!amount || amount === '') { + throw `Invalid amount to withdraw from the ${reserve} reserve`; + } + + await withdraw(reserve, amount, user, expected, testEnv, revertMessage); + } + break; + case 'borrow': + { + const { amount, timeTravel, onBehalfOf: onBehalfOfIndex } = action.args; + + const onBehalfOf = onBehalfOfIndex + ? users[parseInt(onBehalfOfIndex)].address + : user.address; + + if (!amount || amount === '') { + throw `Invalid amount to borrow from the ${reserve} reserve`; + } + + await borrow( + reserve, + amount, + rateMode, + user, + onBehalfOf, + timeTravel, + expected, + testEnv, + revertMessage + ); + } + break; + + case 'repay': + { + const { amount, borrowRateMode, sendValue } = action.args; + let { onBehalfOf: onBehalfOfIndex } = action.args; + + if (!amount || amount === '') { + throw `Invalid amount to repay into the ${reserve} reserve`; + } + + let userToRepayOnBehalf: SignerWithAddress; + if (!onBehalfOfIndex || onBehalfOfIndex === '') { + console.log( + 'WARNING: No onBehalfOf specified for a repay action. Defaulting to the repayer address' + ); + userToRepayOnBehalf = user; + } else { + userToRepayOnBehalf = users[parseInt(onBehalfOfIndex)]; + } + + await repay( + reserve, + amount, + rateMode, + user, + userToRepayOnBehalf, + sendValue, + expected, + testEnv, + revertMessage + ); + } + break; + + case 'setUseAsCollateral': + { + const { useAsCollateral } = action.args; + + if (!useAsCollateral || useAsCollateral === '') { + throw `A valid value for useAsCollateral needs to be set when calling setUseReserveAsCollateral on reserve ${reserve}`; + } + await setUseAsCollateral(reserve, user, useAsCollateral, expected, testEnv, revertMessage); + } + break; + + case 'swapBorrowRateMode': + await swapBorrowRateMode(reserve, user, rateMode, expected, testEnv, revertMessage); + break; + + case 'rebalanceStableBorrowRate': + { + const { target: targetIndex } = action.args; + + if (!targetIndex || targetIndex === '') { + throw `A target must be selected when trying to rebalance a stable rate`; + } + const target = users[parseInt(targetIndex)]; + + await rebalanceStableBorrowRate(reserve, user, target, expected, testEnv, revertMessage); + } + break; + + default: + throw `Invalid action requested: ${name}`; + } +}; diff --git a/test-suites/test-amm/helpers/scenarios/borrow-negatives.json b/test-suites/test-amm/helpers/scenarios/borrow-negatives.json new file mode 100644 index 00000000..03f4d005 --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/borrow-negatives.json @@ -0,0 +1,142 @@ +{ + "title": "LendingPool: Borrow negatives (reverts)", + "description": "Test cases for the deposit function.", + "stories": [ + { + "description": "User 0 deposits 1000 DAI, user 1 deposits 1 WETH as collateral and tries to borrow 100 DAI with rate mode NONE (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "none", + "user": "1" + }, + "expected": "revert", + "revertMessage": "Invalid interest rate mode selected" + } + ] + }, + { + "description": "User 0 deposits 1000 DAI, user 1 deposits 1 WETH as collateral and tries to borrow 100 DAI with an invalid rate mode (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "invalid", + "user": "1" + }, + "expected": "revert", + "revertMessage": "Invalid interest rate mode selected" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/borrow-repay-stable.json b/test-suites/test-amm/helpers/scenarios/borrow-repay-stable.json new file mode 100644 index 00000000..6cebf5b5 --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/borrow-repay-stable.json @@ -0,0 +1,730 @@ +{ + "title": "LendingPool: Borrow/repay (stable rate)", + "description": "Test cases for the borrow function, stable mode.", + "stories": [ + { + "description": "User 0 deposits 1000 DAI, user 1 deposits 1 WETH as collateral and tries to borrow 100 DAI at stable rate (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "stable", + "user": "1", + "timeTravel": "365" + }, + "expected": "revert" + } + ] + }, + { + "description": "User 1 tries to borrow the rest of the DAI liquidity (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "900", + "borrowRateMode": "stable", + "user": "1" + }, + "expected": "revert", + "revertMessage": "12" + } + ] + }, + { + "description": "User 1 tries to borrow 100 DAI at variable", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 tries to borrow the rest of the DAI liquidity at stable (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "900", + "borrowRateMode": "stable", + "user": "1" + }, + "expected": "revert", + "revertMessage": "12" + } + ] + }, + { + "description": "User 1 repays half of the DAI borrow at stable after one year (revert expected)", + "actions": [ + { + "name": "mint", + "description": "Mint 10 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "10", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "50", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "stable" + }, + "expected": "revert" + } + ] + }, + { + "description": "User 1 repays half of the variable DAI borrow after one year", + "actions": [ + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "50", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 repays the rest of the DAI borrow at stable after one year (revert expected)", + "actions": [ + { + "name": "mint", + "description": "Mint 15 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "15", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "stable" + }, + "expected": "revert" + } + ] + }, + { + "description": "User 1 repays the rest of the DAI borrow after one year at variable", + "actions": [ + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws the deposited DAI plus interest", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 deposits 1000 DAI, user 2 tries to borrow 1000 DAI at a stable rate without any collateral (revert expected) User 1 withdrawws", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "1000", + "borrowRateMode": "stable", + "user": "2" + }, + "expected": "revert", + "revertMessage": "The collateral balance is 0" + }, + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 deposits 1000 DAI, user 1,2,3,4 deposit 1 WETH each and borrow 100 DAI at stable rate (revert expected) user 0 withdraws", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "stable", + "user": "1", + "timeTravel": "365" + }, + "expected": "revert" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "2" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "2" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "2" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "stable", + "user": "2", + "timeTravel": "365" + }, + "expected": "revert" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "3" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "3" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "3" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "stable", + "user": "3", + "timeTravel": "365" + }, + "expected": "revert" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "4" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "4" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "4" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "stable", + "user": "4", + "timeTravel": "365" + }, + "expected": "revert" + }, + { + "name": "mint", + "description": "Mint 15 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "15", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "mint", + "description": "Mint 20 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "20", + "user": "2" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "2" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "2", + "onBehalfOf": "2", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "mint", + "description": "Mint 30 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "30", + "user": "3" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "3" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "3", + "onBehalfOf": "3", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "mint", + "description": "Mint 30 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "30", + "user": "4" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "4" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "4", + "onBehalfOf": "4", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 deposits 1000 DAI, user 1 deposits 2 WETH and borrow 100 DAI at stable rate first (revert expected), then 100 DAI at variable rate twice, repays everything. User 0 withdraws", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "stable", + "user": "1", + "timeTravel": "365" + }, + "expected": "revert" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "1", + "timeTravel": "365" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "1", + "timeTravel": "365" + }, + "expected": "success" + }, + { + "name": "mint", + "description": "Mint 50 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "50", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + }, + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/borrow-repay-variable.json b/test-suites/test-amm/helpers/scenarios/borrow-repay-variable.json new file mode 100644 index 00000000..ccde9089 --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/borrow-repay-variable.json @@ -0,0 +1,956 @@ +{ + "title": "LendingPool: Borrow/repay (variable rate)", + "description": "Test cases for the borrow function, variable mode.", + "stories": [ + { + "description": "User 2 deposits 1 DAI to account for rounding errors", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1", + "user": "2" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "2" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1", + "user": "2" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 deposits 1000 DAI, user 1 deposits 1 WETH as collateral and borrows 100 DAI at variable rate", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "1", + "timeTravel": "365" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 tries to borrow the rest of the DAI liquidity (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "900", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "revert", + "revertMessage": "There is not enough collateral to cover a new borrow" + } + ] + }, + { + "description": "User 1 tries to repay 0 DAI (revert expected)", + "actions": [ + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "0", + "user": "1", + "onBehalfOf": "1" + }, + "expected": "revert", + "revertMessage": "Amount must be greater than 0" + } + ] + }, + { + "description": "User 1 repays a small amount of DAI, enough to cover a small part of the interest", + "actions": [ + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "1.25", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 repays the DAI borrow after one year", + "actions": [ + { + "name": "mint", + "description": "Mint 10 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "10", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws the deposited DAI plus interest", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 withdraws the collateral", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 2 deposits a small amount of WETH to account for rounding errors", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "0.001", + "user": "2" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "2" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "0.001", + "user": "2" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 deposits 1 WETH, user 1 deposits 100 UNILINKWETH as collateral and borrows 0.5 WETH at variable rate", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "UNILINKWETH", + "amount": "100", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "UNILINKWETH", + "user": "1" + }, + "expected": "success" + }, + + { + "name": "deposit", + "args": { + "reserve": "UNILINKWETH", + "amount": "100", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "0.5", + "borrowRateMode": "variable", + "user": "1", + "timeTravel": "365" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 tries to repay 0 WETH", + "actions": [ + { + "name": "repay", + "args": { + "reserve": "WETH", + "amount": "0", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "revert", + "revertMessage": "Amount must be greater than 0" + } + ] + }, + { + "description": "User 2 tries to repay everything on behalf of user 1 using uint(-1) (revert expected)", + "actions": [ + { + "name": "repay", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "2", + "borrowRateMode": "variable", + "onBehalfOf": "1" + }, + "expected": "revert", + "revertMessage": "To repay on behalf of an user an explicit amount to repay is needed" + } + ] + }, + { + "description": "User 3 repays a small amount of WETH on behalf of user 1", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "3" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "3" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "WETH", + "amount": "0.2", + "user": "3", + "borrowRateMode": "variable", + "onBehalfOf": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 repays the WETH borrow after one year", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "2" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "2" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "WETH", + "amount": "-1", + "borrowRateMode": "variable", + "user": "1", + "onBehalfOf": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws the deposited WETH plus interest", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 withdraws the collateral", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "UNILINKWETH", + "amount": "-1", + "user": "1" + }, + "expected": "success" + } + ] + }, + + { + "description": "User 2 deposits 1 USDC to account for rounding errors", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "1", + "user": "2" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "2" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1", + "user": "2" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 deposits 1000 USDC, user 1 deposits 1 WETH as collateral and borrows 100 USDC at variable rate", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "100", + "borrowRateMode": "variable", + "user": "1", + "timeTravel": "365" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 tries to borrow the rest of the USDC liquidity (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "900", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "revert", + "revertMessage": "There is not enough collateral to cover a new borrow" + } + ] + }, + { + "description": "User 1 repays the USDC borrow after one year", + "actions": [ + { + "name": "mint", + "description": "Mint 10 USDC to cover the interest", + "args": { + "reserve": "USDC", + "amount": "10", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "USDC", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws the deposited USDC plus interest", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "USDC", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 withdraws the collateral", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 deposits 1000 DAI, user 3 tries to borrow 1000 DAI without any collateral (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "1000", + "borrowRateMode": "variable", + "user": "3" + }, + "expected": "revert", + "revertMessage": "The collateral balance is 0" + } + ] + }, + { + "description": "user 3 deposits 0.1 WETH collateral to borrow 100 DAI; 0.1 WETH is not enough to borrow 100 DAI (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "0.1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "0.1", + "user": "3" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "3" + }, + "expected": "revert", + "revertMessage": "There is not enough collateral to cover a new borrow" + } + ] + }, + { + "description": "user 3 withdraws the 0.1 WETH", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "3" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 deposits 1000 USDC, user 3 tries to borrow 1000 USDC without any collateral (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "1000", + "borrowRateMode": "variable", + "user": "3" + }, + "expected": "revert", + "revertMessage": "The collateral balance is 0" + } + ] + }, + { + "description": "user 3 deposits 0.1 WETH collateral to borrow 100 USDC; 0.1 WETH is not enough to borrow 100 USDC (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "3" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "0.1", + "user": "3" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "100", + "borrowRateMode": "variable", + "user": "3" + }, + "expected": "revert", + "revertMessage": "There is not enough collateral to cover a new borrow" + } + ] + }, + { + "description": "user 3 withdraws the 0.1 WETH", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "3" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 deposits 1000 DAI, user 6 deposits 2 WETH and borrow 100 DAI at variable rate first, then 100 DAI at stable rate (revert expected), then 100 DAI at variable again, repays everything. User 0 withdraws", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "6" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "6" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "6" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "6", + "timeTravel": "365" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "stable", + "user": "6", + "timeTravel": "365" + }, + "expected": "revert" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "6", + "timeTravel": "365" + }, + "expected": "success" + }, + { + "name": "mint", + "description": "Mint 50 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "50", + "user": "6" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "6" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "6", + "onBehalfOf": "6", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "6", + "onBehalfOf": "6", + "borrowRateMode": "variable" + }, + "expected": "success" + }, + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/credit-delegation.json b/test-suites/test-amm/helpers/scenarios/credit-delegation.json new file mode 100644 index 00000000..8798083b --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/credit-delegation.json @@ -0,0 +1,184 @@ +{ + "title": "LendingPool: credit delegation", + "description": "Test cases for the credit delegation related functions.", + "stories": [ + { + "description": "User 3 deposits 1000 WETH. User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH on variable to user 4, user 4 borrows 1 WETH variable on behalf of user 0", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1000", + "user": "3" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "3" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1000", + "user": "3" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "delegateBorrowAllowance", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "0", + "borrowRateMode": "variable", + "toUser": "4" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "4", + "onBehalfOf": "0", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + }, + { + "description": "User 4 trying to borrow 1 WETH stable on behalf of user 0, (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "4", + "onBehalfOf": "0", + "borrowRateMode": "stable" + }, + "expected": "revert", + "revertMessage": "59" + } + ] + }, + { + "description": "User 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 3 WETH variable on behalf of user 0, (revert expected)", + "actions": [ + { + "name": "delegateBorrowAllowance", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0", + "borrowRateMode": "variable", + "toUser": "4" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "3", + "user": "4", + "onBehalfOf": "0", + "borrowRateMode": "variable" + }, + "expected": "revert", + "revertMessage": "59" + } + ] + }, + { + "description": "User 0 delegates borrowing of 1 WETH on stable to user 2, user 2 borrows 1 WETH stable on behalf of user 0 (revert expected)", + "actions": [ + { + "name": "delegateBorrowAllowance", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0", + "borrowRateMode": "stable", + "toUser": "2" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "2", + "onBehalfOf": "0", + "borrowRateMode": "stable" + }, + "expected": "revert" + } + ] + }, + { + "description": "User 0 delegates borrowing of 1 WETH on variable to user 2, user 2 borrows 1 WETH variable on behalf of user 0", + "actions": [ + { + "name": "delegateBorrowAllowance", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0", + "borrowRateMode": "variable", + "toUser": "2" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "2", + "onBehalfOf": "0", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/deposit.json b/test-suites/test-amm/helpers/scenarios/deposit.json new file mode 100644 index 00000000..b4b73879 --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/deposit.json @@ -0,0 +1,266 @@ +{ + "title": "LendingPool: Deposit", + "description": "Test cases for the deposit function.", + "stories": [ + { + "description": "User 0 Deposits 1000 DAI in an empty reserve", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 deposits 1000 DAI after user 0", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 deposits 1000 USDC in an empty reserve", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 deposits 1000 USDC after user 0", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 deposits 1 WETH in an empty reserve", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 deposits 1 WETH after user 0", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 deposits 0 WETH (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "0", + "user": "1" + }, + "expected": "revert", + "revertMessage": "Amount must be greater than 0" + } + ] + }, + { + "description": "User 1 deposits 0 DAI", + "actions": [ + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "0", + "user": "1" + }, + "expected": "revert", + "revertMessage": "Amount must be greater than 0" + } + ] + }, + { + "description": "User 1 deposits 100 DAI on behalf of user 2, user 2 tries to borrow 0.1 WETH", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "100", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "100", + "user": "1", + "onBehalfOf": "2" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "0.1", + "borrowRateMode": "variable", + "user": "2" + }, + "expected": "success" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/rebalance-stable-rate.json b/test-suites/test-amm/helpers/scenarios/rebalance-stable-rate.json new file mode 100644 index 00000000..8e4f17b6 --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/rebalance-stable-rate.json @@ -0,0 +1,198 @@ +{ + "title": "LendingPool: Rebalance stable rate", + "description": "Test cases for the rebalanceStableBorrowRate() function.", + "stories": [ + { + "description": "User 0 tries to rebalance user 1 who has no borrows in progress (revert expected)", + "actions": [ + { + "name": "rebalanceStableBorrowRate", + "args": { + "reserve": "USDC", + "user": "0", + "target": "1" + }, + "expected": "revert", + "revertMessage": "User does not have any stable rate loan for this reserve" + } + ] + }, + { + "description": "User 0 deposits 1000 USDC, user 1 deposits 7 WETH, borrows 250 USDC at a variable rate, user 0 rebalances user 1 (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "7", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "7", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "250", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "success" + }, + { + "name": "rebalanceStableBorrowRate", + "args": { + "reserve": "USDC", + "user": "0", + "target": "1" + }, + "expected": "revert", + "revertMessage": "The user borrow is variable and cannot be rebalanced" + } + ] + }, + { + "description": "User 1 borrows another 200 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "200", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "success" + }, + { + "name": "rebalanceStableBorrowRate", + "args": { + "reserve": "USDC", + "user": "0", + "target": "1" + }, + "expected": "revert", + "revertMessage": "Interest rate rebalance conditions were not met" + } + ] + }, + { + "description": "User 1 borrows another 200 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "200", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "success" + }, + { + "name": "rebalanceStableBorrowRate", + "args": { + "reserve": "USDC", + "user": "0", + "target": "1" + }, + "expected": "revert", + "revertMessage": "Interest rate rebalance conditions were not met" + } + ] + }, + { + "description": "User 1 borrows another 100 at variable, user 0 tries to rebalance but the conditions are not met (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "280", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "success" + }, + { + "name": "rebalanceStableBorrowRate", + "args": { + "reserve": "USDC", + "user": "0", + "target": "1" + }, + "expected": "revert", + "revertMessage": "Interest rate rebalance conditions were not met" + } + ] + }, + + { + "description": "User 1 borrows the remaining USDC (usage ratio = 100%) at variable. User 0 rebalances user 1, fails since there are no stable borrows (revert expected)", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "USDC", + "amount": "20", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "success" + }, + { + "name": "rebalanceStableBorrowRate", + "args": { + "reserve": "USDC", + "user": "0", + "target": "1" + }, + "expected": "revert" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/set-use-as-collateral.json b/test-suites/test-amm/helpers/scenarios/set-use-as-collateral.json new file mode 100644 index 00000000..d2c8520a --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/set-use-as-collateral.json @@ -0,0 +1,236 @@ +{ + "title": "LendingPool: Usage as collateral", + "description": "Test cases for the setUserUseReserveAsCollateral() function.", + "stories": [ + { + "description": "User 0 Deposits 1000 DAI, disables DAI as collateral", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "setUseAsCollateral", + "args": { + "reserve": "DAI", + "user": "0", + "useAsCollateral": "false" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 Deposits 2 WETH, disables WETH as collateral, borrows 400 DAI (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "1" + }, + "expected": "success" + }, + { + "name": "setUseAsCollateral", + "args": { + "reserve": "WETH", + "user": "1", + "useAsCollateral": "false" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "400", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "revert", + "revertMessage": "The collateral balance is 0" + } + ] + }, + { + "description": "User 1 enables WETH as collateral, borrows 400 DAI", + "actions": [ + { + "name": "setUseAsCollateral", + "args": { + "reserve": "WETH", + "user": "1", + "useAsCollateral": "true" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "400", + "borrowRateMode": "variable", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 disables WETH as collateral (revert expected)", + "actions": [ + { + "name": "setUseAsCollateral", + "args": { + "reserve": "WETH", + "user": "1", + "useAsCollateral": "false" + }, + "expected": "revert", + "revertMessage": "User deposit is already being used as collateral" + } + ] + }, + { + "description": "User 1 Deposits 10 UNILINKWETH, disables WETH as collateral. Should revert as 10 UNILINKWETH are not enough to cover the debt (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "UNILINKWETH", + "amount": "10", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "UNILINKWETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "UNILINKWETH", + "amount": "10", + "user": "1" + }, + "expected": "success" + }, + { + "name": "setUseAsCollateral", + "args": { + "reserve": "WETH", + "user": "1", + "useAsCollateral": "false" + }, + "expected": "revert" + } + ] + }, + { + "description": "User 1 Deposits 640 more UNILINKWETH (enough to cover the DAI debt), disables WETH as collateral", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "UNILINKWETH", + "amount": "640", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "UNILINKWETH", + "amount": "640", + "user": "1" + }, + "expected": "success" + }, + { + "name": "setUseAsCollateral", + "args": { + "reserve": "WETH", + "user": "1", + "useAsCollateral": "false" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 disables UNILINKWETH as collateral (revert expected)", + "actions": [ + { + "name": "setUseAsCollateral", + "args": { + "reserve": "UNILINKWETH", + "user": "1", + "useAsCollateral": "false" + }, + "expected": "revert" + } + ] + }, + { + "description": "User 1 reenables WETH as collateral", + "actions": [ + { + "name": "setUseAsCollateral", + "args": { + "reserve": "WETH", + "user": "1", + "useAsCollateral": "true" + }, + "expected": "success" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/swap-rate-mode.json b/test-suites/test-amm/helpers/scenarios/swap-rate-mode.json new file mode 100644 index 00000000..17f9dfe3 --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/swap-rate-mode.json @@ -0,0 +1,221 @@ +{ + "title": "LendingPool: Swap rate mode", + "description": "Test cases for the swapBorrowRateMode() function.", + "stories": [ + { + "description": "User 0 tries to swap rate mode without any variable rate loan in progress (revert expected)", + "actions": [ + { + "name": "swapBorrowRateMode", + "args": { + "reserve": "DAI", + "user": "1", + "borrowRateMode": "variable" + }, + "expected": "revert", + "revertMessage": "User does not have a variable rate loan in progress on this reserve" + } + ] + }, + { + "description": "User 0 tries to swap rate mode without any stable rate loan in progress (revert expected)", + "actions": [ + { + "name": "swapBorrowRateMode", + "args": { + "reserve": "DAI", + "user": "1", + "borrowRateMode": "stable" + }, + "expected": "revert", + "revertMessage": "User does not have a stable rate loan in progress on this reserve" + } + ] + }, + { + "description": "User 0 deposits 1000 DAI, user 1 deposits 2 WETH as collateral, borrows 100 DAI at variable rate and swaps to stable after one year, fails because stable borrowing is disabled (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "1", + "timeTravel": "365" + }, + "expected": "success" + }, + { + "name": "swapBorrowRateMode", + "args": { + "reserve": "DAI", + "user": "1", + "borrowRateMode": "variable" + }, + "expected": "revert" + } + ] + }, + { + "description": "User 1 borrows another 100 DAI at stable (revert expected), and swaps back to variable after one year (revert expected), repays the loan", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "stable", + "user": "1", + "timeTravel": "365" + }, + "expected": "revert" + }, + { + "name": "swapBorrowRateMode", + "args": { + "reserve": "DAI", + "user": "1", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "mint", + "description": "Mint 50 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "50", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + }, + { + "description": "User 1 borrows another 100 DAI at variable, and tries to swap to stable after one year (revert expected), repays the loan", + "actions": [ + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "borrowRateMode": "variable", + "user": "1", + "timeTravel": "365" + }, + "expected": "success" + }, + { + "name": "swapBorrowRateMode", + "args": { + "reserve": "DAI", + "user": "1", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "mint", + "description": "Mint 50 DAI to cover the interest", + "args": { + "reserve": "DAI", + "amount": "50", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "repay", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1", + "onBehalfOf": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/withdraw-negatives.json b/test-suites/test-amm/helpers/scenarios/withdraw-negatives.json new file mode 100644 index 00000000..cf50812d --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/withdraw-negatives.json @@ -0,0 +1,164 @@ +{ + "title": "LendingPool: Redeem negative test cases", + "description": "Redeem function.", + "stories": [ + { + "description": "Users 0 Deposits 1000 DAI and tries to redeem 0 DAI (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "0", + "user": "0" + }, + "expected": "revert", + "revertMessage": "Amount to redeem needs to be > 0" + } + ] + }, + { + "description": "Users 0 tries to redeem 1100 DAI from the 1000 DAI deposited (revert expected)", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "1100", + "user": "0" + }, + "expected": "revert", + "revertMessage": "User cannot redeem more than the available balance" + } + ] + }, + { + "description": "Users 1 deposits 1 WETH, borrows 100 DAI stable (revert expected), redeems the 1 WETH deposited", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "user": "1", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "Users 1 deposits 1 WETH, borrows 100 DAI at variable rate, tries to redeem the 1 WETH deposited (revert expected)", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "user": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + }, + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "1" + }, + "expected": "revert", + "revertMessage": "Transfer cannot be allowed." + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/scenarios/withdraw.json b/test-suites/test-amm/helpers/scenarios/withdraw.json new file mode 100644 index 00000000..d3b69908 --- /dev/null +++ b/test-suites/test-amm/helpers/scenarios/withdraw.json @@ -0,0 +1,434 @@ +{ + "title": "LendingPool: withdraw", + "description": "withdraw function.", + "stories": [ + { + "description": "User 0 Deposits 1000 DAI in an empty reserve", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws half of the deposited DAI", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "500", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws remaining half of the deposited DAI", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 Deposits 1000 USDC in an empty reserve", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws half of the deposited USDC", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "USDC", + "amount": "500", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws remaining half of the deposited USDC", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "USDC", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 Deposits 1 WETH in an empty reserve", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws half of the deposited WETH", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "0.5", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 withdraws remaining half of the deposited WETH", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "-1", + "user": "0" + }, + "expected": "success" + } + ] + }, + { + "description": "Users 0 and 1 Deposit 1000 DAI, both withdraw", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "0" + }, + "expected": "success" + }, + { + "name": "withdraw", + "args": { + "reserve": "DAI", + "amount": "-1", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "Users 0 deposits 1000 DAI, user 1 Deposit 1000 USDC and 1 WETH, borrows 100 DAI at stable rate (revert expected). User 1 tries to withdraw all the USDC", + "actions": [ + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "user": "1", + "borrowRateMode": "stable" + }, + "expected": "revert" + }, + { + "name": "withdraw", + "args": { + "reserve": "USDC", + "amount": "-1", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "Users 0 deposits 1000 DAI, user 1 Deposit 1000 USDC and 1 WETH, borrows 100 DAI at variable rate. User 1 tries to withdraw all the USDC", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "USDC", + "amount": "10000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "USDC", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "USDC", + "amount": "1000", + "user": "1" + }, + "expected": "success" + }, + { + "name": "mint", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "WETH", + "user": "1" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "1" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "DAI", + "amount": "100", + "user": "1", + "borrowRateMode": "variable" + }, + "expected": "success" + }, + { + "name": "withdraw", + "args": { + "reserve": "USDC", + "amount": "-1", + "user": "1" + }, + "expected": "success" + } + ] + }, + { + "description": "Users 1 tries to withdraw 0.05 WETH, which does not bring the HF below 1", + "actions": [ + { + "name": "withdraw", + "args": { + "reserve": "WETH", + "amount": "0.05", + "user": "1" + }, + "expected": "success" + } + ] + } + ] +} diff --git a/test-suites/test-amm/helpers/utils/calculations.ts b/test-suites/test-amm/helpers/utils/calculations.ts new file mode 100644 index 00000000..e1574656 --- /dev/null +++ b/test-suites/test-amm/helpers/utils/calculations.ts @@ -0,0 +1,1438 @@ +import BigNumber from 'bignumber.js'; +import { ONE_YEAR, RAY, MAX_UINT_AMOUNT, PERCENTAGE_FACTOR } from '../../../../helpers/constants'; +import { + IReserveParams, + iLpPoolAssets, + RateMode, + tEthereumAddress, +} from '../../../../helpers/types'; +import './math'; +import { ReserveData, UserReserveData } from './interfaces'; + +export const strToBN = (amount: string): BigNumber => new BigNumber(amount); + +interface Configuration { + reservesParams: iLpPoolAssets; +} + +export const configuration: Configuration = {}; + +export const calcExpectedUserDataAfterDeposit = ( + amountDeposited: string, + reserveDataBeforeAction: ReserveData, + reserveDataAfterAction: ReserveData, + userDataBeforeAction: UserReserveData, + txTimestamp: BigNumber, + currentTimestamp: BigNumber, + txCost: BigNumber +): UserReserveData => { + const expectedUserData = {}; + + expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; + expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt; + expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; + expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; + expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated; + + expectedUserData.liquidityRate = reserveDataAfterAction.liquidityRate; + + expectedUserData.scaledATokenBalance = calcExpectedScaledATokenBalance( + userDataBeforeAction, + reserveDataAfterAction.liquidityIndex, + new BigNumber(amountDeposited), + new BigNumber(0) + ); + expectedUserData.currentATokenBalance = calcExpectedATokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ).plus(amountDeposited); + + if (userDataBeforeAction.currentATokenBalance.eq(0)) { + expectedUserData.usageAsCollateralEnabled = true; + } else { + expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled; + } + + expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; + expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(amountDeposited); + + expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + expectedUserData.currentVariableDebt = expectedUserData.principalStableDebt = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + return expectedUserData; +}; + +export const calcExpectedUserDataAfterWithdraw = ( + amountWithdrawn: string, + reserveDataBeforeAction: ReserveData, + reserveDataAfterAction: ReserveData, + userDataBeforeAction: UserReserveData, + txTimestamp: BigNumber, + currentTimestamp: BigNumber, + txCost: BigNumber +): UserReserveData => { + const expectedUserData = {}; + + const aTokenBalance = calcExpectedATokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + if (amountWithdrawn == MAX_UINT_AMOUNT) { + amountWithdrawn = aTokenBalance.toFixed(0); + } + + expectedUserData.scaledATokenBalance = calcExpectedScaledATokenBalance( + userDataBeforeAction, + reserveDataAfterAction.liquidityIndex, + new BigNumber(0), + new BigNumber(amountWithdrawn) + ); + + expectedUserData.currentATokenBalance = aTokenBalance.minus(amountWithdrawn); + + expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; + expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt; + + expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; + expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; + expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated; + + expectedUserData.liquidityRate = reserveDataAfterAction.liquidityRate; + + if (userDataBeforeAction.currentATokenBalance.eq(0)) { + expectedUserData.usageAsCollateralEnabled = true; + } else { + //if the user is withdrawing everything, usageAsCollateralEnabled must be false + if (expectedUserData.currentATokenBalance.eq(0)) { + expectedUserData.usageAsCollateralEnabled = false; + } else { + expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled; + } + } + + expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(amountWithdrawn); + + return expectedUserData; +}; + +export const calcExpectedReserveDataAfterDeposit = ( + amountDeposited: string, + reserveDataBeforeAction: ReserveData, + txTimestamp: BigNumber +): ReserveData => { + const expectedReserveData: ReserveData = {}; + + expectedReserveData.address = reserveDataBeforeAction.address; + + expectedReserveData.totalLiquidity = new BigNumber(reserveDataBeforeAction.totalLiquidity).plus( + amountDeposited + ); + expectedReserveData.availableLiquidity = new BigNumber( + reserveDataBeforeAction.availableLiquidity + ).plus(amountDeposited); + + expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate; + expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( + reserveDataBeforeAction, + txTimestamp + ); + expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt( + reserveDataBeforeAction.principalStableDebt, + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebtLastUpdated, + txTimestamp + ); + expectedReserveData.totalVariableDebt = calcExpectedTotalVariableDebt( + reserveDataBeforeAction, + expectedReserveData.variableBorrowIndex + ); + + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; + expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt; + + expectedReserveData.utilizationRate = calcExpectedUtilizationRate( + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.totalLiquidity + ); + const rates = calcExpectedInterestRates( + reserveDataBeforeAction.symbol, + reserveDataBeforeAction.marketStableRate, + expectedReserveData.utilizationRate, + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.averageStableBorrowRate + ); + expectedReserveData.liquidityRate = rates[0]; + expectedReserveData.stableBorrowRate = rates[1]; + expectedReserveData.variableBorrowRate = rates[2]; + + return expectedReserveData; +}; + +export const calcExpectedReserveDataAfterWithdraw = ( + amountWithdrawn: string, + reserveDataBeforeAction: ReserveData, + userDataBeforeAction: UserReserveData, + txTimestamp: BigNumber +): ReserveData => { + const expectedReserveData: ReserveData = {}; + + expectedReserveData.address = reserveDataBeforeAction.address; + + if (amountWithdrawn == MAX_UINT_AMOUNT) { + amountWithdrawn = calcExpectedATokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ).toFixed(); + } + + expectedReserveData.availableLiquidity = new BigNumber( + reserveDataBeforeAction.availableLiquidity + ).minus(amountWithdrawn); + + expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt; + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; + + expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( + reserveDataBeforeAction, + txTimestamp + ); + expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt( + reserveDataBeforeAction.principalStableDebt, + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebtLastUpdated, + txTimestamp + ); + expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul( + expectedReserveData.variableBorrowIndex + ); + + expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate; + + expectedReserveData.totalLiquidity = new BigNumber(reserveDataBeforeAction.availableLiquidity) + .minus(amountWithdrawn) + .plus(expectedReserveData.totalVariableDebt) + .plus(expectedReserveData.totalStableDebt); + + expectedReserveData.utilizationRate = calcExpectedUtilizationRate( + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.totalLiquidity + ); + const rates = calcExpectedInterestRates( + reserveDataBeforeAction.symbol, + reserveDataBeforeAction.marketStableRate, + expectedReserveData.utilizationRate, + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.averageStableBorrowRate + ); + expectedReserveData.liquidityRate = rates[0]; + expectedReserveData.stableBorrowRate = rates[1]; + expectedReserveData.variableBorrowRate = rates[2]; + + return expectedReserveData; +}; + +export const calcExpectedReserveDataAfterBorrow = ( + amountBorrowed: string, + borrowRateMode: string, + reserveDataBeforeAction: ReserveData, + userDataBeforeAction: UserReserveData, + txTimestamp: BigNumber, + currentTimestamp: BigNumber +): ReserveData => { + const expectedReserveData = {}; + + expectedReserveData.address = reserveDataBeforeAction.address; + + const amountBorrowedBN = new BigNumber(amountBorrowed); + + expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity.minus( + amountBorrowedBN + ); + + expectedReserveData.lastUpdateTimestamp = txTimestamp; + + if (borrowRateMode == RateMode.Stable) { + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; + + const expectedVariableDebtAfterTx = expectedReserveData.scaledVariableDebt.rayMul( + expectedReserveData.variableBorrowIndex + ); + + const expectedStableDebtUntilTx = calcExpectedTotalStableDebt( + reserveDataBeforeAction.principalStableDebt, + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebtLastUpdated, + txTimestamp + ); + + expectedReserveData.principalStableDebt = expectedStableDebtUntilTx.plus(amountBorrowedBN); + + expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate( + reserveDataBeforeAction.averageStableBorrowRate, + expectedStableDebtUntilTx, + amountBorrowedBN, + reserveDataBeforeAction.stableBorrowRate + ); + + const totalLiquidityAfterTx = expectedReserveData.availableLiquidity + .plus(expectedReserveData.principalStableDebt) + .plus(expectedVariableDebtAfterTx); + + const utilizationRateAfterTx = calcExpectedUtilizationRate( + expectedReserveData.principalStableDebt, //the expected principal debt is the total debt immediately after the tx + expectedVariableDebtAfterTx, + totalLiquidityAfterTx + ); + + const ratesAfterTx = calcExpectedInterestRates( + reserveDataBeforeAction.symbol, + reserveDataBeforeAction.marketStableRate, + utilizationRateAfterTx, + expectedReserveData.principalStableDebt, + expectedVariableDebtAfterTx, + expectedReserveData.averageStableBorrowRate + ); + + expectedReserveData.liquidityRate = ratesAfterTx[0]; + + expectedReserveData.stableBorrowRate = ratesAfterTx[1]; + + expectedReserveData.variableBorrowRate = ratesAfterTx[2]; + + expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt( + expectedReserveData.principalStableDebt, + expectedReserveData.averageStableBorrowRate, + txTimestamp, + currentTimestamp + ); + + expectedReserveData.totalVariableDebt = reserveDataBeforeAction.scaledVariableDebt.rayMul( + calcExpectedReserveNormalizedDebt( + expectedReserveData.variableBorrowRate, + expectedReserveData.variableBorrowIndex, + txTimestamp, + currentTimestamp + ) + ); + + expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity + .plus(expectedReserveData.totalVariableDebt) + .plus(expectedReserveData.totalStableDebt); + + expectedReserveData.utilizationRate = calcExpectedUtilizationRate( + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.totalLiquidity + ); + } else { + expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt; + + const totalStableDebtAfterTx = calcExpectedStableDebtTokenBalance( + reserveDataBeforeAction.principalStableDebt, + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebtLastUpdated, + txTimestamp + ); + + expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt( + reserveDataBeforeAction.principalStableDebt, + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebtLastUpdated, + currentTimestamp + ); + + expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate; + + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.plus( + amountBorrowedBN.rayDiv(expectedReserveData.variableBorrowIndex) + ); + + const totalVariableDebtAfterTx = expectedReserveData.scaledVariableDebt.rayMul( + expectedReserveData.variableBorrowIndex + ); + + const utilizationRateAfterTx = calcExpectedUtilizationRate( + totalStableDebtAfterTx, + totalVariableDebtAfterTx, + expectedReserveData.availableLiquidity + .plus(totalStableDebtAfterTx) + .plus(totalVariableDebtAfterTx) + ); + + const rates = calcExpectedInterestRates( + reserveDataBeforeAction.symbol, + reserveDataBeforeAction.marketStableRate, + utilizationRateAfterTx, + totalStableDebtAfterTx, + totalVariableDebtAfterTx, + expectedReserveData.averageStableBorrowRate + ); + + expectedReserveData.liquidityRate = rates[0]; + + expectedReserveData.stableBorrowRate = rates[1]; + + expectedReserveData.variableBorrowRate = rates[2]; + + expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul( + calcExpectedReserveNormalizedDebt( + expectedReserveData.variableBorrowRate, + expectedReserveData.variableBorrowIndex, + txTimestamp, + currentTimestamp + ) + ); + + expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity + .plus(expectedReserveData.totalStableDebt) + .plus(expectedReserveData.totalVariableDebt); + + expectedReserveData.utilizationRate = calcExpectedUtilizationRate( + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.totalLiquidity + ); + } + + return expectedReserveData; +}; + +export const calcExpectedReserveDataAfterRepay = ( + amountRepaid: string, + borrowRateMode: RateMode, + reserveDataBeforeAction: ReserveData, + userDataBeforeAction: UserReserveData, + txTimestamp: BigNumber, + currentTimestamp: BigNumber +): ReserveData => { + const expectedReserveData: ReserveData = {}; + + expectedReserveData.address = reserveDataBeforeAction.address; + + let amountRepaidBN = new BigNumber(amountRepaid); + + const userStableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + const userVariableDebt = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + //if amount repaid == MAX_UINT_AMOUNT, user is repaying everything + if (amountRepaidBN.abs().eq(MAX_UINT_AMOUNT)) { + if (borrowRateMode == RateMode.Stable) { + amountRepaidBN = userStableDebt; + } else { + amountRepaidBN = userVariableDebt; + } + } + + expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( + reserveDataBeforeAction, + txTimestamp + ); + expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( + reserveDataBeforeAction, + txTimestamp + ); + + if (borrowRateMode == RateMode.Stable) { + const expectedDebt = calcExpectedTotalStableDebt( + reserveDataBeforeAction.principalStableDebt, + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebtLastUpdated, + txTimestamp + ); + + expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = expectedDebt.minus( + amountRepaidBN + ); + + //due to accumulation errors, the total stable debt might be smaller than the last user debt. + //in this case we simply set the total supply and avg stable rate to 0. + if (expectedReserveData.totalStableDebt.lt(0)) { + expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = expectedReserveData.averageStableBorrowRate = new BigNumber( + 0 + ); + } else { + expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate( + reserveDataBeforeAction.averageStableBorrowRate, + expectedDebt, + amountRepaidBN.negated(), + userDataBeforeAction.stableBorrowRate + ); + + //also due to accumulation errors, the final avg stable rate when the last user repays might be negative. + //if that is the case, it means a small leftover of total stable debt is left, which can be erased. + + if (expectedReserveData.averageStableBorrowRate.lt(0)) { + expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = expectedReserveData.averageStableBorrowRate = new BigNumber( + 0 + ); + } + } + + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; + + expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul( + expectedReserveData.variableBorrowIndex + ); + } else { + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.minus( + amountRepaidBN.rayDiv(expectedReserveData.variableBorrowIndex) + ); + + expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul( + expectedReserveData.variableBorrowIndex + ); + + expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt; + expectedReserveData.totalStableDebt = reserveDataBeforeAction.totalStableDebt; + + expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate; + } + + expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity.plus( + amountRepaidBN + ); + + expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity + .plus(expectedReserveData.totalStableDebt) + .plus(expectedReserveData.totalVariableDebt); + + expectedReserveData.utilizationRate = calcExpectedUtilizationRate( + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.totalLiquidity + ); + + const rates = calcExpectedInterestRates( + reserveDataBeforeAction.symbol, + reserveDataBeforeAction.marketStableRate, + expectedReserveData.utilizationRate, + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.averageStableBorrowRate + ); + expectedReserveData.liquidityRate = rates[0]; + + expectedReserveData.stableBorrowRate = rates[1]; + + expectedReserveData.variableBorrowRate = rates[2]; + + expectedReserveData.lastUpdateTimestamp = txTimestamp; + + return expectedReserveData; +}; + +export const calcExpectedUserDataAfterBorrow = ( + amountBorrowed: string, + interestRateMode: string, + reserveDataBeforeAction: ReserveData, + expectedDataAfterAction: ReserveData, + userDataBeforeAction: UserReserveData, + txTimestamp: BigNumber, + currentTimestamp: BigNumber +): UserReserveData => { + const expectedUserData = {}; + + const amountBorrowedBN = new BigNumber(amountBorrowed); + + if (interestRateMode == RateMode.Stable) { + const stableDebtUntilTx = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + expectedUserData.principalStableDebt = stableDebtUntilTx.plus(amountBorrowed); + expectedUserData.stableRateLastUpdated = txTimestamp; + + expectedUserData.stableBorrowRate = calcExpectedUserStableRate( + stableDebtUntilTx, + userDataBeforeAction.stableBorrowRate, + amountBorrowedBN, + reserveDataBeforeAction.stableBorrowRate + ); + + expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance( + expectedUserData.principalStableDebt, + expectedUserData.stableBorrowRate, + txTimestamp, + currentTimestamp + ); + + expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt; + } else { + expectedUserData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.plus( + amountBorrowedBN.rayDiv(expectedDataAfterAction.variableBorrowIndex) + ); + + expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; + + expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; + + expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated; + + expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + currentTimestamp + ); + } + + expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance( + expectedDataAfterAction, + expectedUserData, + currentTimestamp + ); + + expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate; + + expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled; + + expectedUserData.currentATokenBalance = calcExpectedATokenBalance( + expectedDataAfterAction, + userDataBeforeAction, + currentTimestamp + ); + + expectedUserData.scaledATokenBalance = userDataBeforeAction.scaledATokenBalance; + + expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(amountBorrowed); + + return expectedUserData; +}; + +export const calcExpectedUserDataAfterRepay = ( + totalRepaid: string, + rateMode: RateMode, + reserveDataBeforeAction: ReserveData, + expectedDataAfterAction: ReserveData, + userDataBeforeAction: UserReserveData, + user: string, + onBehalfOf: string, + txTimestamp: BigNumber, + currentTimestamp: BigNumber +): UserReserveData => { + const expectedUserData = {}; + + const variableDebt = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + currentTimestamp + ); + + const stableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + currentTimestamp + ); + + let totalRepaidBN = new BigNumber(totalRepaid); + if (totalRepaidBN.abs().eq(MAX_UINT_AMOUNT)) { + totalRepaidBN = rateMode == RateMode.Stable ? stableDebt : variableDebt; + } + + if (rateMode == RateMode.Stable) { + expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt; + expectedUserData.currentVariableDebt = variableDebt; + + expectedUserData.principalStableDebt = expectedUserData.currentStableDebt = stableDebt.minus( + totalRepaidBN + ); + + if (expectedUserData.currentStableDebt.eq('0')) { + //user repaid everything + expectedUserData.stableBorrowRate = expectedUserData.stableRateLastUpdated = new BigNumber( + '0' + ); + } else { + expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; + expectedUserData.stableRateLastUpdated = txTimestamp; + } + } else { + expectedUserData.currentStableDebt = userDataBeforeAction.principalStableDebt; + expectedUserData.principalStableDebt = stableDebt; + expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; + expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated; + + expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt.minus( + totalRepaidBN.rayDiv(expectedDataAfterAction.variableBorrowIndex) + ); + expectedUserData.currentVariableDebt = expectedUserData.scaledVariableDebt.rayMul( + expectedDataAfterAction.variableBorrowIndex + ); + } + + expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate; + + expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled; + + expectedUserData.currentATokenBalance = calcExpectedATokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + expectedUserData.scaledATokenBalance = userDataBeforeAction.scaledATokenBalance; + + if (user === onBehalfOf) { + expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(totalRepaidBN); + } else { + //wallet balance didn't change + expectedUserData.walletBalance = userDataBeforeAction.walletBalance; + } + + return expectedUserData; +}; + +export const calcExpectedUserDataAfterSetUseAsCollateral = ( + useAsCollateral: boolean, + reserveDataBeforeAction: ReserveData, + userDataBeforeAction: UserReserveData, + txCost: BigNumber +): UserReserveData => { + const expectedUserData = { ...userDataBeforeAction }; + + expectedUserData.usageAsCollateralEnabled = useAsCollateral; + + return expectedUserData; +}; + +export const calcExpectedReserveDataAfterSwapRateMode = ( + reserveDataBeforeAction: ReserveData, + userDataBeforeAction: UserReserveData, + rateMode: string, + txTimestamp: BigNumber +): ReserveData => { + const expectedReserveData: ReserveData = {}; + + expectedReserveData.address = reserveDataBeforeAction.address; + + const variableDebt = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + const stableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity; + + const totalStableDebtUntilTx = calcExpectedTotalStableDebt( + reserveDataBeforeAction.principalStableDebt, + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebtLastUpdated, + txTimestamp + ); + + if (rateMode === RateMode.Stable) { + //swap user stable debt to variable + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.plus( + stableDebt.rayDiv(expectedReserveData.variableBorrowIndex) + ); + + expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul( + expectedReserveData.variableBorrowIndex + ); + + expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = totalStableDebtUntilTx.minus( + stableDebt + ); + + expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate( + reserveDataBeforeAction.averageStableBorrowRate, + expectedReserveData.principalStableDebt.plus(stableDebt), + stableDebt.negated(), + userDataBeforeAction.stableBorrowRate + ); + } else { + //swap variable to stable + + expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = totalStableDebtUntilTx.plus( + variableDebt + ); + + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.minus( + variableDebt.rayDiv(expectedReserveData.variableBorrowIndex) + ); + + expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul( + expectedReserveData.variableBorrowIndex + ); + + expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate( + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebt, + variableDebt, + reserveDataBeforeAction.stableBorrowRate + ); + } + + expectedReserveData.totalLiquidity = reserveDataBeforeAction.availableLiquidity + .plus(expectedReserveData.totalStableDebt) + .plus(expectedReserveData.totalVariableDebt); + + expectedReserveData.utilizationRate = calcExpectedUtilizationRate( + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.totalLiquidity + ); + + const rates = calcExpectedInterestRates( + reserveDataBeforeAction.symbol, + reserveDataBeforeAction.marketStableRate, + expectedReserveData.utilizationRate, + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.averageStableBorrowRate + ); + expectedReserveData.liquidityRate = rates[0]; + + expectedReserveData.stableBorrowRate = rates[1]; + + expectedReserveData.variableBorrowRate = rates[2]; + + return expectedReserveData; +}; + +export const calcExpectedUserDataAfterSwapRateMode = ( + reserveDataBeforeAction: ReserveData, + expectedDataAfterAction: ReserveData, + userDataBeforeAction: UserReserveData, + rateMode: string, + txCost: BigNumber, + txTimestamp: BigNumber +): UserReserveData => { + const expectedUserData = { ...userDataBeforeAction }; + + const stableDebtBalance = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + const variableDebtBalance = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + expectedUserData.currentATokenBalance = calcExpectedATokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + if (rateMode === RateMode.Stable) { + // swap to variable + expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = new BigNumber(0); + + expectedUserData.stableBorrowRate = new BigNumber(0); + + expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt.plus( + stableDebtBalance.rayDiv(expectedDataAfterAction.variableBorrowIndex) + ); + expectedUserData.currentVariableDebt = expectedUserData.scaledVariableDebt.rayMul( + expectedDataAfterAction.variableBorrowIndex + ); + + expectedUserData.stableRateLastUpdated = new BigNumber(0); + } else { + expectedUserData.principalStableDebt = expectedUserData.currentStableDebt = userDataBeforeAction.currentStableDebt.plus( + variableDebtBalance + ); + + //weighted average of the previous and the current + expectedUserData.stableBorrowRate = calcExpectedUserStableRate( + stableDebtBalance, + userDataBeforeAction.stableBorrowRate, + variableDebtBalance, + reserveDataBeforeAction.stableBorrowRate + ); + + expectedUserData.stableRateLastUpdated = txTimestamp; + + expectedUserData.currentVariableDebt = expectedUserData.scaledVariableDebt = new BigNumber(0); + } + + expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate; + + return expectedUserData; +}; + +export const calcExpectedReserveDataAfterStableRateRebalance = ( + reserveDataBeforeAction: ReserveData, + userDataBeforeAction: UserReserveData, + txTimestamp: BigNumber +): ReserveData => { + const expectedReserveData: ReserveData = {}; + + expectedReserveData.address = reserveDataBeforeAction.address; + + const userStableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; + expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul( + expectedReserveData.variableBorrowIndex + ); + + expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt( + reserveDataBeforeAction.principalStableDebt, + reserveDataBeforeAction.averageStableBorrowRate, + reserveDataBeforeAction.totalStableDebtLastUpdated, + txTimestamp + ); + + expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity; + + expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity + .plus(expectedReserveData.totalStableDebt) + .plus(expectedReserveData.totalVariableDebt); + + //removing the stable liquidity at the old rate + + const avgRateBefore = calcExpectedAverageStableBorrowRateRebalance( + reserveDataBeforeAction.averageStableBorrowRate, + expectedReserveData.totalStableDebt, + userStableDebt.negated(), + userDataBeforeAction.stableBorrowRate + ); + // adding it again at the new rate + + expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRateRebalance( + avgRateBefore, + expectedReserveData.totalStableDebt.minus(userStableDebt), + userStableDebt, + reserveDataBeforeAction.stableBorrowRate + ); + + expectedReserveData.utilizationRate = calcExpectedUtilizationRate( + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.totalLiquidity + ); + + const rates = calcExpectedInterestRates( + reserveDataBeforeAction.symbol, + reserveDataBeforeAction.marketStableRate, + expectedReserveData.utilizationRate, + expectedReserveData.totalStableDebt, + expectedReserveData.totalVariableDebt, + expectedReserveData.averageStableBorrowRate + ); + + expectedReserveData.liquidityRate = rates[0]; + + expectedReserveData.stableBorrowRate = rates[1]; + + expectedReserveData.variableBorrowRate = rates[2]; + + return expectedReserveData; +}; + +export const calcExpectedUserDataAfterStableRateRebalance = ( + reserveDataBeforeAction: ReserveData, + expectedDataAfterAction: ReserveData, + userDataBeforeAction: UserReserveData, + txCost: BigNumber, + txTimestamp: BigNumber +): UserReserveData => { + const expectedUserData = { ...userDataBeforeAction }; + + expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; + + expectedUserData.principalVariableDebt = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = calcExpectedStableDebtTokenBalance( + userDataBeforeAction.principalStableDebt, + userDataBeforeAction.stableBorrowRate, + userDataBeforeAction.stableRateLastUpdated, + txTimestamp + ); + + expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + expectedUserData.stableRateLastUpdated = txTimestamp; + + expectedUserData.principalVariableDebt = userDataBeforeAction.principalVariableDebt; + + // Stable rate after burn + expectedUserData.stableBorrowRate = expectedDataAfterAction.averageStableBorrowRate; + expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate; + + expectedUserData.currentATokenBalance = calcExpectedATokenBalance( + reserveDataBeforeAction, + userDataBeforeAction, + txTimestamp + ); + + return expectedUserData; +}; + +const calcExpectedScaledATokenBalance = ( + userDataBeforeAction: UserReserveData, + index: BigNumber, + amountAdded: BigNumber, + amountTaken: BigNumber +) => { + return userDataBeforeAction.scaledATokenBalance + .plus(amountAdded.rayDiv(index)) + .minus(amountTaken.rayDiv(index)); +}; + +export const calcExpectedATokenBalance = ( + reserveData: ReserveData, + userData: UserReserveData, + currentTimestamp: BigNumber +) => { + const index = calcExpectedReserveNormalizedIncome(reserveData, currentTimestamp); + + const { scaledATokenBalance: scaledBalanceBeforeAction } = userData; + + return scaledBalanceBeforeAction.rayMul(index); +}; + +const calcExpectedAverageStableBorrowRate = ( + avgStableRateBefore: BigNumber, + totalStableDebtBefore: BigNumber, + amountChanged: string | BigNumber, + rate: BigNumber +) => { + const weightedTotalBorrows = avgStableRateBefore.multipliedBy(totalStableDebtBefore); + const weightedAmountBorrowed = rate.multipliedBy(amountChanged); + const totalBorrowedStable = totalStableDebtBefore.plus(amountChanged); + + if (totalBorrowedStable.eq(0)) return new BigNumber('0'); + + return weightedTotalBorrows + .plus(weightedAmountBorrowed) + .div(totalBorrowedStable) + .decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +const calcExpectedAverageStableBorrowRateRebalance = ( + avgStableRateBefore: BigNumber, + totalStableDebtBefore: BigNumber, + amountChanged: BigNumber, + rate: BigNumber +) => { + const weightedTotalBorrows = avgStableRateBefore.rayMul(totalStableDebtBefore); + const weightedAmountBorrowed = rate.rayMul(amountChanged.wadToRay()); + const totalBorrowedStable = totalStableDebtBefore.plus(amountChanged.wadToRay()); + + if (totalBorrowedStable.eq(0)) return new BigNumber('0'); + + return weightedTotalBorrows + .plus(weightedAmountBorrowed) + .rayDiv(totalBorrowedStable) + .decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +export const calcExpectedVariableDebtTokenBalance = ( + reserveData: ReserveData, + userData: UserReserveData, + currentTimestamp: BigNumber +) => { + const normalizedDebt = calcExpectedReserveNormalizedDebt( + reserveData.variableBorrowRate, + reserveData.variableBorrowIndex, + reserveData.lastUpdateTimestamp, + currentTimestamp + ); + + const { scaledVariableDebt } = userData; + + return scaledVariableDebt.rayMul(normalizedDebt); +}; + +export const calcExpectedStableDebtTokenBalance = ( + principalStableDebt: BigNumber, + stableBorrowRate: BigNumber, + stableRateLastUpdated: BigNumber, + currentTimestamp: BigNumber +) => { + if ( + stableBorrowRate.eq(0) || + currentTimestamp.eq(stableRateLastUpdated) || + stableRateLastUpdated.eq(0) + ) { + return principalStableDebt; + } + + const cumulatedInterest = calcCompoundedInterest( + stableBorrowRate, + currentTimestamp, + stableRateLastUpdated + ); + + return principalStableDebt.rayMul(cumulatedInterest); +}; + +const calcLinearInterest = ( + rate: BigNumber, + currentTimestamp: BigNumber, + lastUpdateTimestamp: BigNumber +) => { + const timeDifference = currentTimestamp.minus(lastUpdateTimestamp); + + const cumulatedInterest = rate + .multipliedBy(timeDifference) + .dividedBy(new BigNumber(ONE_YEAR)) + .plus(RAY); + + return cumulatedInterest; +}; + +const calcCompoundedInterest = ( + rate: BigNumber, + currentTimestamp: BigNumber, + lastUpdateTimestamp: BigNumber +) => { + const timeDifference = currentTimestamp.minus(lastUpdateTimestamp); + + if (timeDifference.eq(0)) { + return new BigNumber(RAY); + } + + const expMinusOne = timeDifference.minus(1); + const expMinusTwo = timeDifference.gt(2) ? timeDifference.minus(2) : 0; + + const ratePerSecond = rate.div(ONE_YEAR); + + const basePowerTwo = ratePerSecond.rayMul(ratePerSecond); + const basePowerThree = basePowerTwo.rayMul(ratePerSecond); + + const secondTerm = timeDifference.times(expMinusOne).times(basePowerTwo).div(2); + const thirdTerm = timeDifference + .times(expMinusOne) + .times(expMinusTwo) + .times(basePowerThree) + .div(6); + + return new BigNumber(RAY) + .plus(ratePerSecond.times(timeDifference)) + .plus(secondTerm) + .plus(thirdTerm); +}; + +export const calcExpectedInterestRates = ( + reserveSymbol: string, + marketStableRate: BigNumber, + utilizationRate: BigNumber, + totalStableDebt: BigNumber, + totalVariableDebt: BigNumber, + averageStableBorrowRate: BigNumber +): BigNumber[] => { + const { reservesParams } = configuration; + + // Fixes WETH - LpWETH mock token symbol mismatch + // if(reserveSymbol === 'WETH') { + // reserveSymbol = 'LpWETH'; + // } + const reserveIndex = Object.keys(reservesParams).findIndex((value) => value === reserveSymbol); + const [, reserveConfiguration] = (Object.entries(reservesParams) as [string, IReserveParams][])[ + reserveIndex + ]; + + let stableBorrowRate: BigNumber = marketStableRate; + let variableBorrowRate: BigNumber = new BigNumber(reserveConfiguration.strategy.baseVariableBorrowRate); + + const optimalRate = new BigNumber(reserveConfiguration.strategy.optimalUtilizationRate); + const excessRate = new BigNumber(RAY).minus(optimalRate); + if (utilizationRate.gt(optimalRate)) { + const excessUtilizationRateRatio = utilizationRate + .minus(reserveConfiguration.strategy.optimalUtilizationRate) + .rayDiv(excessRate); + + stableBorrowRate = stableBorrowRate + .plus(reserveConfiguration.strategy.stableRateSlope1) + .plus( + new BigNumber(reserveConfiguration.strategy.stableRateSlope2).rayMul(excessUtilizationRateRatio) + ); + + variableBorrowRate = variableBorrowRate + .plus(reserveConfiguration.strategy.variableRateSlope1) + .plus( + new BigNumber(reserveConfiguration.strategy.variableRateSlope2).rayMul(excessUtilizationRateRatio) + ); + } else { + stableBorrowRate = stableBorrowRate.plus( + new BigNumber(reserveConfiguration.strategy.stableRateSlope1).rayMul( + utilizationRate.rayDiv(new BigNumber(optimalRate)) + ) + ); + + variableBorrowRate = variableBorrowRate.plus( + utilizationRate + .rayDiv(optimalRate) + .rayMul(new BigNumber(reserveConfiguration.strategy.variableRateSlope1)) + ); + } + + const expectedOverallRate = calcExpectedOverallBorrowRate( + totalStableDebt, + totalVariableDebt, + variableBorrowRate, + averageStableBorrowRate + ); + const liquidityRate = expectedOverallRate + .rayMul(utilizationRate) + .percentMul(new BigNumber(PERCENTAGE_FACTOR).minus(reserveConfiguration.reserveFactor)); + + return [liquidityRate, stableBorrowRate, variableBorrowRate]; +}; + +export const calcExpectedOverallBorrowRate = ( + totalStableDebt: BigNumber, + totalVariableDebt: BigNumber, + currentVariableBorrowRate: BigNumber, + currentAverageStableBorrowRate: BigNumber +): BigNumber => { + const totalBorrows = totalStableDebt.plus(totalVariableDebt); + + if (totalBorrows.eq(0)) return strToBN('0'); + + const weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate); + + const weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate); + + const overallBorrowRate = weightedVariableRate + .plus(weightedStableRate) + .rayDiv(totalBorrows.wadToRay()); + + return overallBorrowRate; +}; + +export const calcExpectedUtilizationRate = ( + totalStableDebt: BigNumber, + totalVariableDebt: BigNumber, + totalLiquidity: BigNumber +): BigNumber => { + if (totalStableDebt.eq('0') && totalVariableDebt.eq('0')) { + return strToBN('0'); + } + + const utilization = totalStableDebt.plus(totalVariableDebt).rayDiv(totalLiquidity); + + return utilization; +}; + +const calcExpectedReserveNormalizedIncome = ( + reserveData: ReserveData, + currentTimestamp: BigNumber +) => { + const { liquidityRate, liquidityIndex, lastUpdateTimestamp } = reserveData; + + //if utilization rate is 0, nothing to compound + if (liquidityRate.eq('0')) { + return liquidityIndex; + } + + const cumulatedInterest = calcLinearInterest( + liquidityRate, + currentTimestamp, + lastUpdateTimestamp + ); + + const income = cumulatedInterest.rayMul(liquidityIndex); + + return income; +}; + +const calcExpectedReserveNormalizedDebt = ( + variableBorrowRate: BigNumber, + variableBorrowIndex: BigNumber, + lastUpdateTimestamp: BigNumber, + currentTimestamp: BigNumber +) => { + //if utilization rate is 0, nothing to compound + if (variableBorrowRate.eq('0')) { + return variableBorrowIndex; + } + + const cumulatedInterest = calcCompoundedInterest( + variableBorrowRate, + currentTimestamp, + lastUpdateTimestamp + ); + + const debt = cumulatedInterest.rayMul(variableBorrowIndex); + + return debt; +}; + +const calcExpectedUserStableRate = ( + balanceBefore: BigNumber, + rateBefore: BigNumber, + amount: BigNumber, + rateNew: BigNumber +) => { + return balanceBefore + .times(rateBefore) + .plus(amount.times(rateNew)) + .div(balanceBefore.plus(amount)); +}; + +const calcExpectedLiquidityIndex = (reserveData: ReserveData, timestamp: BigNumber) => { + //if utilization rate is 0, nothing to compound + if (reserveData.utilizationRate.eq('0')) { + return reserveData.liquidityIndex; + } + + const cumulatedInterest = calcLinearInterest( + reserveData.liquidityRate, + timestamp, + reserveData.lastUpdateTimestamp + ); + + return cumulatedInterest.rayMul(reserveData.liquidityIndex); +}; + +const calcExpectedVariableBorrowIndex = (reserveData: ReserveData, timestamp: BigNumber) => { + //if totalVariableDebt is 0, nothing to compound + if (reserveData.totalVariableDebt.eq('0')) { + return reserveData.variableBorrowIndex; + } + + const cumulatedInterest = calcCompoundedInterest( + reserveData.variableBorrowRate, + timestamp, + reserveData.lastUpdateTimestamp + ); + + return cumulatedInterest.rayMul(reserveData.variableBorrowIndex); +}; + +const calcExpectedTotalStableDebt = ( + principalStableDebt: BigNumber, + averageStableBorrowRate: BigNumber, + lastUpdateTimestamp: BigNumber, + currentTimestamp: BigNumber +) => { + const cumulatedInterest = calcCompoundedInterest( + averageStableBorrowRate, + currentTimestamp, + lastUpdateTimestamp + ); + + return cumulatedInterest.rayMul(principalStableDebt); +}; + +const calcExpectedTotalVariableDebt = ( + reserveData: ReserveData, + expectedVariableDebtIndex: BigNumber +) => { + return reserveData.scaledVariableDebt.rayMul(expectedVariableDebtIndex); +}; diff --git a/test-suites/test-amm/helpers/utils/helpers.ts b/test-suites/test-amm/helpers/utils/helpers.ts new file mode 100644 index 00000000..a1a1a82a --- /dev/null +++ b/test-suites/test-amm/helpers/utils/helpers.ts @@ -0,0 +1,129 @@ +import { LendingPool } from '../../../../types/LendingPool'; +import { ReserveData, UserReserveData } from './interfaces'; +import { + getLendingRateOracle, + getIErc20Detailed, + getMintableERC20, + getAToken, + getStableDebtToken, + getVariableDebtToken, +} from '../../../../helpers/contracts-getters'; +import { tEthereumAddress } from '../../../../helpers/types'; +import BigNumber from 'bignumber.js'; +import { getDb, DRE } from '../../../../helpers/misc-utils'; +import { AaveProtocolDataProvider } from '../../../../types/AaveProtocolDataProvider'; + +export const getReserveData = async ( + helper: AaveProtocolDataProvider, + reserve: tEthereumAddress +): Promise => { + const [reserveData, tokenAddresses, rateOracle, token] = await Promise.all([ + helper.getReserveData(reserve), + helper.getReserveTokensAddresses(reserve), + getLendingRateOracle(), + getIErc20Detailed(reserve), + ]); + + const stableDebtToken = await getStableDebtToken(tokenAddresses.stableDebtTokenAddress); + const variableDebtToken = await getVariableDebtToken(tokenAddresses.variableDebtTokenAddress); + + const { 0: principalStableDebt } = await stableDebtToken.getSupplyData(); + const totalStableDebtLastUpdated = await stableDebtToken.getTotalSupplyLastUpdated(); + + const scaledVariableDebt = await variableDebtToken.scaledTotalSupply(); + + const rate = (await rateOracle.getMarketBorrowRate(reserve)).toString(); + const symbol = await token.symbol(); + const decimals = new BigNumber(await token.decimals()); + + const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + const utilizationRate = new BigNumber( + totalLiquidity.eq(0) + ? 0 + : new BigNumber(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()) + .rayDiv(totalLiquidity) + ); + + return { + totalLiquidity, + utilizationRate, + availableLiquidity: new BigNumber(reserveData.availableLiquidity.toString()), + totalStableDebt: new BigNumber(reserveData.totalStableDebt.toString()), + totalVariableDebt: new BigNumber(reserveData.totalVariableDebt.toString()), + liquidityRate: new BigNumber(reserveData.liquidityRate.toString()), + variableBorrowRate: new BigNumber(reserveData.variableBorrowRate.toString()), + stableBorrowRate: new BigNumber(reserveData.stableBorrowRate.toString()), + averageStableBorrowRate: new BigNumber(reserveData.averageStableBorrowRate.toString()), + liquidityIndex: new BigNumber(reserveData.liquidityIndex.toString()), + variableBorrowIndex: new BigNumber(reserveData.variableBorrowIndex.toString()), + lastUpdateTimestamp: new BigNumber(reserveData.lastUpdateTimestamp), + totalStableDebtLastUpdated: new BigNumber(totalStableDebtLastUpdated), + principalStableDebt: new BigNumber(principalStableDebt.toString()), + scaledVariableDebt: new BigNumber(scaledVariableDebt.toString()), + address: reserve, + aTokenAddress: tokenAddresses.aTokenAddress, + symbol, + decimals, + marketStableRate: new BigNumber(rate), + }; +}; + +export const getUserData = async ( + pool: LendingPool, + helper: AaveProtocolDataProvider, + reserve: string, + user: tEthereumAddress, + sender?: tEthereumAddress +): Promise => { + const [userData, scaledATokenBalance] = await Promise.all([ + helper.getUserReserveData(reserve, user), + getATokenUserData(reserve, user, helper), + ]); + + const token = await getMintableERC20(reserve); + const walletBalance = new BigNumber((await token.balanceOf(sender || user)).toString()); + + return { + scaledATokenBalance: new BigNumber(scaledATokenBalance), + currentATokenBalance: new BigNumber(userData.currentATokenBalance.toString()), + currentStableDebt: new BigNumber(userData.currentStableDebt.toString()), + currentVariableDebt: new BigNumber(userData.currentVariableDebt.toString()), + principalStableDebt: new BigNumber(userData.principalStableDebt.toString()), + scaledVariableDebt: new BigNumber(userData.scaledVariableDebt.toString()), + stableBorrowRate: new BigNumber(userData.stableBorrowRate.toString()), + liquidityRate: new BigNumber(userData.liquidityRate.toString()), + usageAsCollateralEnabled: userData.usageAsCollateralEnabled, + stableRateLastUpdated: new BigNumber(userData.stableRateLastUpdated.toString()), + walletBalance, + }; +}; + +export const getReserveAddressFromSymbol = async (symbol: string) => { + + const token = await getMintableERC20( + (await getDb().get(`${symbol}.${DRE.network.name}`).value()).address + ); + + if (!token) { + throw `Could not find instance for contract ${symbol}`; + } + return token.address; +}; + +const getATokenUserData = async ( + reserve: string, + user: string, + helpersContract: AaveProtocolDataProvider +) => { + const aTokenAddress: string = (await helpersContract.getReserveTokensAddresses(reserve)) + .aTokenAddress; + + const aToken = await getAToken(aTokenAddress); + + const scaledBalance = await aToken.scaledBalanceOf(user); + return scaledBalance.toString(); +}; diff --git a/test-suites/test-amm/helpers/utils/interfaces/index.ts b/test-suites/test-amm/helpers/utils/interfaces/index.ts new file mode 100644 index 00000000..17660fcc --- /dev/null +++ b/test-suites/test-amm/helpers/utils/interfaces/index.ts @@ -0,0 +1,40 @@ +import BigNumber from 'bignumber.js'; + +export interface UserReserveData { + scaledATokenBalance: BigNumber; + currentATokenBalance: BigNumber; + currentStableDebt: BigNumber; + currentVariableDebt: BigNumber; + principalStableDebt: BigNumber; + scaledVariableDebt: BigNumber; + liquidityRate: BigNumber; + stableBorrowRate: BigNumber; + stableRateLastUpdated: BigNumber; + usageAsCollateralEnabled: Boolean; + walletBalance: BigNumber; + [key: string]: BigNumber | string | Boolean; +} + +export interface ReserveData { + address: string; + symbol: string; + decimals: BigNumber; + totalLiquidity: BigNumber; + availableLiquidity: BigNumber; + totalStableDebt: BigNumber; + totalVariableDebt: BigNumber; + principalStableDebt: BigNumber; + scaledVariableDebt: BigNumber; + averageStableBorrowRate: BigNumber; + variableBorrowRate: BigNumber; + stableBorrowRate: BigNumber; + utilizationRate: BigNumber; + liquidityIndex: BigNumber; + variableBorrowIndex: BigNumber; + aTokenAddress: string; + marketStableRate: BigNumber; + lastUpdateTimestamp: BigNumber; + totalStableDebtLastUpdated: BigNumber; + liquidityRate: BigNumber; + [key: string]: BigNumber | string; +} diff --git a/test-suites/test-amm/helpers/utils/math.ts b/test-suites/test-amm/helpers/utils/math.ts new file mode 100644 index 00000000..27ea0531 --- /dev/null +++ b/test-suites/test-amm/helpers/utils/math.ts @@ -0,0 +1,97 @@ +import BigNumber from 'bignumber.js'; +import { + RAY, + WAD, + HALF_RAY, + HALF_WAD, + WAD_RAY_RATIO, + HALF_PERCENTAGE, + PERCENTAGE_FACTOR, +} from '../../../../helpers/constants'; + +declare module 'bignumber.js' { + interface BigNumber { + ray: () => BigNumber; + wad: () => BigNumber; + halfRay: () => BigNumber; + halfWad: () => BigNumber; + halfPercentage: () => BigNumber; + wadMul: (a: BigNumber) => BigNumber; + wadDiv: (a: BigNumber) => BigNumber; + rayMul: (a: BigNumber) => BigNumber; + rayDiv: (a: BigNumber) => BigNumber; + percentMul: (a: BigNumber) => BigNumber; + percentDiv: (a: BigNumber) => BigNumber; + rayToWad: () => BigNumber; + wadToRay: () => BigNumber; + } +} + +BigNumber.prototype.ray = (): BigNumber => { + return new BigNumber(RAY).decimalPlaces(0); +}; +BigNumber.prototype.wad = (): BigNumber => { + return new BigNumber(WAD).decimalPlaces(0); +}; + +BigNumber.prototype.halfRay = (): BigNumber => { + return new BigNumber(HALF_RAY).decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.halfWad = (): BigNumber => { + return new BigNumber(HALF_WAD).decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.wadMul = function (b: BigNumber): BigNumber { + return this.halfWad().plus(this.multipliedBy(b)).div(WAD).decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.wadDiv = function (a: BigNumber): BigNumber { + const halfA = a.div(2).decimalPlaces(0, BigNumber.ROUND_DOWN); + + return halfA.plus(this.multipliedBy(WAD)).div(a).decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.rayMul = function (a: BigNumber): BigNumber { + return this.halfRay().plus(this.multipliedBy(a)).div(RAY).decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.rayDiv = function (a: BigNumber): BigNumber { + const halfA = a.div(2).decimalPlaces(0, BigNumber.ROUND_DOWN); + + return halfA + .plus(this.multipliedBy(RAY)) + .decimalPlaces(0, BigNumber.ROUND_DOWN) + .div(a) + .decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.rayToWad = function (): BigNumber { + const halfRatio = new BigNumber(WAD_RAY_RATIO).div(2); + + return halfRatio.plus(this).div(WAD_RAY_RATIO).decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.wadToRay = function (): BigNumber { + return this.multipliedBy(WAD_RAY_RATIO).decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.halfPercentage = (): BigNumber => { + return new BigNumber(HALF_PERCENTAGE).decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.percentMul = function (b: BigNumber): BigNumber { + return this.halfPercentage() + .plus(this.multipliedBy(b)) + .div(PERCENTAGE_FACTOR) + .decimalPlaces(0, BigNumber.ROUND_DOWN); +}; + +BigNumber.prototype.percentDiv = function (a: BigNumber): BigNumber { + const halfA = a.div(2).decimalPlaces(0, BigNumber.ROUND_DOWN); + + return halfA + .plus(this.multipliedBy(PERCENTAGE_FACTOR)) + .div(a) + .decimalPlaces(0, BigNumber.ROUND_DOWN); +}; diff --git a/test-suites/test-amm/lending-pool-addresses-provider.spec.ts b/test-suites/test-amm/lending-pool-addresses-provider.spec.ts new file mode 100644 index 00000000..0f7630f9 --- /dev/null +++ b/test-suites/test-amm/lending-pool-addresses-provider.spec.ts @@ -0,0 +1,101 @@ +import { expect } from 'chai'; +import { createRandomAddress } from '../../helpers/misc-utils'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors } from '../../helpers/types'; +import { ethers } from 'ethers'; +import { ZERO_ADDRESS } from '../../helpers/constants'; +import { waitForTx } from '../../helpers/misc-utils'; +import { deployLendingPool } from '../../helpers/contracts-deployments'; + +const { utils } = ethers; + +makeSuite('LendingPoolAddressesProvider', (testEnv: TestEnv) => { + it('Test the accessibility of the LendingPoolAddressesProvider', async () => { + const { addressesProvider, users } = testEnv; + const mockAddress = createRandomAddress(); + const { INVALID_OWNER_REVERT_MSG } = ProtocolErrors; + + await addressesProvider.transferOwnership(users[1].address); + + for (const contractFunction of [ + addressesProvider.setMarketId, + addressesProvider.setLendingPoolImpl, + addressesProvider.setLendingPoolConfiguratorImpl, + addressesProvider.setLendingPoolCollateralManager, + addressesProvider.setPoolAdmin, + addressesProvider.setPriceOracle, + addressesProvider.setLendingRateOracle, + ]) { + await expect(contractFunction(mockAddress)).to.be.revertedWith(INVALID_OWNER_REVERT_MSG); + } + + await expect( + addressesProvider.setAddress(utils.keccak256(utils.toUtf8Bytes('RANDOM_ID')), mockAddress) + ).to.be.revertedWith(INVALID_OWNER_REVERT_MSG); + + await expect( + addressesProvider.setAddressAsProxy( + utils.keccak256(utils.toUtf8Bytes('RANDOM_ID')), + mockAddress + ) + ).to.be.revertedWith(INVALID_OWNER_REVERT_MSG); + }); + + it('Tests adding a proxied address with `setAddressAsProxy()`', async () => { + const { addressesProvider, users } = testEnv; + const { INVALID_OWNER_REVERT_MSG } = ProtocolErrors; + + const currentAddressesProviderOwner = users[1]; + + const mockLendingPool = await deployLendingPool(); + const proxiedAddressId = utils.keccak256(utils.toUtf8Bytes('RANDOM_PROXIED')); + + const proxiedAddressSetReceipt = await waitForTx( + await addressesProvider + .connect(currentAddressesProviderOwner.signer) + .setAddressAsProxy(proxiedAddressId, mockLendingPool.address) + ); + + if (!proxiedAddressSetReceipt.events || proxiedAddressSetReceipt.events?.length < 1) { + throw new Error('INVALID_EVENT_EMMITED'); + } + + expect(proxiedAddressSetReceipt.events[0].event).to.be.equal('ProxyCreated'); + expect(proxiedAddressSetReceipt.events[1].event).to.be.equal('AddressSet'); + expect(proxiedAddressSetReceipt.events[1].args?.id).to.be.equal(proxiedAddressId); + expect(proxiedAddressSetReceipt.events[1].args?.newAddress).to.be.equal( + mockLendingPool.address + ); + expect(proxiedAddressSetReceipt.events[1].args?.hasProxy).to.be.equal(true); + }); + + it('Tests adding a non proxied address with `setAddress()`', async () => { + const { addressesProvider, users } = testEnv; + const { INVALID_OWNER_REVERT_MSG } = ProtocolErrors; + + const currentAddressesProviderOwner = users[1]; + const mockNonProxiedAddress = createRandomAddress(); + const nonProxiedAddressId = utils.keccak256(utils.toUtf8Bytes('RANDOM_NON_PROXIED')); + + const nonProxiedAddressSetReceipt = await waitForTx( + await addressesProvider + .connect(currentAddressesProviderOwner.signer) + .setAddress(nonProxiedAddressId, mockNonProxiedAddress) + ); + + expect(mockNonProxiedAddress.toLowerCase()).to.be.equal( + (await addressesProvider.getAddress(nonProxiedAddressId)).toLowerCase() + ); + + if (!nonProxiedAddressSetReceipt.events || nonProxiedAddressSetReceipt.events?.length < 1) { + throw new Error('INVALID_EVENT_EMMITED'); + } + + expect(nonProxiedAddressSetReceipt.events[0].event).to.be.equal('AddressSet'); + expect(nonProxiedAddressSetReceipt.events[0].args?.id).to.be.equal(nonProxiedAddressId); + expect(nonProxiedAddressSetReceipt.events[0].args?.newAddress).to.be.equal( + mockNonProxiedAddress + ); + expect(nonProxiedAddressSetReceipt.events[0].args?.hasProxy).to.be.equal(false); + }); +}); diff --git a/test-suites/test-amm/liquidation-atoken.spec.ts b/test-suites/test-amm/liquidation-atoken.spec.ts new file mode 100644 index 00000000..90296edb --- /dev/null +++ b/test-suites/test-amm/liquidation-atoken.spec.ts @@ -0,0 +1,379 @@ +import BigNumber from 'bignumber.js'; + +import { DRE } from '../../helpers/misc-utils'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { makeSuite } from './helpers/make-suite'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { calcExpectedVariableDebtTokenBalance } from './helpers/utils/calculations'; +import { getUserData, getReserveData } from './helpers/utils/helpers'; + +const chai = require('chai'); +const { expect } = chai; + +makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => { + const { + LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD, + INVALID_HF, + LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER, + LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED, + LP_IS_PAUSED, + } = ProtocolErrors; + + it('Deposits WETH, borrows DAI/Check liquidation fails because health factor is above 1', async () => { + const { dai, weth, users, pool, oracle } = testEnv; + const depositor = users[0]; + const borrower = users[1]; + + //mints DAI to depositor + await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + //approve protocol to access depositor wallet + await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //user 1 deposits 1000 DAI + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + await pool + .connect(depositor.signer) + .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); + + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + //mints WETH to borrower + await weth.connect(borrower.signer).mint(amountETHtoDeposit); + + //approve protocol to access borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //user 2 deposits 1 WETH + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + + //user 2 borrows + const userGlobalData = await pool.getUserAccountData(borrower.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + + const amountDAIToBorrow = await convertToCurrencyDecimals( + dai.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(daiPrice.toString()) + .multipliedBy(0.95) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0', borrower.address); + + const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); + + expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.bignumber.equal( + '8250', + 'Invalid liquidation threshold' + ); + + //someone tries to liquidate user 2 + await expect( + pool.liquidationCall(weth.address, dai.address, borrower.address, 1, true) + ).to.be.revertedWith(LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD); + }); + + it('Drop the health factor below 1', async () => { + const { dai, users, pool, oracle } = testEnv; + const borrower = users[1]; + + const daiPrice = await oracle.getAssetPrice(dai.address); + + await oracle.setAssetPrice( + dai.address, + new BigNumber(daiPrice.toString()).multipliedBy(1.15).toFixed(0) + ); + + const userGlobalData = await pool.getUserAccountData(borrower.address); + + expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt( + oneEther.toString(), + INVALID_HF + ); + }); + + it('Tries to liquidate a different currency than the loan principal', async () => { + const { pool, users, weth } = testEnv; + const borrower = users[1]; + //user 2 tries to borrow + await expect( + pool.liquidationCall(weth.address, weth.address, borrower.address, oneEther.toString(), true) + ).revertedWith(LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER); + }); + + it('Tries to liquidate a different collateral than the borrower collateral', async () => { + const { pool, dai, users } = testEnv; + const borrower = users[1]; + + await expect( + pool.liquidationCall(dai.address, dai.address, borrower.address, oneEther.toString(), true) + ).revertedWith(LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED); + }); + + it('Liquidates the borrow', async () => { + const { pool, dai, weth, aWETH, aDai, users, oracle, helpersContract, deployer } = testEnv; + const borrower = users[1]; + + //mints dai to the caller + + await dai.mint(await convertToCurrencyDecimals(dai.address, '1000')); + + //approve protocol to access depositor wallet + await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const daiReserveDataBefore = await getReserveData(helpersContract, dai.address); + const ethReserveDataBefore = await helpersContract.getReserveData(weth.address); + + const userReserveDataBefore = await getUserData( + pool, + helpersContract, + dai.address, + borrower.address + ); + + const amountToLiquidate = new BigNumber(userReserveDataBefore.currentVariableDebt.toString()) + .div(2) + .toFixed(0); + + const tx = await pool.liquidationCall( + weth.address, + dai.address, + borrower.address, + amountToLiquidate, + true + ); + + const userReserveDataAfter = await helpersContract.getUserReserveData( + dai.address, + borrower.address + ); + + const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); + + const daiReserveDataAfter = await helpersContract.getReserveData(dai.address); + const ethReserveDataAfter = await helpersContract.getReserveData(weth.address); + + const collateralPrice = (await oracle.getAssetPrice(weth.address)).toString(); + const principalPrice = (await oracle.getAssetPrice(dai.address)).toString(); + + const collateralDecimals = ( + await helpersContract.getReserveConfigurationData(weth.address) + ).decimals.toString(); + const principalDecimals = ( + await helpersContract.getReserveConfigurationData(dai.address) + ).decimals.toString(); + + const expectedCollateralLiquidated = new BigNumber(principalPrice) + .times(new BigNumber(amountToLiquidate).times(105)) + .times(new BigNumber(10).pow(collateralDecimals)) + .div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals))) + .decimalPlaces(0, BigNumber.ROUND_DOWN); + + if (!tx.blockNumber) { + expect(false, 'Invalid block number'); + return; + } + + const txTimestamp = new BigNumber( + (await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp + ); + + const variableDebtBeforeTx = calcExpectedVariableDebtTokenBalance( + daiReserveDataBefore, + userReserveDataBefore, + txTimestamp + ); + + expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt( + oneEther.toFixed(0), + 'Invalid health factor' + ); + + expect(userReserveDataAfter.currentVariableDebt.toString()).to.be.bignumber.almostEqual( + new BigNumber(variableDebtBeforeTx).minus(amountToLiquidate).toFixed(0), + 'Invalid user borrow balance after liquidation' + ); + + expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(daiReserveDataBefore.availableLiquidity.toString()) + .plus(amountToLiquidate) + .toFixed(0), + 'Invalid principal available liquidity' + ); + + //the liquidity index of the principal reserve needs to be bigger than the index before + expect(daiReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte( + daiReserveDataBefore.liquidityIndex.toString(), + 'Invalid liquidity index' + ); + + //the principal APY after a liquidation needs to be lower than the APY before + expect(daiReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt( + daiReserveDataBefore.liquidityRate.toString(), + 'Invalid liquidity APY' + ); + + expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(ethReserveDataBefore.availableLiquidity.toString()).toFixed(0), + 'Invalid collateral available liquidity' + ); + + expect( + (await helpersContract.getUserReserveData(weth.address, deployer.address)) + .usageAsCollateralEnabled + ).to.be.true; + }); + + it('User 3 deposits 1000 USDC, user 4 1 WETH, user 4 borrows at variable - drops HF, liquidates the borrow', async () => { + const { users, pool, usdc, oracle, weth, helpersContract } = testEnv; + const depositor = users[3]; + const borrower = users[4]; + + //mints USDC to depositor + await usdc + .connect(depositor.signer) + .mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + //approve protocol to access depositor wallet + await usdc.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //user 3 deposits 1000 USDC + const amountUSDCtoDeposit = await convertToCurrencyDecimals(usdc.address, '1000'); + + await pool + .connect(depositor.signer) + .deposit(usdc.address, amountUSDCtoDeposit, depositor.address, '0'); + + //user 4 deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + //mints WETH to borrower + await weth.connect(borrower.signer).mint(amountETHtoDeposit); + + //approve protocol to access borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + + //user 4 borrows + const userGlobalData = await pool.getUserAccountData(borrower.address); + + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const amountUSDCToBorrow = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(usdcPrice.toString()) + .multipliedBy(0.9502) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(usdc.address, amountUSDCToBorrow, RateMode.Variable, '0', borrower.address); + + //drops HF below 1 + + await oracle.setAssetPrice( + usdc.address, + new BigNumber(usdcPrice.toString()).multipliedBy(1.12).toFixed(0) + ); + + //mints dai to the liquidator + + await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + //approve protocol to access depositor wallet + await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const userReserveDataBefore = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const usdcReserveDataBefore = await helpersContract.getReserveData(usdc.address); + const ethReserveDataBefore = await helpersContract.getReserveData(weth.address); + + const amountToLiquidate = new BigNumber(userReserveDataBefore.currentVariableDebt.toString()) + .multipliedBy(0.5) + .toFixed(0); + + await pool.liquidationCall( + weth.address, + usdc.address, + borrower.address, + amountToLiquidate, + true + ); + + const userReserveDataAfter = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); + + const usdcReserveDataAfter = await helpersContract.getReserveData(usdc.address); + const ethReserveDataAfter = await helpersContract.getReserveData(weth.address); + + const collateralPrice = (await oracle.getAssetPrice(weth.address)).toString(); + const principalPrice = (await oracle.getAssetPrice(usdc.address)).toString(); + + const collateralDecimals = ( + await helpersContract.getReserveConfigurationData(weth.address) + ).decimals.toString(); + const principalDecimals = ( + await helpersContract.getReserveConfigurationData(usdc.address) + ).decimals.toString(); + + const expectedCollateralLiquidated = new BigNumber(principalPrice) + .times(new BigNumber(amountToLiquidate).times(105)) + .times(new BigNumber(10).pow(collateralDecimals)) + .div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals))) + .decimalPlaces(0, BigNumber.ROUND_DOWN); + + expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt( + oneEther.toFixed(0), + 'Invalid health factor' + ); + + expect(userReserveDataAfter.currentVariableDebt.toString()).to.be.bignumber.almostEqual( + new BigNumber(userReserveDataBefore.currentVariableDebt.toString()) + .minus(amountToLiquidate) + .toFixed(0), + 'Invalid user borrow balance after liquidation' + ); + + expect(usdcReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(usdcReserveDataBefore.availableLiquidity.toString()) + .plus(amountToLiquidate) + .toFixed(0), + 'Invalid principal available liquidity' + ); + + //the liquidity index of the principal reserve needs to be bigger than the index before + expect(usdcReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte( + usdcReserveDataBefore.liquidityIndex.toString(), + 'Invalid liquidity index' + ); + + //the principal APY after a liquidation needs to be lower than the APY before + expect(usdcReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt( + usdcReserveDataBefore.liquidityRate.toString(), + 'Invalid liquidity APY' + ); + + expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(ethReserveDataBefore.availableLiquidity.toString()).toFixed(0), + 'Invalid collateral available liquidity' + ); + }); +}); diff --git a/test-suites/test-amm/liquidation-underlying.spec.ts b/test-suites/test-amm/liquidation-underlying.spec.ts new file mode 100644 index 00000000..e44a2d80 --- /dev/null +++ b/test-suites/test-amm/liquidation-underlying.spec.ts @@ -0,0 +1,492 @@ +import BigNumber from 'bignumber.js'; + +import { DRE, increaseTime } from '../../helpers/misc-utils'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { makeSuite } from './helpers/make-suite'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { calcExpectedVariableDebtTokenBalance } from './helpers/utils/calculations'; +import { getReserveData, getUserData } from './helpers/utils/helpers'; +import { CommonsConfig } from '../../markets/amm/commons'; + +import { parseEther } from 'ethers/lib/utils'; + +const chai = require('chai'); + +const { expect } = chai; + +makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', (testEnv) => { + const { INVALID_HF } = ProtocolErrors; + + before('Before LendingPool liquidation: set config', () => { + BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN }); + }); + + after('After LendingPool liquidation: reset config', () => { + BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); + }); + + it("It's not possible to liquidate on a non-active collateral or a non active principal", async () => { + const { configurator, weth, pool, users, dai } = testEnv; + const user = users[1]; + await configurator.deactivateReserve(weth.address); + + await expect( + pool.liquidationCall(weth.address, dai.address, user.address, parseEther('1000'), false) + ).to.be.revertedWith('2'); + + await configurator.activateReserve(weth.address); + + await configurator.deactivateReserve(dai.address); + + await expect( + pool.liquidationCall(weth.address, dai.address, user.address, parseEther('1000'), false) + ).to.be.revertedWith('2'); + + await configurator.activateReserve(dai.address); + }); + + it('Deposits WETH, borrows DAI', async () => { + const { dai, weth, users, pool, oracle } = testEnv; + const depositor = users[0]; + const borrower = users[1]; + + //mints DAI to depositor + await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + //approve protocol to access depositor wallet + await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //user 1 deposits 1000 DAI + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool + .connect(depositor.signer) + .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); + //user 2 deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + //mints WETH to borrower + await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + + //approve protocol to access the borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + + //user 2 borrows + + const userGlobalData = await pool.getUserAccountData(borrower.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + + const amountDAIToBorrow = await convertToCurrencyDecimals( + dai.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(daiPrice.toString()) + .multipliedBy(0.95) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0', borrower.address); + + const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); + + expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.bignumber.equal( + '8250', + INVALID_HF + ); + }); + + it('Drop the health factor below 1', async () => { + const { dai, weth, users, pool, oracle } = testEnv; + const borrower = users[1]; + + const daiPrice = await oracle.getAssetPrice(dai.address); + + await oracle.setAssetPrice( + dai.address, + new BigNumber(daiPrice.toString()).multipliedBy(1.18).toFixed(0) + ); + + const userGlobalData = await pool.getUserAccountData(borrower.address); + + expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt( + oneEther.toFixed(0), + INVALID_HF + ); + }); + + it('Liquidates the borrow', async () => { + const { dai, weth, users, pool, oracle, helpersContract } = testEnv; + const liquidator = users[3]; + const borrower = users[1]; + + //mints dai to the liquidator + await dai.connect(liquidator.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + //approve protocol to access the liquidator wallet + await dai.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const daiReserveBefore = await getReserveData(helpersContract, dai.address); + const daiReserveDataBefore = await helpersContract.getReserveData(dai.address); + const ethReserveDataBefore = await helpersContract.getReserveData(weth.address); + + const userReserveDataBefore = await getUserData( + pool, + helpersContract, + dai.address, + borrower.address + ); + + const amountToLiquidate = userReserveDataBefore.currentVariableDebt.div(2).toFixed(0); + + await increaseTime(100); + + const tx = await pool + .connect(liquidator.signer) + .liquidationCall(weth.address, dai.address, borrower.address, amountToLiquidate, false); + + const userReserveDataAfter = await getUserData( + pool, + helpersContract, + dai.address, + borrower.address + ); + + const daiReserveDataAfter = await helpersContract.getReserveData(dai.address); + const ethReserveDataAfter = await helpersContract.getReserveData(weth.address); + + const collateralPrice = await oracle.getAssetPrice(weth.address); + const principalPrice = await oracle.getAssetPrice(dai.address); + + const collateralDecimals = ( + await helpersContract.getReserveConfigurationData(weth.address) + ).decimals.toString(); + const principalDecimals = ( + await helpersContract.getReserveConfigurationData(dai.address) + ).decimals.toString(); + + const expectedCollateralLiquidated = new BigNumber(principalPrice.toString()) + .times(new BigNumber(amountToLiquidate).times(105)) + .times(new BigNumber(10).pow(collateralDecimals)) + .div( + new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(100) + .decimalPlaces(0, BigNumber.ROUND_DOWN); + + if (!tx.blockNumber) { + expect(false, 'Invalid block number'); + return; + } + const txTimestamp = new BigNumber( + (await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp + ); + const reserve = await getReserveData + const variableDebtBeforeTx = calcExpectedVariableDebtTokenBalance( + daiReserveBefore, + userReserveDataBefore, + txTimestamp + ); + + expect(userReserveDataAfter.currentVariableDebt.toString()).to.be.bignumber.almostEqual( + variableDebtBeforeTx.minus(amountToLiquidate).toFixed(0), + 'Invalid user debt after liquidation' + ); + + //the liquidity index of the principal reserve needs to be bigger than the index before + expect(daiReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte( + daiReserveDataBefore.liquidityIndex.toString(), + 'Invalid liquidity index' + ); + + //the principal APY after a liquidation needs to be lower than the APY before + expect(daiReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt( + daiReserveDataBefore.liquidityRate.toString(), + 'Invalid liquidity APY' + ); + + expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(daiReserveDataBefore.availableLiquidity.toString()) + .plus(amountToLiquidate) + .toFixed(0), + 'Invalid principal available liquidity' + ); + + expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(ethReserveDataBefore.availableLiquidity.toString()) + .minus(expectedCollateralLiquidated) + .toFixed(0), + 'Invalid collateral available liquidity' + ); + }); + + it('User 3 deposits 1000 USDC, user 4 1 WETH, user 4 borrows - drops HF, liquidates the borrow', async () => { + const { usdc, users, pool, oracle, weth, helpersContract } = testEnv; + + const depositor = users[3]; + const borrower = users[4]; + const liquidator = users[5]; + + //mints USDC to depositor + await usdc + .connect(depositor.signer) + .mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + //approve protocol to access depositor wallet + await usdc.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //depositor deposits 1000 USDC + const amountUSDCtoDeposit = await convertToCurrencyDecimals(usdc.address, '1000'); + + await pool + .connect(depositor.signer) + .deposit(usdc.address, amountUSDCtoDeposit, depositor.address, '0'); + + //borrower deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + //mints WETH to borrower + await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + + //approve protocol to access the borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + + //borrower borrows + const userGlobalData = await pool.getUserAccountData(borrower.address); + + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const amountUSDCToBorrow = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(usdcPrice.toString()) + .multipliedBy(0.9502) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(usdc.address, amountUSDCToBorrow, RateMode.Variable, '0', borrower.address); + + //drops HF below 1 + await oracle.setAssetPrice( + usdc.address, + new BigNumber(usdcPrice.toString()).multipliedBy(1.12).toFixed(0) + ); + + //mints dai to the liquidator + + await usdc + .connect(liquidator.signer) + .mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + //approve protocol to access depositor wallet + await usdc.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const userReserveDataBefore = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const usdcReserveDataBefore = await helpersContract.getReserveData(usdc.address); + const ethReserveDataBefore = await helpersContract.getReserveData(weth.address); + + const amountToLiquidate = DRE.ethers.BigNumber.from( + userReserveDataBefore.currentVariableDebt.toString() + ) + .div(2) + .toString(); + + await pool + .connect(liquidator.signer) + .liquidationCall(weth.address, usdc.address, borrower.address, amountToLiquidate, false); + + const userReserveDataAfter = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); + + const usdcReserveDataAfter = await helpersContract.getReserveData(usdc.address); + const ethReserveDataAfter = await helpersContract.getReserveData(weth.address); + + const collateralPrice = await oracle.getAssetPrice(weth.address); + const principalPrice = await oracle.getAssetPrice(usdc.address); + + const collateralDecimals = ( + await helpersContract.getReserveConfigurationData(weth.address) + ).decimals.toString(); + const principalDecimals = ( + await helpersContract.getReserveConfigurationData(usdc.address) + ).decimals.toString(); + + const expectedCollateralLiquidated = new BigNumber(principalPrice.toString()) + .times(new BigNumber(amountToLiquidate).times(105)) + .times(new BigNumber(10).pow(collateralDecimals)) + .div( + new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(100) + .decimalPlaces(0, BigNumber.ROUND_DOWN); + + expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt( + oneEther.toFixed(0), + 'Invalid health factor' + ); + + expect(userReserveDataAfter.currentVariableDebt.toString()).to.be.bignumber.almostEqual( + new BigNumber(userReserveDataBefore.currentVariableDebt.toString()) + .minus(amountToLiquidate) + .toFixed(0), + 'Invalid user borrow balance after liquidation' + ); + + //the liquidity index of the principal reserve needs to be bigger than the index before + expect(usdcReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte( + usdcReserveDataBefore.liquidityIndex.toString(), + 'Invalid liquidity index' + ); + + //the principal APY after a liquidation needs to be lower than the APY before + expect(usdcReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt( + usdcReserveDataBefore.liquidityRate.toString(), + 'Invalid liquidity APY' + ); + + expect(usdcReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(usdcReserveDataBefore.availableLiquidity.toString()) + .plus(amountToLiquidate) + .toFixed(0), + 'Invalid principal available liquidity' + ); + + expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(ethReserveDataBefore.availableLiquidity.toString()) + .minus(expectedCollateralLiquidated) + .toFixed(0), + 'Invalid collateral available liquidity' + ); + }); + + it('User 4 deposits 10 AAVE - drops HF, liquidates the AAVE, which results on a lower amount being liquidated', async () => { + const { aave, usdc, users, pool, oracle, helpersContract } = testEnv; + + const depositor = users[3]; + const borrower = users[4]; + const liquidator = users[5]; + + //mints AAVE to borrower + await aave.connect(borrower.signer).mint(await convertToCurrencyDecimals(aave.address, '10')); + + //approve protocol to access the borrower wallet + await aave.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //borrower deposits 10 AAVE + const amountToDeposit = await convertToCurrencyDecimals(aave.address, '10'); + + await pool + .connect(borrower.signer) + .deposit(aave.address, amountToDeposit, borrower.address, '0'); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + //drops HF below 1 + await oracle.setAssetPrice( + usdc.address, + new BigNumber(usdcPrice.toString()).multipliedBy(1.14).toFixed(0) + ); + + //mints usdc to the liquidator + await usdc + .connect(liquidator.signer) + .mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + //approve protocol to access depositor wallet + await usdc.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const userReserveDataBefore = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const usdcReserveDataBefore = await helpersContract.getReserveData(usdc.address); + const aaveReserveDataBefore = await helpersContract.getReserveData(aave.address); + + const amountToLiquidate = new BigNumber(userReserveDataBefore.currentVariableDebt.toString()) + .div(2) + .decimalPlaces(0, BigNumber.ROUND_DOWN) + .toFixed(0); + + const collateralPrice = await oracle.getAssetPrice(aave.address); + const principalPrice = await oracle.getAssetPrice(usdc.address); + + await pool + .connect(liquidator.signer) + .liquidationCall(aave.address, usdc.address, borrower.address, amountToLiquidate, false); + + const userReserveDataAfter = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); + + const usdcReserveDataAfter = await helpersContract.getReserveData(usdc.address); + const aaveReserveDataAfter = await helpersContract.getReserveData(aave.address); + + const aaveConfiguration = await helpersContract.getReserveConfigurationData(aave.address); + const collateralDecimals = aaveConfiguration.decimals.toString(); + const liquidationBonus = aaveConfiguration.liquidationBonus.toString(); + + const principalDecimals = ( + await helpersContract.getReserveConfigurationData(usdc.address) + ).decimals.toString(); + + const expectedCollateralLiquidated = oneEther.multipliedBy('10'); + + const expectedPrincipal = new BigNumber(collateralPrice.toString()) + .times(expectedCollateralLiquidated) + .times(new BigNumber(10).pow(principalDecimals)) + .div( + new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) + ) + .times(10000) + .div(liquidationBonus.toString()) + .decimalPlaces(0, BigNumber.ROUND_DOWN); + + expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt( + oneEther.toFixed(0), + 'Invalid health factor' + ); + + expect(userReserveDataAfter.currentVariableDebt.toString()).to.be.bignumber.almostEqual( + new BigNumber(userReserveDataBefore.currentVariableDebt.toString()) + .minus(expectedPrincipal) + .toFixed(0), + 'Invalid user borrow balance after liquidation' + ); + + expect(usdcReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(usdcReserveDataBefore.availableLiquidity.toString()) + .plus(expectedPrincipal) + .toFixed(0), + 'Invalid principal available liquidity' + ); + + expect(aaveReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( + new BigNumber(aaveReserveDataBefore.availableLiquidity.toString()) + .minus(expectedCollateralLiquidated) + .toFixed(0), + 'Invalid collateral available liquidity' + ); + }); +}); diff --git a/test/weth-gateway.spec.ts b/test-suites/test-amm/mainnet/check-list.spec.ts similarity index 90% rename from test/weth-gateway.spec.ts rename to test-suites/test-amm/mainnet/check-list.spec.ts index eadf9703..a741dab4 100644 --- a/test/weth-gateway.spec.ts +++ b/test-suites/test-amm/mainnet/check-list.spec.ts @@ -1,28 +1,25 @@ -import { MAX_UINT_AMOUNT } from '../helpers/constants'; -import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; -import { makeSuite, TestEnv } from './helpers/make-suite'; +import { MAX_UINT_AMOUNT } from '../../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../../helpers/contracts-helpers'; +import { makeSuite, TestEnv } from '../helpers/make-suite'; import { parseEther } from 'ethers/lib/utils'; -import { DRE, waitForTx } from '../helpers/misc-utils'; +import { DRE, waitForTx } from '../../../helpers/misc-utils'; import { BigNumber } from 'ethers'; -import { getStableDebtToken, getVariableDebtToken } from '../helpers/contracts-getters'; -import { deploySelfdestructTransferMock } from '../helpers/contracts-deployments'; +import { getStableDebtToken, getVariableDebtToken } from '../../../helpers/contracts-getters'; +import { deploySelfdestructTransferMock } from '../../../helpers/contracts-deployments'; +import { IUniswapV2Router02Factory } from '../../../types/IUniswapV2Router02Factory'; const { expect } = require('chai'); -makeSuite('Use native ETH at LendingPool via WETHGateway', (testEnv: TestEnv) => { +const UNISWAP_ROUTER = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'; + +makeSuite('Mainnet Check list', (testEnv: TestEnv) => { const zero = BigNumber.from('0'); const depositSize = parseEther('5'); const daiSize = parseEther('10000'); - it('Deposit WETH via WethGateway and DAI', async () => { - const { users, wethGateway, aWETH } = testEnv; + it('Deposit WETH', async () => { + const { users, wethGateway, aWETH, pool } = testEnv; const user = users[1]; - const depositor = users[0]; - - // Deposit liquidity with native ETH - await wethGateway - .connect(depositor.signer) - .depositETH(depositor.address, '0', { value: depositSize }); // Deposit with native ETH await wethGateway.connect(user.signer).depositETH(user.address, '0', { value: depositSize }); @@ -102,16 +99,10 @@ makeSuite('Use native ETH at LendingPool via WETHGateway', (testEnv: TestEnv) => }); it('Borrow stable WETH and Full Repay with ETH', async () => { - const { users, wethGateway, aDai, weth, dai, pool, helpersContract } = testEnv; + const { users, wethGateway, aWETH, dai, aDai, weth, pool, helpersContract } = testEnv; const borrowSize = parseEther('1'); const repaySize = borrowSize.add(borrowSize.mul(5).div(100)); const user = users[1]; - const depositor = users[0]; - - // Deposit with native ETH - await wethGateway - .connect(depositor.signer) - .depositETH(depositor.address, '0', { value: depositSize }); const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( weth.address @@ -147,10 +138,6 @@ makeSuite('Use native ETH at LendingPool via WETHGateway', (testEnv: TestEnv) => const debtBalanceAfterRepay = await stableDebtToken.balanceOf(user.address); expect(debtBalanceAfterRepay).to.be.eq(zero); - - // Withdraw DAI - await aDai.connect(user.signer).approve(pool.address, MAX_UINT_AMOUNT); - await pool.connect(user.signer).withdraw(dai.address, MAX_UINT_AMOUNT, user.address); }); it('Borrow variable WETH and Full Repay with ETH', async () => { @@ -310,11 +297,20 @@ makeSuite('Use native ETH at LendingPool via WETHGateway', (testEnv: TestEnv) => }); it('Owner can do emergency token recovery', async () => { - const { users, dai, wethGateway, deployer } = testEnv; + const { users, weth, dai, wethGateway, deployer } = testEnv; const user = users[0]; const amount = parseEther('1'); - await dai.connect(user.signer).mint(amount); + const uniswapRouter = IUniswapV2Router02Factory.connect(UNISWAP_ROUTER, user.signer); + await uniswapRouter.swapETHForExactTokens( + amount, // 1 DAI + [weth.address, dai.address], // Uniswap paths WETH - DAI + user.address, + (await DRE.ethers.provider.getBlock('latest')).timestamp + 300, + { + value: amount, // 1 Ether, we get refund of the unneeded Ether to buy 1 DAI + } + ); const daiBalanceAfterMint = await dai.balanceOf(user.address); await dai.connect(user.signer).transfer(wethGateway.address, amount); diff --git a/test-suites/test-amm/pausable-functions.spec.ts b/test-suites/test-amm/pausable-functions.spec.ts new file mode 100644 index 00000000..2096b235 --- /dev/null +++ b/test-suites/test-amm/pausable-functions.spec.ts @@ -0,0 +1,330 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { parseEther, parseUnits } from 'ethers/lib/utils'; +import { BigNumber } from 'bignumber.js'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; + +const { expect } = require('chai'); + +makeSuite('Pausable Pool', (testEnv: TestEnv) => { + let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; + + const { + LP_IS_PAUSED, + INVALID_FROM_BALANCE_AFTER_TRANSFER, + INVALID_TO_BALANCE_AFTER_TRANSFER, + } = ProtocolErrors; + + before(async () => { + _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); + }); + + it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succees', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + const user0Balance = await aDai.balanceOf(users[0].address); + const user1Balance = await aDai.balanceOf(users[1].address); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).setPoolPause(true); + + // User 0 tries the transfer to User 1 + await expect( + aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit) + ).to.revertedWith(LP_IS_PAUSED); + + const pausedFromBalance = await aDai.balanceOf(users[0].address); + const pausedToBalance = await aDai.balanceOf(users[1].address); + + expect(pausedFromBalance).to.be.equal( + user0Balance.toString(), + INVALID_TO_BALANCE_AFTER_TRANSFER + ); + expect(pausedToBalance.toString()).to.be.equal( + user1Balance.toString(), + INVALID_FROM_BALANCE_AFTER_TRANSFER + ); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).setPoolPause(false); + + // User 0 succeeds transfer to User 1 + await aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit); + + const fromBalance = await aDai.balanceOf(users[0].address); + const toBalance = await aDai.balanceOf(users[1].address); + + expect(fromBalance.toString()).to.be.equal( + user0Balance.sub(amountDAItoDeposit), + INVALID_FROM_BALANCE_AFTER_TRANSFER + ); + expect(toBalance.toString()).to.be.equal( + user1Balance.add(amountDAItoDeposit), + INVALID_TO_BALANCE_AFTER_TRANSFER + ); + }); + + it('Deposit', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).setPoolPause(true); + await expect( + pool.connect(users[0].signer).deposit(dai.address, amountDAItoDeposit, users[0].address, '0') + ).to.revertedWith(LP_IS_PAUSED); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); + + it('Withdraw', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).setPoolPause(true); + + // user tries to burn + await expect( + pool.connect(users[0].signer).withdraw(dai.address, amountDAItoDeposit, users[0].address) + ).to.revertedWith(LP_IS_PAUSED); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); + + it('Borrow', async () => { + const { pool, dai, users, configurator } = testEnv; + + const user = users[1]; + // Pause the pool + await configurator.connect(users[1].signer).setPoolPause(true); + + // Try to execute liquidation + await expect( + pool.connect(user.signer).borrow(dai.address, '1', '2', '0', user.address) + ).revertedWith(LP_IS_PAUSED); + + // Unpause the pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); + + it('Repay', async () => { + const { pool, dai, users, configurator } = testEnv; + + const user = users[1]; + // Pause the pool + await configurator.connect(users[1].signer).setPoolPause(true); + + // Try to execute liquidation + await expect(pool.connect(user.signer).repay(dai.address, '1', '2', user.address)).revertedWith( + LP_IS_PAUSED + ); + + // Unpause the pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); + + it('Flash loan', async () => { + const { dai, pool, weth, users, configurator } = testEnv; + + const caller = users[3]; + + const flashAmount = parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + // Pause pool + await configurator.connect(users[1].signer).setPoolPause(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [2], + caller.address, + '0x10', + '0' + ) + ).revertedWith(LP_IS_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); + + it('Liquidation call', async () => { + const { users, pool, usdc, oracle, weth, configurator, helpersContract } = testEnv; + const depositor = users[3]; + const borrower = users[4]; + + //mints USDC to depositor + await usdc + .connect(depositor.signer) + .mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + //approve protocol to access depositor wallet + await usdc.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //user 3 deposits 1000 USDC + const amountUSDCtoDeposit = await convertToCurrencyDecimals(usdc.address, '1000'); + + await pool + .connect(depositor.signer) + .deposit(usdc.address, amountUSDCtoDeposit, depositor.address, '0'); + + //user 4 deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + //mints WETH to borrower + await weth.connect(borrower.signer).mint(amountETHtoDeposit); + + //approve protocol to access borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + + //user 4 borrows + const userGlobalData = await pool.getUserAccountData(borrower.address); + + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const amountUSDCToBorrow = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(usdcPrice.toString()) + .multipliedBy(0.9502) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(usdc.address, amountUSDCToBorrow, RateMode.Variable, '0', borrower.address); + + // Drops HF below 1 + await oracle.setAssetPrice( + usdc.address, + new BigNumber(usdcPrice.toString()).multipliedBy(1.2).toFixed(0) + ); + + //mints dai to the liquidator + await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000')); + await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const userReserveDataBefore = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const amountToLiquidate = new BigNumber(userReserveDataBefore.currentVariableDebt.toString()) + .multipliedBy(0.5) + .toFixed(0); + + // Pause pool + await configurator.connect(users[1].signer).setPoolPause(true); + + // Do liquidation + await expect( + pool.liquidationCall(weth.address, usdc.address, borrower.address, amountToLiquidate, true) + ).revertedWith(LP_IS_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); + + it('SwapBorrowRateMode should fail because pool is paused', async () => { + const { pool, weth, dai, usdc, users, configurator } = testEnv; + const user = users[1]; + const amountWETHToDeposit = parseEther('10'); + const amountDAIToDeposit = parseEther('120'); + const amountToBorrow = parseUnits('65', 6); + + await weth.connect(user.signer).mint(amountWETHToDeposit); + await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0'); + + await dai.connect(user.signer).mint(amountDAIToDeposit); + await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0'); + + await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address); + + // Pause pool + await configurator.connect(users[1].signer).setPoolPause(true); + + // Try to repay + await expect( + pool.connect(user.signer).swapBorrowRateMode(usdc.address, RateMode.Stable) + ).revertedWith(LP_IS_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); + + it('RebalanceStableBorrowRate should fail because the pool is paused, even if there is no stable borrow', async () => { + const { pool, dai, users, configurator } = testEnv; + const user = users[1]; + // Pause pool + await configurator.connect(users[1].signer).setPoolPause(true); + + await expect( + pool.connect(user.signer).rebalanceStableBorrowRate(dai.address, user.address) + ).revertedWith(LP_IS_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); + + it('setUserUseReserveAsCollateral', async () => { + const { pool, weth, users, configurator } = testEnv; + const user = users[1]; + + const amountWETHToDeposit = parseEther('1'); + await weth.connect(user.signer).mint(amountWETHToDeposit); + await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0'); + + // Pause pool + await configurator.connect(users[1].signer).setPoolPause(true); + + await expect( + pool.connect(user.signer).setUserUseReserveAsCollateral(weth.address, false) + ).revertedWith(LP_IS_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).setPoolPause(false); + }); +}); diff --git a/test/pool-modifiers.spec.ts b/test-suites/test-amm/pool-modifiers.spec.ts similarity index 100% rename from test/pool-modifiers.spec.ts rename to test-suites/test-amm/pool-modifiers.spec.ts diff --git a/test-suites/test-amm/scenario.spec.ts b/test-suites/test-amm/scenario.spec.ts new file mode 100644 index 00000000..f9c4d78b --- /dev/null +++ b/test-suites/test-amm/scenario.spec.ts @@ -0,0 +1,44 @@ +import { configuration as actionsConfiguration } from './helpers/actions'; +import { configuration as calculationsConfiguration } from './helpers/utils/calculations'; + +import fs from 'fs'; +import BigNumber from 'bignumber.js'; +import { makeSuite } from './helpers/make-suite'; +import { getReservesConfigByPool } from '../../helpers/configuration'; +import { AavePools, iLpPoolAssets, IReserveParams } from '../../helpers/types'; +import { executeStory } from './helpers/scenario-engine'; + +const scenarioFolder = './test-suites/test-amm/helpers/scenarios/'; + +const selectedScenarios: string[] = []; + +fs.readdirSync(scenarioFolder).forEach((file) => { + if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return; + + const scenario = require(`./helpers/scenarios/${file}`); + + makeSuite(scenario.title, async (testEnv) => { + before('Initializing configuration', async () => { + // Sets BigNumber for this suite, instead of globally + BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN }); + + actionsConfiguration.skipIntegrityCheck = false; //set this to true to execute solidity-coverage + + calculationsConfiguration.reservesParams = >( + getReservesConfigByPool(AavePools.amm) + ); + }); + after('Reset', () => { + // Reset BigNumber + BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); + }); + + for (const story of scenario.stories) { + it(story.description, async function () { + // Retry the test scenarios up to 4 times if an error happens, due erratic HEVM network errors + this.retries(4); + await executeStory(story, testEnv); + }); + } + }); +}); diff --git a/test-suites/test-amm/stable-rate-economy.spec.ts b/test-suites/test-amm/stable-rate-economy.spec.ts new file mode 100644 index 00000000..1c2c156a --- /dev/null +++ b/test-suites/test-amm/stable-rate-economy.spec.ts @@ -0,0 +1,199 @@ +// import { +// LendingPoolInstance, +// LendingPoolCoreInstance, +// MintableERC20Instance, +// ATokenInstance, +// } from "../utils/typechain-types/truffle-contracts" +// import { +// iATokenBase, +// iAssetsWithoutETH, +// ITestEnvWithoutInstances, +// RateMode, +// } from "../utils/types" +// import { +// APPROVAL_AMOUNT_LENDING_POOL_CORE, +// ETHEREUM_ADDRESS, +// } from "../utils/constants" +// import { testEnvProviderWithoutInstances} from "../utils/truffle/dlp-tests-env" +// import {convertToCurrencyDecimals} from "../utils/misc-utils" + +// const expectRevert = require("@openzeppelin/test-helpers").expectRevert + +// contract("LendingPool - stable rate economy tests", async ([deployer, ...users]) => { +// let _testEnvProvider: ITestEnvWithoutInstances +// let _lendingPoolInstance: LendingPoolInstance +// let _lendingPoolCoreInstance: LendingPoolCoreInstance +// let _aTokenInstances: iATokenBase +// let _tokenInstances: iAssetsWithoutETH + +// let _daiAddress: string + +// let _depositorAddress: string +// let _borrowerAddress: string + +// let _web3: Web3 + +// before("Initializing LendingPool test variables", async () => { +// console.time('setup-test'); +// _testEnvProvider = await testEnvProviderWithoutInstances( +// artifacts, +// [deployer, ...users] +// ) + +// const { +// getWeb3, +// getAllAssetsInstances, +// getFirstBorrowerAddressOnTests, +// getFirstDepositorAddressOnTests, +// getLendingPoolInstance, +// getLendingPoolCoreInstance, +// getATokenInstances +// } = _testEnvProvider + +// const instances = await Promise.all([ +// getLendingPoolInstance(), +// getLendingPoolCoreInstance(), +// getATokenInstances(), +// getAllAssetsInstances() +// ]) +// _lendingPoolInstance = instances[0] +// _lendingPoolCoreInstance = instances[1] +// _aTokenInstances = instances[2] +// _tokenInstances = instances[3] +// _daiAddress = _tokenInstances.DAI.address +// _depositorAddress = await getFirstDepositorAddressOnTests() +// _borrowerAddress = await getFirstBorrowerAddressOnTests() + +// _web3 = await getWeb3() +// console.timeEnd('setup-test'); +// }) + +// it("BORROW - Test user cannot borrow using the same currency as collateral", async () => { +// const {aDAI: aDaiInstance} = _aTokenInstances +// const {DAI: daiInstance} = _tokenInstances + +// //mints DAI to depositor +// await daiInstance.mint(await convertToCurrencyDecimals(daiInstance.address, "1000"), { +// from: _depositorAddress, +// }) + +// //mints DAI to borrower +// await daiInstance.mint(await convertToCurrencyDecimals(daiInstance.address, "1000"), { +// from: _borrowerAddress, +// }) + +// //approve protocol to access depositor wallet +// await daiInstance.approve(_lendingPoolCoreInstance.address, APPROVAL_AMOUNT_LENDING_POOL_CORE, { +// from: _depositorAddress, +// }) + +// //approve protocol to access borrower wallet +// await daiInstance.approve(_lendingPoolCoreInstance.address, APPROVAL_AMOUNT_LENDING_POOL_CORE, { +// from: _borrowerAddress, +// }) + +// const amountDAItoDeposit = await convertToCurrencyDecimals(_daiAddress, "1000") + +// //user 1 deposits 1000 DAI +// const txResult = await _lendingPoolInstance.deposit(_daiAddress, amountDAItoDeposit, "0", { +// from: _depositorAddress, +// }) + +// //user 2 deposits 1000 DAI, tries to borrow. Needs to be reverted as you can't borrow at a stable rate with the same collateral as the currency. +// const amountDAIToDepositBorrower = await convertToCurrencyDecimals(_daiAddress, "1000") +// await _lendingPoolInstance.deposit(_daiAddress, amountDAIToDepositBorrower, "0", { +// from: _borrowerAddress, +// }) + +// const data: any = await _lendingPoolInstance.getReserveData(_daiAddress) + +// //user 2 tries to borrow +// const amountDAIToBorrow = await convertToCurrencyDecimals(_daiAddress, "250") + +// //user 2 tries to borrow +// await expectRevert( +// _lendingPoolInstance.borrow(_daiAddress, amountDAIToBorrow, RateMode.Stable, "0", { +// from: _borrowerAddress, +// }), +// "User cannot borrow the selected amount with a stable rate", +// ) +// }) + +// it("BORROW - Test user cannot borrow more than 25% of the liquidity available", async () => { +// const {aDAI: aDaiInstance} = _aTokenInstances +// const {DAI: daiInstance} = _tokenInstances + +// //redeem the DAI previously deposited +// const amountADAIToRedeem = await convertToCurrencyDecimals(aDaiInstance.address, "1000") +// await aDaiInstance.redeem(amountADAIToRedeem, { +// from: _borrowerAddress, +// }) + +// //user 2 deposits 5 ETH tries to borrow. needs to be reverted as you can't borrow more than 25% of the available reserve (250 DAI) +// const amountETHToDeposit = await convertToCurrencyDecimals(ETHEREUM_ADDRESS, "5") +// await _lendingPoolInstance.deposit(ETHEREUM_ADDRESS, amountETHToDeposit, "0", { +// from: _borrowerAddress, +// value: amountETHToDeposit, +// }) + +// const data: any = await _lendingPoolInstance.getReserveData(_daiAddress) + +// const amountDAIToBorrow = await convertToCurrencyDecimals(_daiAddress, "500") + +// //user 2 tries to borrow +// await expectRevert( +// _lendingPoolInstance.borrow(_daiAddress, amountDAIToBorrow, RateMode.Stable, "0", { +// from: _borrowerAddress, +// }), +// "User is trying to borrow too much liquidity at a stable rate", +// ) +// }) + +// it("BORROW - Test user can still borrow a currency that he previously deposited as a collateral but he transferred/redeemed", async () => { +// const {aDAI: aDaiInstance} = _aTokenInstances +// const {DAI: daiInstance} = _tokenInstances + +// const user = users[2] + +// //user deposits 1000 DAI +// await daiInstance.mint(await convertToCurrencyDecimals(daiInstance.address, "1000"), { +// from: user, +// }) +// await daiInstance.approve(_lendingPoolCoreInstance.address, APPROVAL_AMOUNT_LENDING_POOL_CORE, { +// from: user, +// }) + +// const amountDAIToDeposit = await convertToCurrencyDecimals(daiInstance.address, "1000") +// await _lendingPoolInstance.deposit(daiInstance.address, amountDAIToDeposit, "0", { +// from: user, +// }) + +// //user deposits 5 ETH as collateral +// const amountETHToDeposit = await convertToCurrencyDecimals(ETHEREUM_ADDRESS, "5") +// await _lendingPoolInstance.deposit(ETHEREUM_ADDRESS, amountETHToDeposit, "0", { +// from: user, +// value: amountETHToDeposit, +// }) + +// //user transfers to another address all the overlying aDAI + +// const aDAIBalance = await aDaiInstance.balanceOf(user) + +// await aDaiInstance.transfer(users[3], aDAIBalance, { +// from: user, +// }) + +// //check the underlying balance is 0 +// const userData: any = await _lendingPoolInstance.getUserReserveData(daiInstance.address, user) + +// expect(userData.currentATokenBalance.toString()).to.be.equal("0") + +// //user tries to borrow the DAI at a stable rate using the ETH as collateral +// const amountDAIToBorrow = await convertToCurrencyDecimals(_daiAddress, "100") + +// //user tries to borrow. No revert expected +// await _lendingPoolInstance.borrow(_daiAddress, amountDAIToBorrow, RateMode.Stable, "0", { +// from: user, +// }) +// }) +// }) diff --git a/test-suites/test-amm/stable-token.spec.ts b/test-suites/test-amm/stable-token.spec.ts new file mode 100644 index 00000000..d3c75fc6 --- /dev/null +++ b/test-suites/test-amm/stable-token.spec.ts @@ -0,0 +1,37 @@ +import { expect } from 'chai'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors } from '../../helpers/types'; +import { getStableDebtToken } from '../../helpers/contracts-getters'; + +makeSuite('Stable debt token tests', (testEnv: TestEnv) => { + const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors; + + it('Tries to invoke mint not being the LendingPool', async () => { + const { deployer, pool, dai, helpersContract } = testEnv; + + const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address)) + .stableDebtTokenAddress; + + const stableDebtContract = await getStableDebtToken(daiStableDebtTokenAddress); + + await expect( + stableDebtContract.mint(deployer.address, deployer.address, '1', '1') + ).to.be.revertedWith(CT_CALLER_MUST_BE_LENDING_POOL); + }); + + it('Tries to invoke burn not being the LendingPool', async () => { + const { deployer, dai, helpersContract } = testEnv; + + const daiStableDebtTokenAddress = (await helpersContract.getReserveTokensAddresses(dai.address)) + .stableDebtTokenAddress; + + const stableDebtContract = await getStableDebtToken(daiStableDebtTokenAddress); + + const name = await stableDebtContract.name(); + + expect(name).to.be.equal('Aave AMM Market stable debt DAI'); + await expect(stableDebtContract.burn(deployer.address, '1')).to.be.revertedWith( + CT_CALLER_MUST_BE_LENDING_POOL + ); + }); +}); diff --git a/test-suites/test-amm/subgraph-scenarios.spec.ts b/test-suites/test-amm/subgraph-scenarios.spec.ts new file mode 100644 index 00000000..a622eb34 --- /dev/null +++ b/test-suites/test-amm/subgraph-scenarios.spec.ts @@ -0,0 +1,32 @@ +import { configuration as actionsConfiguration } from './helpers/actions'; +import { configuration as calculationsConfiguration } from './helpers/utils/calculations'; + +import BigNumber from 'bignumber.js'; +import { makeSuite } from './helpers/make-suite'; +import { getReservesConfigByPool } from '../../helpers/configuration'; +import { AavePools, iLpPoolAssets, IReserveParams } from '../../helpers/types'; +import { executeStory } from './helpers/scenario-engine'; + +makeSuite('Subgraph scenario tests', async (testEnv) => { + let story: any; + let scenario; + before('Initializing configuration', async () => { + const scenario = require(`./helpers/scenarios/borrow-repay-stable`); + story = scenario.stories[0]; + // Sets BigNumber for this suite, instead of globally + BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN }); + + actionsConfiguration.skipIntegrityCheck = false; //set this to true to execute solidity-coverage + + calculationsConfiguration.reservesParams = >( + getReservesConfigByPool(AavePools.amm) + ); + }); + after('Reset', () => { + // Reset BigNumber + BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); + }); + it('deposit-borrow', async () => { + await executeStory(story, testEnv); + }); +}); diff --git a/test-suites/test-amm/uniswapAdapters.base.spec.ts b/test-suites/test-amm/uniswapAdapters.base.spec.ts new file mode 100644 index 00000000..85134cb0 --- /dev/null +++ b/test-suites/test-amm/uniswapAdapters.base.spec.ts @@ -0,0 +1,227 @@ +// import { makeSuite, TestEnv } from './helpers/make-suite'; +// import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +// import { getMockUniswapRouter } from '../../helpers/contracts-getters'; +// import { MockUniswapV2Router02 } from '../../types/MockUniswapV2Router02'; +// import BigNumber from 'bignumber.js'; +// import { evmRevert, evmSnapshot } from '../../helpers/misc-utils'; +// import { ethers } from 'ethers'; +// import { USD_ADDRESS } from '../../helpers/constants'; +// const { parseEther } = ethers.utils; + +// const { expect } = require('chai'); + +// makeSuite('Uniswap adapters', (testEnv: TestEnv) => { +// let mockUniswapRouter: MockUniswapV2Router02; +// let evmSnapshotId: string; + +// before(async () => { +// mockUniswapRouter = await getMockUniswapRouter(); +// }); + +// beforeEach(async () => { +// evmSnapshotId = await evmSnapshot(); +// }); + +// afterEach(async () => { +// await evmRevert(evmSnapshotId); +// }); + +// describe('BaseUniswapAdapter', () => { +// describe('getAmountsOut', () => { +// it('should return the estimated amountOut and prices for the asset swap', async () => { +// const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; + +// const amountIn = parseEther('1'); +// const flashloanPremium = amountIn.mul(9).div(10000); +// const amountToSwap = amountIn.sub(flashloanPremium); + +// const wethPrice = await oracle.getAssetPrice(weth.address); +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountToSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// const outPerInPrice = amountToSwap +// .mul(parseEther('1')) +// .mul(parseEther('1')) +// .div(expectedDaiAmount.mul(parseEther('1'))); +// const ethUsdValue = amountIn +// .mul(wethPrice) +// .div(parseEther('1')) +// .mul(usdPrice) +// .div(parseEther('1')); +// const daiUsdValue = expectedDaiAmount +// .mul(daiPrice) +// .div(parseEther('1')) +// .mul(usdPrice) +// .div(parseEther('1')); + +// await mockUniswapRouter.setAmountOut( +// amountToSwap, +// weth.address, +// dai.address, +// expectedDaiAmount +// ); + +// const result = await uniswapLiquiditySwapAdapter.getAmountsOut( +// amountIn, +// weth.address, +// dai.address +// ); + +// expect(result['0']).to.be.eq(expectedDaiAmount); +// expect(result['1']).to.be.eq(outPerInPrice); +// expect(result['2']).to.be.eq(ethUsdValue); +// expect(result['3']).to.be.eq(daiUsdValue); +// }); +// it('should work correctly with different decimals', async () => { +// const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; + +// const amountIn = parseEther('10'); +// const flashloanPremium = amountIn.mul(9).div(10000); +// const amountToSwap = amountIn.sub(flashloanPremium); + +// const aavePrice = await oracle.getAssetPrice(aave.address); +// const usdcPrice = await oracle.getAssetPrice(usdc.address); +// const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + +// const expectedUSDCAmount = await convertToCurrencyDecimals( +// usdc.address, +// new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) +// ); + +// const outPerInPrice = amountToSwap +// .mul(parseEther('1')) +// .mul('1000000') // usdc 6 decimals +// .div(expectedUSDCAmount.mul(parseEther('1'))); + +// const aaveUsdValue = amountIn +// .mul(aavePrice) +// .div(parseEther('1')) +// .mul(usdPrice) +// .div(parseEther('1')); + +// const usdcUsdValue = expectedUSDCAmount +// .mul(usdcPrice) +// .div('1000000') // usdc 6 decimals +// .mul(usdPrice) +// .div(parseEther('1')); + +// await mockUniswapRouter.setAmountOut( +// amountToSwap, +// aave.address, +// usdc.address, +// expectedUSDCAmount +// ); + +// const result = await uniswapLiquiditySwapAdapter.getAmountsOut( +// amountIn, +// aave.address, +// usdc.address +// ); + +// expect(result['0']).to.be.eq(expectedUSDCAmount); +// expect(result['1']).to.be.eq(outPerInPrice); +// expect(result['2']).to.be.eq(aaveUsdValue); +// expect(result['3']).to.be.eq(usdcUsdValue); +// }); +// }); + +// describe('getAmountsIn', () => { +// it('should return the estimated required amountIn for the asset swap', async () => { +// const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; + +// const amountIn = parseEther('1'); +// const flashloanPremium = amountIn.mul(9).div(10000); +// const amountToSwap = amountIn.add(flashloanPremium); + +// const wethPrice = await oracle.getAssetPrice(weth.address); +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + +// const amountOut = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountIn.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// const inPerOutPrice = amountOut +// .mul(parseEther('1')) +// .mul(parseEther('1')) +// .div(amountToSwap.mul(parseEther('1'))); + +// const ethUsdValue = amountToSwap +// .mul(wethPrice) +// .div(parseEther('1')) +// .mul(usdPrice) +// .div(parseEther('1')); +// const daiUsdValue = amountOut +// .mul(daiPrice) +// .div(parseEther('1')) +// .mul(usdPrice) +// .div(parseEther('1')); + +// await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn); + +// const result = await uniswapLiquiditySwapAdapter.getAmountsIn( +// amountOut, +// weth.address, +// dai.address +// ); + +// expect(result['0']).to.be.eq(amountToSwap); +// expect(result['1']).to.be.eq(inPerOutPrice); +// expect(result['2']).to.be.eq(ethUsdValue); +// expect(result['3']).to.be.eq(daiUsdValue); +// }); +// it('should work correctly with different decimals', async () => { +// const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; + +// const amountIn = parseEther('10'); +// const flashloanPremium = amountIn.mul(9).div(10000); +// const amountToSwap = amountIn.add(flashloanPremium); + +// const aavePrice = await oracle.getAssetPrice(aave.address); +// const usdcPrice = await oracle.getAssetPrice(usdc.address); +// const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + +// const amountOut = await convertToCurrencyDecimals( +// usdc.address, +// new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) +// ); + +// const inPerOutPrice = amountOut +// .mul(parseEther('1')) +// .mul(parseEther('1')) +// .div(amountToSwap.mul('1000000')); // usdc 6 decimals + +// const aaveUsdValue = amountToSwap +// .mul(aavePrice) +// .div(parseEther('1')) +// .mul(usdPrice) +// .div(parseEther('1')); + +// const usdcUsdValue = amountOut +// .mul(usdcPrice) +// .div('1000000') // usdc 6 decimals +// .mul(usdPrice) +// .div(parseEther('1')); + +// await mockUniswapRouter.setAmountIn(amountOut, aave.address, usdc.address, amountIn); + +// const result = await uniswapLiquiditySwapAdapter.getAmountsIn( +// amountOut, +// aave.address, +// usdc.address +// ); + +// expect(result['0']).to.be.eq(amountToSwap); +// expect(result['1']).to.be.eq(inPerOutPrice); +// expect(result['2']).to.be.eq(aaveUsdValue); +// expect(result['3']).to.be.eq(usdcUsdValue); +// }); +// }); +// }); +// }); diff --git a/test-suites/test-amm/uniswapAdapters.flashLiquidation.spec.ts b/test-suites/test-amm/uniswapAdapters.flashLiquidation.spec.ts new file mode 100644 index 00000000..71b365bb --- /dev/null +++ b/test-suites/test-amm/uniswapAdapters.flashLiquidation.spec.ts @@ -0,0 +1,850 @@ +// import { makeSuite, TestEnv } from './helpers/make-suite'; +// import { +// convertToCurrencyDecimals, +// buildFlashLiquidationAdapterParams, +// } from '../../helpers/contracts-helpers'; +// import { getMockUniswapRouter } from '../../helpers/contracts-getters'; +// import { deployFlashLiquidationAdapter } from '../../helpers/contracts-deployments'; +// import { MockUniswapV2Router02 } from '../../types/MockUniswapV2Router02'; +// import BigNumber from 'bignumber.js'; +// import { DRE, evmRevert, evmSnapshot, increaseTime, waitForTx } from '../../helpers/misc-utils'; +// import { ethers } from 'ethers'; +// import { ProtocolErrors, RateMode } from '../../helpers/types'; +// import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, oneEther } from '../../helpers/constants'; +// import { getUserData } from './helpers/utils/helpers'; +// import { calcExpectedStableDebtTokenBalance } from './helpers/utils/calculations'; +// const { expect } = require('chai'); + +// makeSuite('Uniswap adapters', (testEnv: TestEnv) => { +// let mockUniswapRouter: MockUniswapV2Router02; +// let evmSnapshotId: string; +// const { INVALID_HF, LP_LIQUIDATION_CALL_FAILED } = ProtocolErrors; + +// before(async () => { +// mockUniswapRouter = await getMockUniswapRouter(); +// }); + +// const depositAndHFBelowOne = async () => { +// const { dai, weth, users, pool, oracle } = testEnv; +// const depositor = users[0]; +// const borrower = users[1]; + +// //mints DAI to depositor +// await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + +// //approve protocol to access depositor wallet +// await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + +// //user 1 deposits 1000 DAI +// const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + +// await pool +// .connect(depositor.signer) +// .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); +// //user 2 deposits 1 ETH +// const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + +// //mints WETH to borrower +// await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + +// //approve protocol to access the borrower wallet +// await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + +// await pool +// .connect(borrower.signer) +// .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + +// //user 2 borrows + +// const userGlobalDataBefore = await pool.getUserAccountData(borrower.address); +// const daiPrice = await oracle.getAssetPrice(dai.address); + +// const amountDAIToBorrow = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(userGlobalDataBefore.availableBorrowsETH.toString()) +// .div(daiPrice.toString()) +// .multipliedBy(0.95) +// .toFixed(0) +// ); + +// await pool +// .connect(borrower.signer) +// .borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address); + +// const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); + +// expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.equal( +// '8250', +// INVALID_HF +// ); + +// await oracle.setAssetPrice( +// dai.address, +// new BigNumber(daiPrice.toString()).multipliedBy(1.18).toFixed(0) +// ); + +// const userGlobalData = await pool.getUserAccountData(borrower.address); + +// expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt( +// oneEther.toFixed(0), +// INVALID_HF +// ); +// }; + +// const depositSameAssetAndHFBelowOne = async () => { +// const { dai, weth, users, pool, oracle } = testEnv; +// const depositor = users[0]; +// const borrower = users[1]; + +// //mints DAI to depositor +// await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + +// //approve protocol to access depositor wallet +// await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + +// //user 1 deposits 1000 DAI +// const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + +// await pool +// .connect(depositor.signer) +// .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); +// //user 2 deposits 1 ETH +// const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + +// //mints WETH to borrower +// await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + +// //approve protocol to access the borrower wallet +// await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + +// await pool +// .connect(borrower.signer) +// .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + +// //user 2 borrows + +// const userGlobalDataBefore = await pool.getUserAccountData(borrower.address); +// const daiPrice = await oracle.getAssetPrice(dai.address); + +// const amountDAIToBorrow = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(userGlobalDataBefore.availableBorrowsETH.toString()) +// .div(daiPrice.toString()) +// .multipliedBy(0.8) +// .toFixed(0) +// ); +// await waitForTx( +// await pool +// .connect(borrower.signer) +// .borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address) +// ); + +// const userGlobalDataBefore2 = await pool.getUserAccountData(borrower.address); + +// const amountWETHToBorrow = new BigNumber(userGlobalDataBefore2.availableBorrowsETH.toString()) +// .multipliedBy(0.8) +// .toFixed(0); + +// await pool +// .connect(borrower.signer) +// .borrow(weth.address, amountWETHToBorrow, RateMode.Variable, '0', borrower.address); + +// const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); + +// expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.equal( +// '8250', +// INVALID_HF +// ); + +// await oracle.setAssetPrice( +// dai.address, +// new BigNumber(daiPrice.toString()).multipliedBy(1.18).toFixed(0) +// ); + +// const userGlobalData = await pool.getUserAccountData(borrower.address); + +// expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt( +// oneEther.toFixed(0), +// INVALID_HF +// ); +// }; + +// beforeEach(async () => { +// evmSnapshotId = await evmSnapshot(); +// }); + +// afterEach(async () => { +// await evmRevert(evmSnapshotId); +// }); + +// describe('Flash Liquidation Adapter', () => { +// before('Before LendingPool liquidation: set config', () => { +// BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN }); +// }); + +// after('After LendingPool liquidation: reset config', () => { +// BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); +// }); + +// describe('constructor', () => { +// it('should deploy with correct parameters', async () => { +// const { addressesProvider, weth } = testEnv; +// await deployFlashLiquidationAdapter([ +// addressesProvider.address, +// mockUniswapRouter.address, +// weth.address, +// ]); +// }); + +// it('should revert if not valid addresses provider', async () => { +// const { weth } = testEnv; +// expect( +// deployFlashLiquidationAdapter([ +// mockUniswapRouter.address, +// mockUniswapRouter.address, +// weth.address, +// ]) +// ).to.be.reverted; +// }); +// }); + +// describe('executeOperation: succesfully liquidateCall and swap via Flash Loan with profits', () => { +// it('Liquidates the borrow with profit', async () => { +// await depositAndHFBelowOne(); +// await increaseTime(100); + +// const { +// dai, +// weth, +// users, +// pool, +// oracle, +// helpersContract, +// flashLiquidationAdapter, +// } = testEnv; + +// const liquidator = users[3]; +// const borrower = users[1]; +// const expectedSwap = ethers.utils.parseEther('0.4'); + +// const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address); + +// // Set how much ETH will be sold and swapped for DAI at Uniswap mock +// await (await mockUniswapRouter.setAmountToSwap(weth.address, expectedSwap)).wait(); + +// const collateralPrice = await oracle.getAssetPrice(weth.address); +// const principalPrice = await oracle.getAssetPrice(dai.address); +// const daiReserveDataBefore = await helpersContract.getReserveData(dai.address); +// const ethReserveDataBefore = await helpersContract.getReserveData(weth.address); +// const userReserveDataBefore = await getUserData( +// pool, +// helpersContract, +// dai.address, +// borrower.address +// ); + +// const collateralDecimals = ( +// await helpersContract.getReserveConfigurationData(weth.address) +// ).decimals.toString(); +// const principalDecimals = ( +// await helpersContract.getReserveConfigurationData(dai.address) +// ).decimals.toString(); +// const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0); + +// const expectedCollateralLiquidated = new BigNumber(principalPrice.toString()) +// .times(new BigNumber(amountToLiquidate).times(105)) +// .times(new BigNumber(10).pow(collateralDecimals)) +// .div( +// new BigNumber(collateralPrice.toString()).times( +// new BigNumber(10).pow(principalDecimals) +// ) +// ) +// .div(100) +// .decimalPlaces(0, BigNumber.ROUND_DOWN); + +// const flashLoanDebt = new BigNumber(amountToLiquidate.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// const expectedProfit = ethers.BigNumber.from(expectedCollateralLiquidated.toString()).sub( +// expectedSwap +// ); + +// const params = buildFlashLiquidationAdapterParams( +// weth.address, +// dai.address, +// borrower.address, +// amountToLiquidate, +// false +// ); +// const tx = await pool +// .connect(liquidator.signer) +// .flashLoan( +// flashLiquidationAdapter.address, +// [dai.address], +// [amountToLiquidate], +// [0], +// borrower.address, +// params, +// 0 +// ); + +// // Expect Swapped event +// await expect(Promise.resolve(tx)).to.emit(flashLiquidationAdapter, 'Swapped'); + +// // Expect LiquidationCall event +// await expect(Promise.resolve(tx)).to.emit(pool, 'LiquidationCall'); + +// const userReserveDataAfter = await getUserData( +// pool, +// helpersContract, +// dai.address, +// borrower.address +// ); +// const liquidatorWethBalanceAfter = await weth.balanceOf(liquidator.address); + +// const daiReserveDataAfter = await helpersContract.getReserveData(dai.address); +// const ethReserveDataAfter = await helpersContract.getReserveData(weth.address); + +// if (!tx.blockNumber) { +// expect(false, 'Invalid block number'); +// return; +// } +// const txTimestamp = new BigNumber( +// (await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp +// ); + +// const stableDebtBeforeTx = calcExpectedStableDebtTokenBalance( +// userReserveDataBefore.principalStableDebt, +// userReserveDataBefore.stableBorrowRate, +// userReserveDataBefore.stableRateLastUpdated, +// txTimestamp +// ); + +// const collateralAssetContractBalance = await weth.balanceOf( +// flashLiquidationAdapter.address +// ); +// const borrowAssetContractBalance = await dai.balanceOf(flashLiquidationAdapter.address); + +// expect(collateralAssetContractBalance).to.be.equal( +// '0', +// 'Contract address should not keep any balance.' +// ); +// expect(borrowAssetContractBalance).to.be.equal( +// '0', +// 'Contract address should not keep any balance.' +// ); + +// expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual( +// stableDebtBeforeTx.minus(amountToLiquidate).toFixed(0), +// 'Invalid user debt after liquidation' +// ); + +// //the liquidity index of the principal reserve needs to be bigger than the index before +// expect(daiReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte( +// daiReserveDataBefore.liquidityIndex.toString(), +// 'Invalid liquidity index' +// ); + +// //the principal APY after a liquidation needs to be lower than the APY before +// expect(daiReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt( +// daiReserveDataBefore.liquidityRate.toString(), +// 'Invalid liquidity APY' +// ); + +// expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( +// new BigNumber(daiReserveDataBefore.availableLiquidity.toString()) +// .plus(flashLoanDebt) +// .toFixed(0), +// 'Invalid principal available liquidity' +// ); + +// expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( +// new BigNumber(ethReserveDataBefore.availableLiquidity.toString()) +// .minus(expectedCollateralLiquidated) +// .toFixed(0), +// 'Invalid collateral available liquidity' +// ); + +// // Profit after flash loan liquidation +// expect(liquidatorWethBalanceAfter).to.be.equal( +// liquidatorWethBalanceBefore.add(expectedProfit), +// 'Invalid expected WETH profit' +// ); +// }); +// }); + +// describe('executeOperation: succesfully liquidateCall with same asset via Flash Loan, but no swap needed', () => { +// it('Liquidates the borrow with profit', async () => { +// await depositSameAssetAndHFBelowOne(); +// await increaseTime(100); + +// const { weth, users, pool, oracle, helpersContract, flashLiquidationAdapter } = testEnv; + +// const liquidator = users[3]; +// const borrower = users[1]; + +// const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address); + +// const assetPrice = await oracle.getAssetPrice(weth.address); +// const ethReserveDataBefore = await helpersContract.getReserveData(weth.address); +// const userReserveDataBefore = await getUserData( +// pool, +// helpersContract, +// weth.address, +// borrower.address +// ); + +// const assetDecimals = ( +// await helpersContract.getReserveConfigurationData(weth.address) +// ).decimals.toString(); +// const amountToLiquidate = userReserveDataBefore.currentVariableDebt.div(2).toFixed(0); + +// const expectedCollateralLiquidated = new BigNumber(assetPrice.toString()) +// .times(new BigNumber(amountToLiquidate).times(105)) +// .times(new BigNumber(10).pow(assetDecimals)) +// .div(new BigNumber(assetPrice.toString()).times(new BigNumber(10).pow(assetDecimals))) +// .div(100) +// .decimalPlaces(0, BigNumber.ROUND_DOWN); + +// const flashLoanDebt = new BigNumber(amountToLiquidate.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// const params = buildFlashLiquidationAdapterParams( +// weth.address, +// weth.address, +// borrower.address, +// amountToLiquidate, +// false +// ); +// const tx = await pool +// .connect(liquidator.signer) +// .flashLoan( +// flashLiquidationAdapter.address, +// [weth.address], +// [amountToLiquidate], +// [0], +// borrower.address, +// params, +// 0 +// ); + +// // Dont expect Swapped event due is same asset +// await expect(Promise.resolve(tx)).to.not.emit(flashLiquidationAdapter, 'Swapped'); + +// // Expect LiquidationCall event +// await expect(Promise.resolve(tx)) +// .to.emit(pool, 'LiquidationCall') +// .withArgs( +// weth.address, +// weth.address, +// borrower.address, +// amountToLiquidate.toString(), +// expectedCollateralLiquidated.toString(), +// flashLiquidationAdapter.address, +// false +// ); + +// const borrowAssetContractBalance = await weth.balanceOf(flashLiquidationAdapter.address); + +// expect(borrowAssetContractBalance).to.be.equal( +// '0', +// 'Contract address should not keep any balance.' +// ); +// }); +// }); + +// describe('executeOperation: succesfully liquidateCall and swap via Flash Loan without profits', () => { +// it('Liquidates the borrow', async () => { +// await depositAndHFBelowOne(); +// await increaseTime(100); + +// const { +// dai, +// weth, +// users, +// pool, +// oracle, +// helpersContract, +// flashLiquidationAdapter, +// } = testEnv; + +// const liquidator = users[3]; +// const borrower = users[1]; +// const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address); + +// const collateralPrice = await oracle.getAssetPrice(weth.address); +// const principalPrice = await oracle.getAssetPrice(dai.address); +// const daiReserveDataBefore = await helpersContract.getReserveData(dai.address); +// const ethReserveDataBefore = await helpersContract.getReserveData(weth.address); +// const userReserveDataBefore = await getUserData( +// pool, +// helpersContract, +// dai.address, +// borrower.address +// ); + +// const collateralDecimals = ( +// await helpersContract.getReserveConfigurationData(weth.address) +// ).decimals.toString(); +// const principalDecimals = ( +// await helpersContract.getReserveConfigurationData(dai.address) +// ).decimals.toString(); +// const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0); + +// const expectedCollateralLiquidated = new BigNumber(principalPrice.toString()) +// .times(new BigNumber(amountToLiquidate).times(105)) +// .times(new BigNumber(10).pow(collateralDecimals)) +// .div( +// new BigNumber(collateralPrice.toString()).times( +// new BigNumber(10).pow(principalDecimals) +// ) +// ) +// .div(100) +// .decimalPlaces(0, BigNumber.ROUND_DOWN); + +// const flashLoanDebt = new BigNumber(amountToLiquidate.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// // Set how much ETH will be sold and swapped for DAI at Uniswap mock +// await ( +// await mockUniswapRouter.setAmountToSwap( +// weth.address, +// expectedCollateralLiquidated.toString() +// ) +// ).wait(); + +// const params = buildFlashLiquidationAdapterParams( +// weth.address, +// dai.address, +// borrower.address, +// amountToLiquidate, +// false +// ); +// const tx = await pool +// .connect(liquidator.signer) +// .flashLoan( +// flashLiquidationAdapter.address, +// [dai.address], +// [flashLoanDebt], +// [0], +// borrower.address, +// params, +// 0 +// ); + +// // Expect Swapped event +// await expect(Promise.resolve(tx)).to.emit(flashLiquidationAdapter, 'Swapped'); + +// // Expect LiquidationCall event +// await expect(Promise.resolve(tx)).to.emit(pool, 'LiquidationCall'); + +// const userReserveDataAfter = await getUserData( +// pool, +// helpersContract, +// dai.address, +// borrower.address +// ); +// const liquidatorWethBalanceAfter = await weth.balanceOf(liquidator.address); + +// const daiReserveDataAfter = await helpersContract.getReserveData(dai.address); +// const ethReserveDataAfter = await helpersContract.getReserveData(weth.address); + +// if (!tx.blockNumber) { +// expect(false, 'Invalid block number'); +// return; +// } +// const txTimestamp = new BigNumber( +// (await DRE.ethers.provider.getBlock(tx.blockNumber)).timestamp +// ); + +// const stableDebtBeforeTx = calcExpectedStableDebtTokenBalance( +// userReserveDataBefore.principalStableDebt, +// userReserveDataBefore.stableBorrowRate, +// userReserveDataBefore.stableRateLastUpdated, +// txTimestamp +// ); + +// const collateralAssetContractBalance = await dai.balanceOf(flashLiquidationAdapter.address); +// const borrowAssetContractBalance = await weth.balanceOf(flashLiquidationAdapter.address); + +// expect(collateralAssetContractBalance).to.be.equal( +// '0', +// 'Contract address should not keep any balance.' +// ); +// expect(borrowAssetContractBalance).to.be.equal( +// '0', +// 'Contract address should not keep any balance.' +// ); +// expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual( +// stableDebtBeforeTx.minus(amountToLiquidate).toFixed(0), +// 'Invalid user debt after liquidation' +// ); + +// //the liquidity index of the principal reserve needs to be bigger than the index before +// expect(daiReserveDataAfter.liquidityIndex.toString()).to.be.bignumber.gte( +// daiReserveDataBefore.liquidityIndex.toString(), +// 'Invalid liquidity index' +// ); + +// //the principal APY after a liquidation needs to be lower than the APY before +// expect(daiReserveDataAfter.liquidityRate.toString()).to.be.bignumber.lt( +// daiReserveDataBefore.liquidityRate.toString(), +// 'Invalid liquidity APY' +// ); + +// expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( +// new BigNumber(ethReserveDataBefore.availableLiquidity.toString()) +// .minus(expectedCollateralLiquidated) +// .toFixed(0), +// 'Invalid collateral available liquidity' +// ); + +// // Net Profit == 0 after flash loan liquidation +// expect(liquidatorWethBalanceAfter).to.be.equal( +// liquidatorWethBalanceBefore, +// 'Invalid expected WETH profit' +// ); +// }); +// }); + +// describe('executeOperation: succesfully liquidateCall all available debt and swap via Flash Loan ', () => { +// it('Liquidates the borrow', async () => { +// await depositAndHFBelowOne(); +// await increaseTime(100); + +// const { +// dai, +// weth, +// users, +// pool, +// oracle, +// helpersContract, +// flashLiquidationAdapter, +// } = testEnv; + +// const liquidator = users[3]; +// const borrower = users[1]; +// const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address); + +// const collateralPrice = await oracle.getAssetPrice(weth.address); +// const principalPrice = await oracle.getAssetPrice(dai.address); +// const daiReserveDataBefore = await helpersContract.getReserveData(dai.address); +// const ethReserveDataBefore = await helpersContract.getReserveData(weth.address); +// const userReserveDataBefore = await getUserData( +// pool, +// helpersContract, +// dai.address, +// borrower.address +// ); + +// const collateralDecimals = ( +// await helpersContract.getReserveConfigurationData(weth.address) +// ).decimals.toString(); +// const principalDecimals = ( +// await helpersContract.getReserveConfigurationData(dai.address) +// ).decimals.toString(); +// const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0); +// const extraAmount = new BigNumber(amountToLiquidate).times('1.15').toFixed(0); + +// const expectedCollateralLiquidated = new BigNumber(principalPrice.toString()) +// .times(new BigNumber(amountToLiquidate).times(105)) +// .times(new BigNumber(10).pow(collateralDecimals)) +// .div( +// new BigNumber(collateralPrice.toString()).times( +// new BigNumber(10).pow(principalDecimals) +// ) +// ) +// .div(100) +// .decimalPlaces(0, BigNumber.ROUND_DOWN); + +// const flashLoanDebt = new BigNumber(amountToLiquidate.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// // Set how much ETH will be sold and swapped for DAI at Uniswap mock +// await ( +// await mockUniswapRouter.setAmountToSwap( +// weth.address, +// expectedCollateralLiquidated.toString() +// ) +// ).wait(); + +// const params = buildFlashLiquidationAdapterParams( +// weth.address, +// dai.address, +// borrower.address, +// MAX_UINT_AMOUNT, +// false +// ); +// const tx = await pool +// .connect(liquidator.signer) +// .flashLoan( +// flashLiquidationAdapter.address, +// [dai.address], +// [extraAmount], +// [0], +// borrower.address, +// params, +// 0 +// ); + +// // Expect Swapped event +// await expect(Promise.resolve(tx)).to.emit(flashLiquidationAdapter, 'Swapped'); + +// // Expect LiquidationCall event +// await expect(Promise.resolve(tx)).to.emit(pool, 'LiquidationCall'); + +// const collateralAssetContractBalance = await dai.balanceOf(flashLiquidationAdapter.address); +// const borrowAssetContractBalance = await dai.balanceOf(flashLiquidationAdapter.address); + +// expect(collateralAssetContractBalance).to.be.equal( +// '0', +// 'Contract address should not keep any balance.' +// ); +// expect(borrowAssetContractBalance).to.be.equal( +// '0', +// 'Contract address should not keep any balance.' +// ); +// }); +// }); + +// describe('executeOperation: invalid params', async () => { +// it('Revert if debt asset is different than requested flash loan token', async () => { +// await depositAndHFBelowOne(); + +// const { dai, weth, users, pool, helpersContract, flashLiquidationAdapter } = testEnv; + +// const liquidator = users[3]; +// const borrower = users[1]; +// const expectedSwap = ethers.utils.parseEther('0.4'); + +// // Set how much ETH will be sold and swapped for DAI at Uniswap mock +// await (await mockUniswapRouter.setAmountToSwap(weth.address, expectedSwap)).wait(); + +// const userReserveDataBefore = await getUserData( +// pool, +// helpersContract, +// dai.address, +// borrower.address +// ); + +// const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0); + +// // Wrong debt asset +// const params = buildFlashLiquidationAdapterParams( +// weth.address, +// weth.address, // intentionally bad +// borrower.address, +// amountToLiquidate, +// false +// ); +// await expect( +// pool +// .connect(liquidator.signer) +// .flashLoan( +// flashLiquidationAdapter.address, +// [dai.address], +// [amountToLiquidate], +// [0], +// borrower.address, +// params, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); +// }); + +// it('Revert if debt asset amount to liquidate is greater than requested flash loan', async () => { +// await depositAndHFBelowOne(); + +// const { dai, weth, users, pool, helpersContract, flashLiquidationAdapter } = testEnv; + +// const liquidator = users[3]; +// const borrower = users[1]; +// const expectedSwap = ethers.utils.parseEther('0.4'); + +// // Set how much ETH will be sold and swapped for DAI at Uniswap mock +// await (await mockUniswapRouter.setAmountToSwap(weth.address, expectedSwap)).wait(); + +// const userReserveDataBefore = await getUserData( +// pool, +// helpersContract, +// dai.address, +// borrower.address +// ); + +// const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2); + +// // Correct params +// const params = buildFlashLiquidationAdapterParams( +// weth.address, +// dai.address, +// borrower.address, +// amountToLiquidate.toString(), +// false +// ); +// // Bad flash loan params: requested DAI amount below amountToLiquidate +// await expect( +// pool +// .connect(liquidator.signer) +// .flashLoan( +// flashLiquidationAdapter.address, +// [dai.address], +// [amountToLiquidate.div(2).toString()], +// [0], +// borrower.address, +// params, +// 0 +// ) +// ).to.be.revertedWith(LP_LIQUIDATION_CALL_FAILED); +// }); + +// it('Revert if requested multiple assets', async () => { +// await depositAndHFBelowOne(); + +// const { dai, weth, users, pool, helpersContract, flashLiquidationAdapter } = testEnv; + +// const liquidator = users[3]; +// const borrower = users[1]; +// const expectedSwap = ethers.utils.parseEther('0.4'); + +// // Set how much ETH will be sold and swapped for DAI at Uniswap mock +// await (await mockUniswapRouter.setAmountToSwap(weth.address, expectedSwap)).wait(); + +// const userReserveDataBefore = await getUserData( +// pool, +// helpersContract, +// dai.address, +// borrower.address +// ); + +// const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2); + +// // Correct params +// const params = buildFlashLiquidationAdapterParams( +// weth.address, +// dai.address, +// borrower.address, +// amountToLiquidate.toString(), +// false +// ); +// // Bad flash loan params: requested multiple assets +// await expect( +// pool +// .connect(liquidator.signer) +// .flashLoan( +// flashLiquidationAdapter.address, +// [dai.address, weth.address], +// [10, 10], +// [0], +// borrower.address, +// params, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); +// }); +// }); +// }); +// }); diff --git a/test-suites/test-amm/uniswapAdapters.liquiditySwap.spec.ts b/test-suites/test-amm/uniswapAdapters.liquiditySwap.spec.ts new file mode 100644 index 00000000..30631f3e --- /dev/null +++ b/test-suites/test-amm/uniswapAdapters.liquiditySwap.spec.ts @@ -0,0 +1,1854 @@ +// import { makeSuite, TestEnv } from './helpers/make-suite'; +// import { +// convertToCurrencyDecimals, +// getContract, +// buildPermitParams, +// getSignatureFromTypedData, +// buildLiquiditySwapParams, +// } from '../../helpers/contracts-helpers'; +// import { getMockUniswapRouter } from '../../helpers/contracts-getters'; +// import { deployUniswapLiquiditySwapAdapter } from '../../helpers/contracts-deployments'; +// import { MockUniswapV2Router02 } from '../../types/MockUniswapV2Router02'; +// import { Zero } from '@ethersproject/constants'; +// import BigNumber from 'bignumber.js'; +// import { DRE, evmRevert, evmSnapshot } from '../../helpers/misc-utils'; +// import { ethers } from 'ethers'; +// import { eContractid } from '../../helpers/types'; +// import { AToken } from '../../types/AToken'; +// import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +// import { MAX_UINT_AMOUNT } from '../../helpers/constants'; +// const { parseEther } = ethers.utils; + +// const { expect } = require('chai'); + +// makeSuite('Uniswap adapters', (testEnv: TestEnv) => { +// let mockUniswapRouter: MockUniswapV2Router02; +// let evmSnapshotId: string; + +// before(async () => { +// mockUniswapRouter = await getMockUniswapRouter(); +// }); + +// beforeEach(async () => { +// evmSnapshotId = await evmSnapshot(); +// }); + +// afterEach(async () => { +// await evmRevert(evmSnapshotId); +// }); + +// describe('UniswapLiquiditySwapAdapter', () => { +// describe('constructor', () => { +// it('should deploy with correct parameters', async () => { +// const { addressesProvider, weth } = testEnv; +// await deployUniswapLiquiditySwapAdapter([ +// addressesProvider.address, +// mockUniswapRouter.address, +// weth.address, +// ]); +// }); + +// it('should revert if not valid addresses provider', async () => { +// const { weth } = testEnv; +// expect( +// deployUniswapLiquiditySwapAdapter([ +// mockUniswapRouter.address, +// mockUniswapRouter.address, +// weth.address, +// ]) +// ).to.be.reverted; +// }); +// }); + +// describe('executeOperation', () => { +// beforeEach(async () => { +// const { users, weth, dai, usdc, pool, deployer } = testEnv; +// const userAddress = users[0].address; + +// // Provide liquidity +// await dai.mint(parseEther('20000')); +// await dai.approve(pool.address, parseEther('20000')); +// await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + +// const usdcAmount = await convertToCurrencyDecimals(usdc.address, '10'); +// await usdc.mint(usdcAmount); +// await usdc.approve(pool.address, usdcAmount); +// await pool.deposit(usdc.address, usdcAmount, deployer.address, 0); + +// // Make a deposit for user +// await weth.mint(parseEther('100')); +// await weth.approve(pool.address, parseEther('100')); +// await pool.deposit(weth.address, parseEther('100'), userAddress, 0); +// }); + +// it('should correctly swap tokens and deposit the out tokens in the pool', async () => { +// const { +// users, +// weth, +// oracle, +// dai, +// aDai, +// aWETH, +// pool, +// uniswapLiquiditySwapAdapter, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // Subtract the FL fee from the amount to be swapped 0,09% +// const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + +// const params = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should correctly swap and deposit multiple tokens', async () => { +// const { +// users, +// weth, +// oracle, +// dai, +// aDai, +// aWETH, +// usdc, +// pool, +// uniswapLiquiditySwapAdapter, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmountForEth = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); +// const usdcPrice = await oracle.getAssetPrice(usdc.address); + +// const collateralDecimals = (await usdc.decimals()).toString(); +// const principalDecimals = (await dai.decimals()).toString(); + +// const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountUSDCtoSwap.toString()) +// .times( +// new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) +// ) +// .div( +// new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) +// ) +// .toFixed(0) +// ); + +// // Make a deposit for user +// await usdc.connect(user).mint(amountUSDCtoSwap); +// await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); +// await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + +// const aUsdcData = await pool.getReserveData(usdc.address); +// const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmountForEth); +// await mockUniswapRouter.setAmountToReturn(usdc.address, expectedDaiAmountForUsdc); + +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); +// await aUsdc.connect(user).approve(uniswapLiquiditySwapAdapter.address, amountUSDCtoSwap); +// const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); + +// // Subtract the FL fee from the amount to be swapped 0,09% +// const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) +// .div(1.0009) +// .toFixed(0); +// const usdcFlashloanAmount = new BigNumber(amountUSDCtoSwap.toString()) +// .div(1.0009) +// .toFixed(0); + +// const params = buildLiquiditySwapParams( +// [dai.address, dai.address], +// [expectedDaiAmountForEth, expectedDaiAmountForUsdc], +// [0, 0], +// [0, 0], +// [0, 0], +// [0, 0], +// [ +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// ], +// [ +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// ], +// [false, false] +// ); + +// await pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address, usdc.address], +// [wethFlashloanAmount.toString(), usdcFlashloanAmount.toString()], +// [0, 0], +// userAddress, +// params, +// 0 +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const userAUsdcBalance = await aUsdc.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmountForEth.add(expectedDaiAmountForUsdc)); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); +// expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); +// expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); +// }); + +// it('should correctly swap and deposit multiple tokens using permit', async () => { +// const { +// users, +// weth, +// oracle, +// dai, +// aDai, +// aWETH, +// usdc, +// pool, +// uniswapLiquiditySwapAdapter, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; +// const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; +// const deadline = MAX_UINT_AMOUNT; + +// const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; +// if (!ownerPrivateKey) { +// throw new Error('INVALID_OWNER_PK'); +// } + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmountForEth = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); +// const usdcPrice = await oracle.getAssetPrice(usdc.address); + +// const collateralDecimals = (await usdc.decimals()).toString(); +// const principalDecimals = (await dai.decimals()).toString(); + +// const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountUSDCtoSwap.toString()) +// .times( +// new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) +// ) +// .div( +// new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) +// ) +// .toFixed(0) +// ); + +// // Make a deposit for user +// await usdc.connect(user).mint(amountUSDCtoSwap); +// await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); +// await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + +// const aUsdcData = await pool.getReserveData(usdc.address); +// const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmountForEth); +// await mockUniswapRouter.setAmountToReturn(usdc.address, expectedDaiAmountForUsdc); + +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); +// const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); + +// const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) +// .div(1.0009) +// .toFixed(0); + +// const usdcFlashloanAmount = new BigNumber(amountUSDCtoSwap.toString()) +// .div(1.0009) +// .toFixed(0); + +// const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); +// const aWethMsgParams = buildPermitParams( +// chainId, +// aWETH.address, +// '1', +// await aWETH.name(), +// userAddress, +// uniswapLiquiditySwapAdapter.address, +// aWethNonce, +// deadline, +// amountWETHtoSwap.toString() +// ); +// const { v: aWETHv, r: aWETHr, s: aWETHs } = getSignatureFromTypedData( +// ownerPrivateKey, +// aWethMsgParams +// ); + +// const aUsdcNonce = (await aUsdc._nonces(userAddress)).toNumber(); +// const aUsdcMsgParams = buildPermitParams( +// chainId, +// aUsdc.address, +// '1', +// await aUsdc.name(), +// userAddress, +// uniswapLiquiditySwapAdapter.address, +// aUsdcNonce, +// deadline, +// amountUSDCtoSwap.toString() +// ); +// const { v: aUsdcv, r: aUsdcr, s: aUsdcs } = getSignatureFromTypedData( +// ownerPrivateKey, +// aUsdcMsgParams +// ); +// const params = buildLiquiditySwapParams( +// [dai.address, dai.address], +// [expectedDaiAmountForEth, expectedDaiAmountForUsdc], +// [0, 0], +// [amountWETHtoSwap, amountUSDCtoSwap], +// [deadline, deadline], +// [aWETHv, aUsdcv], +// [aWETHr, aUsdcr], +// [aWETHs, aUsdcs], +// [false, false] +// ); + +// await pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address, usdc.address], +// [wethFlashloanAmount.toString(), usdcFlashloanAmount.toString()], +// [0, 0], +// userAddress, +// params, +// 0 +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const userAUsdcBalance = await aUsdc.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmountForEth.add(expectedDaiAmountForUsdc)); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); +// expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); +// expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); +// }); + +// it('should correctly swap tokens with permit', async () => { +// const { +// users, +// weth, +// oracle, +// dai, +// aDai, +// aWETH, +// pool, +// uniswapLiquiditySwapAdapter, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // Subtract the FL fee from the amount to be swapped 0,09% +// const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + +// const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; +// const deadline = MAX_UINT_AMOUNT; +// const nonce = (await aWETH._nonces(userAddress)).toNumber(); +// const msgParams = buildPermitParams( +// chainId, +// aWETH.address, +// '1', +// await aWETH.name(), +// userAddress, +// uniswapLiquiditySwapAdapter.address, +// nonce, +// deadline, +// liquidityToSwap.toString() +// ); + +// const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; +// if (!ownerPrivateKey) { +// throw new Error('INVALID_OWNER_PK'); +// } + +// const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + +// const params = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [0], +// [liquidityToSwap], +// [deadline], +// [v], +// [r], +// [s], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should revert if inconsistent params', async () => { +// const { users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + +// // Subtract the FL fee from the amount to be swapped 0,09% +// const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + +// const params = buildLiquiditySwapParams( +// [dai.address, weth.address], +// [expectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// const params2 = buildLiquiditySwapParams( +// [dai.address, weth.address], +// [expectedDaiAmount], +// [0, 0], +// [0, 0], +// [0, 0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params2, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// const params3 = buildLiquiditySwapParams( +// [dai.address, weth.address], +// [expectedDaiAmount], +// [0, 0], +// [0], +// [0, 0], +// [0, 0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params3, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// const params4 = buildLiquiditySwapParams( +// [dai.address, weth.address], +// [expectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// [ +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// ], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params4, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// const params5 = buildLiquiditySwapParams( +// [dai.address, weth.address], +// [expectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [ +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// ], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params5, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// const params6 = buildLiquiditySwapParams( +// [dai.address, weth.address], +// [expectedDaiAmount, expectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params6, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// const params7 = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [0, 0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params7, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// const params8 = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [0], +// [0, 0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params8, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// const params9 = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false, false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params9, +// 0 +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); +// }); + +// it('should revert if caller not lending pool', async () => { +// const { users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + +// // Subtract the FL fee from the amount to be swapped 0,09% +// const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + +// const params = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// uniswapLiquiditySwapAdapter +// .connect(user) +// .executeOperation( +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params +// ) +// ).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL'); +// }); + +// it('should work correctly with tokens of different decimals', async () => { +// const { +// users, +// usdc, +// oracle, +// dai, +// aDai, +// uniswapLiquiditySwapAdapter, +// pool, +// deployer, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); +// const liquidity = await convertToCurrencyDecimals(usdc.address, '20000'); + +// // Provide liquidity +// await usdc.mint(liquidity); +// await usdc.approve(pool.address, liquidity); +// await pool.deposit(usdc.address, liquidity, deployer.address, 0); + +// // Make a deposit for user +// await usdc.connect(user).mint(amountUSDCtoSwap); +// await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); +// await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + +// const usdcPrice = await oracle.getAssetPrice(usdc.address); +// const daiPrice = await oracle.getAssetPrice(dai.address); + +// // usdc 6 +// const collateralDecimals = (await usdc.decimals()).toString(); +// const principalDecimals = (await dai.decimals()).toString(); + +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountUSDCtoSwap.toString()) +// .times( +// new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) +// ) +// .div( +// new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) +// ) +// .toFixed(0) +// ); + +// await mockUniswapRouter.connect(user).setAmountToReturn(usdc.address, expectedDaiAmount); + +// const aUsdcData = await pool.getReserveData(usdc.address); +// const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); +// const aUsdcBalance = await aUsdc.balanceOf(userAddress); +// await aUsdc.connect(user).approve(uniswapLiquiditySwapAdapter.address, aUsdcBalance); +// // Subtract the FL fee from the amount to be swapped 0,09% +// const flashloanAmount = new BigNumber(amountUSDCtoSwap.toString()).div(1.0009).toFixed(0); + +// const params = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [usdc.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(usdc.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + +// const adapterUsdcBalance = await usdc.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const aDaiBalance = await aDai.balanceOf(userAddress); + +// expect(adapterUsdcBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(aDaiBalance).to.be.eq(expectedDaiAmount); +// }); + +// it('should revert when min amount to receive exceeds the max slippage amount', async () => { +// const { users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); +// const smallExpectedDaiAmount = expectedDaiAmount.div(2); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + +// // Subtract the FL fee from the amount to be swapped 0,09% +// const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + +// const params = buildLiquiditySwapParams( +// [dai.address], +// [smallExpectedDaiAmount], +// [0], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [flashloanAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ).to.be.revertedWith('minAmountOut exceed max slippage'); +// }); + +// it('should correctly swap tokens all the balance', async () => { +// const { +// users, +// weth, +// oracle, +// dai, +// aDai, +// aWETH, +// pool, +// uniswapLiquiditySwapAdapter, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // Remove other balance +// await aWETH.connect(user).transfer(users[1].address, parseEther('90')); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + +// const params = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [1], +// [0], +// [0], +// [0], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// ['0x0000000000000000000000000000000000000000000000000000000000000000'], +// [false] +// ); + +// // Flashloan + premium > aToken balance. Then it will only swap the balance - premium +// const flashloanFee = liquidityToSwap.mul(9).div(10000); +// const swappedAmount = liquidityToSwap.sub(flashloanFee); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [liquidityToSwap.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, swappedAmount.toString(), expectedDaiAmount); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapLiquiditySwapAdapter.address); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmount); +// expect(userAEthBalance).to.be.eq(Zero); +// expect(adapterAEthBalance).to.be.eq(Zero); +// }); + +// it('should correctly swap tokens all the balance using permit', async () => { +// const { +// users, +// weth, +// oracle, +// dai, +// aDai, +// aWETH, +// pool, +// uniswapLiquiditySwapAdapter, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // Remove other balance +// await aWETH.connect(user).transfer(users[1].address, parseEther('90')); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// const liquidityToSwap = parseEther('10'); +// expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); + +// const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; +// const deadline = MAX_UINT_AMOUNT; +// const nonce = (await aWETH._nonces(userAddress)).toNumber(); +// const msgParams = buildPermitParams( +// chainId, +// aWETH.address, +// '1', +// await aWETH.name(), +// userAddress, +// uniswapLiquiditySwapAdapter.address, +// nonce, +// deadline, +// liquidityToSwap.toString() +// ); + +// const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; +// if (!ownerPrivateKey) { +// throw new Error('INVALID_OWNER_PK'); +// } + +// const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + +// const params = buildLiquiditySwapParams( +// [dai.address], +// [expectedDaiAmount], +// [1], +// [liquidityToSwap], +// [deadline], +// [v], +// [r], +// [s], +// [false] +// ); + +// // Flashloan + premium > aToken balance. Then it will only swap the balance - premium +// const flashloanFee = liquidityToSwap.mul(9).div(10000); +// const swappedAmount = liquidityToSwap.sub(flashloanFee); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapLiquiditySwapAdapter.address, +// [weth.address], +// [liquidityToSwap.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, swappedAmount.toString(), expectedDaiAmount); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapLiquiditySwapAdapter.address); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmount); +// expect(userAEthBalance).to.be.eq(Zero); +// expect(adapterAEthBalance).to.be.eq(Zero); +// }); +// }); + +// describe('swapAndDeposit', () => { +// beforeEach(async () => { +// const { users, weth, dai, pool, deployer } = testEnv; +// const userAddress = users[0].address; + +// // Provide liquidity +// await dai.mint(parseEther('20000')); +// await dai.approve(pool.address, parseEther('20000')); +// await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + +// // Make a deposit for user +// await weth.mint(parseEther('100')); +// await weth.approve(pool.address, parseEther('100')); +// await pool.deposit(weth.address, parseEther('100'), userAddress, 0); +// }); + +// it('should correctly swap tokens and deposit the out tokens in the pool', async () => { +// const { users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address], +// [dai.address], +// [amountWETHtoSwap], +// [expectedDaiAmount], +// [ +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// ], +// [false] +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should correctly swap tokens using permit', async () => { +// const { users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; +// const deadline = MAX_UINT_AMOUNT; +// const nonce = (await aWETH._nonces(userAddress)).toNumber(); +// const msgParams = buildPermitParams( +// chainId, +// aWETH.address, +// '1', +// await aWETH.name(), +// userAddress, +// uniswapLiquiditySwapAdapter.address, +// nonce, +// deadline, +// liquidityToSwap.toString() +// ); + +// const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; +// if (!ownerPrivateKey) { +// throw new Error('INVALID_OWNER_PK'); +// } + +// const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address], +// [dai.address], +// [amountWETHtoSwap], +// [expectedDaiAmount], +// [ +// { +// amount: liquidityToSwap, +// deadline, +// v, +// r, +// s, +// }, +// ], +// [false] +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should revert if inconsistent params', async () => { +// const { users, weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; +// const user = users[0].signer; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address, dai.address], +// [dai.address], +// [amountWETHtoSwap], +// [expectedDaiAmount], +// [ +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// ], +// [false] +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address], +// [dai.address, weth.address], +// [amountWETHtoSwap], +// [expectedDaiAmount], +// [ +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// ], +// [false] +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address], +// [dai.address], +// [amountWETHtoSwap, amountWETHtoSwap], +// [expectedDaiAmount], +// [ +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// ], +// [false] +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// await expect( +// uniswapLiquiditySwapAdapter +// .connect(user) +// .swapAndDeposit( +// [weth.address], +// [dai.address], +// [amountWETHtoSwap], +// [expectedDaiAmount], +// [], +// [false] +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address], +// [dai.address], +// [amountWETHtoSwap], +// [expectedDaiAmount, expectedDaiAmount], +// [ +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// ], +// [false] +// ) +// ).to.be.revertedWith('INCONSISTENT_PARAMS'); +// }); + +// it('should revert when min amount to receive exceeds the max slippage amount', async () => { +// const { users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; +// const user = users[0].signer; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); +// const smallExpectedDaiAmount = expectedDaiAmount.div(2); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address], +// [dai.address], +// [amountWETHtoSwap], +// [smallExpectedDaiAmount], +// [ +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// ], +// [false] +// ) +// ).to.be.revertedWith('minAmountOut exceed max slippage'); +// }); + +// it('should correctly swap tokens and deposit multiple tokens', async () => { +// const { +// users, +// weth, +// usdc, +// oracle, +// dai, +// aDai, +// aWETH, +// uniswapLiquiditySwapAdapter, +// pool, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmountForEth = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); +// const usdcPrice = await oracle.getAssetPrice(usdc.address); + +// const collateralDecimals = (await usdc.decimals()).toString(); +// const principalDecimals = (await dai.decimals()).toString(); + +// const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountUSDCtoSwap.toString()) +// .times( +// new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) +// ) +// .div( +// new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) +// ) +// .toFixed(0) +// ); + +// // Make a deposit for user +// await usdc.connect(user).mint(amountUSDCtoSwap); +// await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); +// await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + +// const aUsdcData = await pool.getReserveData(usdc.address); +// const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmountForEth); +// await mockUniswapRouter.setAmountToReturn(usdc.address, expectedDaiAmountForUsdc); + +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); +// await aUsdc.connect(user).approve(uniswapLiquiditySwapAdapter.address, amountUSDCtoSwap); +// const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); + +// await uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address, usdc.address], +// [dai.address, dai.address], +// [amountWETHtoSwap, amountUSDCtoSwap], +// [expectedDaiAmountForEth, expectedDaiAmountForUsdc], +// [ +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// ], +// [false, false] +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const userAUsdcBalance = await aUsdc.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmountForEth.add(expectedDaiAmountForUsdc)); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); +// expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); +// expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); +// }); + +// it('should correctly swap tokens and deposit multiple tokens using permit', async () => { +// const { +// users, +// weth, +// usdc, +// oracle, +// dai, +// aDai, +// aWETH, +// uniswapLiquiditySwapAdapter, +// pool, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; +// const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; +// const deadline = MAX_UINT_AMOUNT; + +// const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; +// if (!ownerPrivateKey) { +// throw new Error('INVALID_OWNER_PK'); +// } + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmountForEth = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); +// const usdcPrice = await oracle.getAssetPrice(usdc.address); + +// const collateralDecimals = (await usdc.decimals()).toString(); +// const principalDecimals = (await dai.decimals()).toString(); + +// const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountUSDCtoSwap.toString()) +// .times( +// new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) +// ) +// .div( +// new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) +// ) +// .toFixed(0) +// ); + +// // Make a deposit for user +// await usdc.connect(user).mint(amountUSDCtoSwap); +// await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); +// await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + +// const aUsdcData = await pool.getReserveData(usdc.address); +// const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmountForEth); +// await mockUniswapRouter.setAmountToReturn(usdc.address, expectedDaiAmountForUsdc); + +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); +// const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); + +// const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); +// const aWethMsgParams = buildPermitParams( +// chainId, +// aWETH.address, +// '1', +// await aWETH.name(), +// userAddress, +// uniswapLiquiditySwapAdapter.address, +// aWethNonce, +// deadline, +// amountWETHtoSwap.toString() +// ); +// const { v: aWETHv, r: aWETHr, s: aWETHs } = getSignatureFromTypedData( +// ownerPrivateKey, +// aWethMsgParams +// ); + +// const aUsdcNonce = (await aUsdc._nonces(userAddress)).toNumber(); +// const aUsdcMsgParams = buildPermitParams( +// chainId, +// aUsdc.address, +// '1', +// await aUsdc.name(), +// userAddress, +// uniswapLiquiditySwapAdapter.address, +// aUsdcNonce, +// deadline, +// amountUSDCtoSwap.toString() +// ); +// const { v: aUsdcv, r: aUsdcr, s: aUsdcs } = getSignatureFromTypedData( +// ownerPrivateKey, +// aUsdcMsgParams +// ); + +// await uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address, usdc.address], +// [dai.address, dai.address], +// [amountWETHtoSwap, amountUSDCtoSwap], +// [expectedDaiAmountForEth, expectedDaiAmountForUsdc], +// [ +// { +// amount: amountWETHtoSwap, +// deadline, +// v: aWETHv, +// r: aWETHr, +// s: aWETHs, +// }, +// { +// amount: amountUSDCtoSwap, +// deadline, +// v: aUsdcv, +// r: aUsdcr, +// s: aUsdcs, +// }, +// ], +// [false, false] +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const userAUsdcBalance = await aUsdc.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmountForEth.add(expectedDaiAmountForUsdc)); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); +// expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); +// expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); +// }); + +// it('should correctly swap all the balance when using a bigger amount', async () => { +// const { users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // Remove other balance +// await aWETH.connect(user).transfer(users[1].address, parseEther('90')); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); + +// // User will swap liquidity 10 aEth to aDai +// await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + +// // Only has 10 atokens, so all the balance will be swapped +// const bigAmountToSwap = parseEther('100'); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address], +// [dai.address], +// [bigAmountToSwap], +// [expectedDaiAmount], +// [ +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// ], +// [false] +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapLiquiditySwapAdapter.address); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmount); +// expect(userAEthBalance).to.be.eq(Zero); +// expect(adapterAEthBalance).to.be.eq(Zero); +// }); + +// it('should correctly swap all the balance when using permit', async () => { +// const { users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + +// // Remove other balance +// await aWETH.connect(user).transfer(users[1].address, parseEther('90')); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // User will swap liquidity 10 aEth to aDai +// const liquidityToSwap = parseEther('10'); +// expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); + +// // Only has 10 atokens, so all the balance will be swapped +// const bigAmountToSwap = parseEther('100'); + +// const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; +// const deadline = MAX_UINT_AMOUNT; + +// const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; +// if (!ownerPrivateKey) { +// throw new Error('INVALID_OWNER_PK'); +// } +// const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); +// const aWethMsgParams = buildPermitParams( +// chainId, +// aWETH.address, +// '1', +// await aWETH.name(), +// userAddress, +// uniswapLiquiditySwapAdapter.address, +// aWethNonce, +// deadline, +// bigAmountToSwap.toString() +// ); +// const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, aWethMsgParams); + +// await expect( +// uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( +// [weth.address], +// [dai.address], +// [bigAmountToSwap], +// [expectedDaiAmount], +// [ +// { +// amount: bigAmountToSwap, +// deadline, +// v, +// r, +// s, +// }, +// ], +// [false] +// ) +// ) +// .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); + +// const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); +// const adapterDaiAllowance = await dai.allowance( +// uniswapLiquiditySwapAdapter.address, +// userAddress +// ); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapLiquiditySwapAdapter.address); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(adapterDaiAllowance).to.be.eq(Zero); +// expect(userADaiBalance).to.be.eq(expectedDaiAmount); +// expect(userAEthBalance).to.be.eq(Zero); +// expect(adapterAEthBalance).to.be.eq(Zero); +// }); +// }); +// }); +// }); diff --git a/test-suites/test-amm/uniswapAdapters.repay.spec.ts b/test-suites/test-amm/uniswapAdapters.repay.spec.ts new file mode 100644 index 00000000..7aace380 --- /dev/null +++ b/test-suites/test-amm/uniswapAdapters.repay.spec.ts @@ -0,0 +1,1469 @@ +// import { makeSuite, TestEnv } from './helpers/make-suite'; +// import { +// convertToCurrencyDecimals, +// getContract, +// buildPermitParams, +// getSignatureFromTypedData, +// buildRepayAdapterParams, +// } from '../../helpers/contracts-helpers'; +// import { getMockUniswapRouter } from '../../helpers/contracts-getters'; +// import { deployUniswapRepayAdapter } from '../../helpers/contracts-deployments'; +// import { MockUniswapV2Router02 } from '../../types/MockUniswapV2Router02'; +// import { Zero } from '@ethersproject/constants'; +// import BigNumber from 'bignumber.js'; +// import { DRE, evmRevert, evmSnapshot } from '../../helpers/misc-utils'; +// import { ethers } from 'ethers'; +// import { eContractid } from '../../helpers/types'; +// import { StableDebtToken } from '../../types/StableDebtToken'; +// import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +// import { MAX_UINT_AMOUNT } from '../../helpers/constants'; +// import { VariableDebtToken } from '../../types'; +// const { parseEther } = ethers.utils; + +// const { expect } = require('chai'); + +// makeSuite('Uniswap adapters', (testEnv: TestEnv) => { +// let mockUniswapRouter: MockUniswapV2Router02; +// let evmSnapshotId: string; + +// before(async () => { +// mockUniswapRouter = await getMockUniswapRouter(); +// }); + +// beforeEach(async () => { +// evmSnapshotId = await evmSnapshot(); +// }); + +// afterEach(async () => { +// await evmRevert(evmSnapshotId); +// }); + +// describe('UniswapRepayAdapter', () => { +// beforeEach(async () => { +// const { users, weth, dai, usdc, aave, pool, deployer } = testEnv; +// const userAddress = users[0].address; + +// // Provide liquidity +// await dai.mint(parseEther('20000')); +// await dai.approve(pool.address, parseEther('20000')); +// await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + +// const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '2000000'); +// await usdc.mint(usdcLiquidity); +// await usdc.approve(pool.address, usdcLiquidity); +// await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); + +// await weth.mint(parseEther('100')); +// await weth.approve(pool.address, parseEther('100')); +// await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); + +// await aave.mint(parseEther('1000000')); +// await aave.approve(pool.address, parseEther('1000000')); +// await pool.deposit(aave.address, parseEther('1000000'), deployer.address, 0); + +// // Make a deposit for user +// await weth.mint(parseEther('1000')); +// await weth.approve(pool.address, parseEther('1000')); +// await pool.deposit(weth.address, parseEther('1000'), userAddress, 0); + +// await aave.mint(parseEther('1000000')); +// await aave.approve(pool.address, parseEther('1000000')); +// await pool.deposit(aave.address, parseEther('1000000'), userAddress, 0); + +// await usdc.mint(usdcLiquidity); +// await usdc.approve(pool.address, usdcLiquidity); +// await pool.deposit(usdc.address, usdcLiquidity, userAddress, 0); +// }); + +// describe('constructor', () => { +// it('should deploy with correct parameters', async () => { +// const { addressesProvider, weth } = testEnv; +// await deployUniswapRepayAdapter([ +// addressesProvider.address, +// mockUniswapRouter.address, +// weth.address, +// ]); +// }); + +// it('should revert if not valid addresses provider', async () => { +// const { weth } = testEnv; +// expect( +// deployUniswapRepayAdapter([ +// mockUniswapRouter.address, +// mockUniswapRouter.address, +// weth.address, +// ]) +// ).to.be.reverted; +// }); +// }); + +// describe('executeOperation', () => { +// it('should correctly swap tokens and repay debt', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const daiStableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).stableDebtTokenAddress; + +// const daiStableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiStableDebtTokenAddress +// ); + +// const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + +// const liquidityToSwap = amountWETHtoSwap; +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + +// const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// await mockUniswapRouter.setAmountIn( +// flashLoanDebt, +// weth.address, +// dai.address, +// liquidityToSwap +// ); + +// const params = buildRepayAdapterParams( +// weth.address, +// liquidityToSwap, +// 1, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [expectedDaiAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ) +// .to.emit(uniswapRepayAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should correctly swap tokens and repay debt with permit', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const daiStableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).stableDebtTokenAddress; + +// const daiStableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiStableDebtTokenAddress +// ); + +// const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + +// const liquidityToSwap = amountWETHtoSwap; +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; +// const deadline = MAX_UINT_AMOUNT; +// const nonce = (await aWETH._nonces(userAddress)).toNumber(); +// const msgParams = buildPermitParams( +// chainId, +// aWETH.address, +// '1', +// await aWETH.name(), +// userAddress, +// uniswapRepayAdapter.address, +// nonce, +// deadline, +// liquidityToSwap.toString() +// ); + +// const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; +// if (!ownerPrivateKey) { +// throw new Error('INVALID_OWNER_PK'); +// } + +// const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + +// const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// await mockUniswapRouter.setAmountIn( +// flashLoanDebt, +// weth.address, +// dai.address, +// liquidityToSwap +// ); + +// const params = buildRepayAdapterParams( +// weth.address, +// liquidityToSwap, +// 1, +// liquidityToSwap, +// deadline, +// v, +// r, +// s, +// false +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [expectedDaiAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ) +// .to.emit(uniswapRepayAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should revert if caller not lending pool', async () => { +// const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const liquidityToSwap = amountWETHtoSwap; +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + +// const params = buildRepayAdapterParams( +// weth.address, +// liquidityToSwap, +// 1, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await expect( +// uniswapRepayAdapter +// .connect(user) +// .executeOperation( +// [dai.address], +// [expectedDaiAmount.toString()], +// [0], +// userAddress, +// params +// ) +// ).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL'); +// }); + +// it('should revert if there is not debt to repay with the specified rate mode', async () => { +// const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// await weth.connect(user).mint(amountWETHtoSwap); +// await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + +// const liquidityToSwap = amountWETHtoSwap; +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + +// const params = buildRepayAdapterParams( +// weth.address, +// liquidityToSwap, +// 1, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [expectedDaiAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ).to.be.reverted; +// }); + +// it('should revert if there is not debt to repay', async () => { +// const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// await weth.connect(user).mint(amountWETHtoSwap); +// await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// const liquidityToSwap = amountWETHtoSwap; +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + +// const params = buildRepayAdapterParams( +// weth.address, +// liquidityToSwap, +// 1, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [expectedDaiAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ).to.be.reverted; +// }); + +// it('should revert when max amount allowed to swap is bigger than max slippage', async () => { +// const { users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); + +// const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// await mockUniswapRouter.setAmountIn( +// flashLoanDebt, +// weth.address, +// dai.address, +// bigMaxAmountToSwap +// ); + +// const params = buildRepayAdapterParams( +// weth.address, +// bigMaxAmountToSwap, +// 1, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [expectedDaiAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); +// }); + +// it('should swap, repay debt and pull the needed ATokens leaving no leftovers', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const daiStableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).stableDebtTokenAddress; + +// const daiStableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiStableDebtTokenAddress +// ); + +// const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + +// const liquidityToSwap = amountWETHtoSwap; +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); +// const userWethBalanceBefore = await weth.balanceOf(userAddress); + +// const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) +// .multipliedBy(0.995) +// .toFixed(0); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); + +// const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// await mockUniswapRouter.setAmountIn( +// flashLoanDebt, +// weth.address, +// dai.address, +// actualWEthSwapped +// ); + +// const params = buildRepayAdapterParams( +// weth.address, +// liquidityToSwap, +// 1, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await expect( +// pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [expectedDaiAmount.toString()], +// [0], +// userAddress, +// params, +// 0 +// ) +// ) +// .to.emit(uniswapRepayAdapter, 'Swapped') +// .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), flashLoanDebt); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); +// const userWethBalance = await weth.balanceOf(userAddress); + +// expect(adapterAEthBalance).to.be.eq(Zero); +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped)); +// expect(userWethBalance).to.be.eq(userWethBalanceBefore); +// }); + +// it('should correctly swap tokens and repay the whole stable debt', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const daiStableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).stableDebtTokenAddress; + +// const daiStableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiStableDebtTokenAddress +// ); + +// const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + +// // Add a % to repay on top of the debt +// const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) +// .multipliedBy(1.1) +// .toFixed(0); + +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // Add a % to repay on top of the debt +// const amountToRepay = new BigNumber(expectedDaiAmount.toString()) +// .multipliedBy(1.1) +// .toFixed(0); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); +// await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + +// const params = buildRepayAdapterParams( +// weth.address, +// liquidityToSwap, +// 1, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [amountToRepay.toString()], +// [0], +// userAddress, +// params, +// 0 +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + +// expect(adapterAEthBalance).to.be.eq(Zero); +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiStableDebtAmount).to.be.eq(Zero); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should correctly swap tokens and repay the whole variable debt', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + +// const daiStableVariableTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).variableDebtTokenAddress; + +// const daiVariableDebtContract = await getContract( +// eContractid.VariableDebtToken, +// daiStableVariableTokenAddress +// ); + +// const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( +// userAddress +// ); + +// // Add a % to repay on top of the debt +// const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) +// .multipliedBy(1.1) +// .toFixed(0); + +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // Add a % to repay on top of the debt +// const amountToRepay = new BigNumber(expectedDaiAmount.toString()) +// .multipliedBy(1.1) +// .toFixed(0); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); +// await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + +// const params = buildRepayAdapterParams( +// weth.address, +// liquidityToSwap, +// 2, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [amountToRepay.toString()], +// [0], +// userAddress, +// params, +// 0 +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + +// expect(adapterAEthBalance).to.be.eq(Zero); +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiVariableDebtAmount).to.be.eq(Zero); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should correctly repay debt via flash loan using the same asset as collateral', async () => { +// const { users, pool, aDai, dai, uniswapRepayAdapter, helpersContract } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// // Add deposit for user +// await dai.mint(parseEther('30')); +// await dai.approve(pool.address, parseEther('30')); +// await pool.deposit(dai.address, parseEther('30'), userAddress, 0); + +// const amountCollateralToSwap = parseEther('10'); +// const debtAmount = parseEther('10'); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, debtAmount, 2, 0, userAddress); + +// const daiVariableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).variableDebtTokenAddress; + +// const daiVariableDebtContract = await getContract( +// eContractid.VariableDebtToken, +// daiVariableDebtTokenAddress +// ); + +// const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( +// userAddress +// ); + +// const flashLoanDebt = new BigNumber(amountCollateralToSwap.toString()) +// .multipliedBy(1.0009) +// .toFixed(0); + +// await aDai.connect(user).approve(uniswapRepayAdapter.address, flashLoanDebt); +// const userADaiBalanceBefore = await aDai.balanceOf(userAddress); +// const userDaiBalanceBefore = await dai.balanceOf(userAddress); + +// const params = buildRepayAdapterParams( +// dai.address, +// amountCollateralToSwap, +// 2, +// 0, +// 0, +// 0, +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// false +// ); + +// await pool +// .connect(user) +// .flashLoan( +// uniswapRepayAdapter.address, +// [dai.address], +// [amountCollateralToSwap.toString()], +// [0], +// userAddress, +// params, +// 0 +// ); + +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); +// const userDaiBalance = await dai.balanceOf(userAddress); + +// expect(adapterADaiBalance).to.be.eq(Zero, 'adapter aDAI balance should be zero'); +// expect(adapterDaiBalance).to.be.eq(Zero, 'adapter DAI balance should be zero'); +// expect(userDaiVariableDebtAmountBefore).to.be.gte( +// debtAmount, +// ' user DAI variable debt before should be gte debtAmount' +// ); +// expect(userDaiVariableDebtAmount).to.be.lt( +// debtAmount, +// 'user dai variable debt amount should be lt debt amount' +// ); +// expect(userADaiBalance).to.be.lt( +// userADaiBalanceBefore, +// 'user aDAI balance should be lt aDAI prior balance' +// ); +// expect(userADaiBalance).to.be.gte( +// userADaiBalanceBefore.sub(flashLoanDebt), +// 'user aDAI balance should be gte aDAI prior balance sub flash loan debt' +// ); +// expect(userDaiBalance).to.be.eq(userDaiBalanceBefore, 'user dai balance eq prior balance'); +// }); +// }); + +// describe('swapAndRepay', () => { +// it('should correctly swap tokens and repay debt', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const daiStableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).stableDebtTokenAddress; + +// const daiStableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiStableDebtTokenAddress +// ); + +// const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + +// const liquidityToSwap = amountWETHtoSwap; +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + +// await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + +// await uniswapRepayAdapter.connect(user).swapAndRepay( +// weth.address, +// dai.address, +// liquidityToSwap, +// expectedDaiAmount, +// 1, +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// false +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should correctly swap tokens and repay debt with permit', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const daiStableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).stableDebtTokenAddress; + +// const daiStableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiStableDebtTokenAddress +// ); + +// const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + +// const liquidityToSwap = amountWETHtoSwap; +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + +// await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + +// const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; +// const deadline = MAX_UINT_AMOUNT; +// const nonce = (await aWETH._nonces(userAddress)).toNumber(); +// const msgParams = buildPermitParams( +// chainId, +// aWETH.address, +// '1', +// await aWETH.name(), +// userAddress, +// uniswapRepayAdapter.address, +// nonce, +// deadline, +// liquidityToSwap.toString() +// ); + +// const ownerPrivateKey = require('../../test-wallets.js').accounts[1].secretKey; +// if (!ownerPrivateKey) { +// throw new Error('INVALID_OWNER_PK'); +// } + +// const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + +// await uniswapRepayAdapter.connect(user).swapAndRepay( +// weth.address, +// dai.address, +// liquidityToSwap, +// expectedDaiAmount, +// 1, +// { +// amount: liquidityToSwap, +// deadline, +// v, +// r, +// s, +// }, +// false +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); + +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should revert if there is not debt to repay', async () => { +// const { users, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; +// const user = users[0].signer; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// const liquidityToSwap = amountWETHtoSwap; +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + +// await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + +// await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + +// await expect( +// uniswapRepayAdapter.connect(user).swapAndRepay( +// weth.address, +// dai.address, +// liquidityToSwap, +// expectedDaiAmount, +// 1, +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// false +// ) +// ).to.be.reverted; +// }); + +// it('should revert when max amount allowed to swap is bigger than max slippage', async () => { +// const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); + +// await mockUniswapRouter.setDefaultMockValue(bigMaxAmountToSwap); + +// await expect( +// uniswapRepayAdapter.connect(user).swapAndRepay( +// weth.address, +// dai.address, +// bigMaxAmountToSwap, +// expectedDaiAmount, +// 1, +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// false +// ) +// ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); +// }); + +// it('should swap, repay debt and pull the needed ATokens leaving no leftovers', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const daiStableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).stableDebtTokenAddress; + +// const daiStableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiStableDebtTokenAddress +// ); + +// const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + +// const liquidityToSwap = amountWETHtoSwap; +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); +// const userWethBalanceBefore = await weth.balanceOf(userAddress); + +// const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) +// .multipliedBy(0.995) +// .toFixed(0); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); + +// await mockUniswapRouter.setDefaultMockValue(actualWEthSwapped); + +// await uniswapRepayAdapter.connect(user).swapAndRepay( +// weth.address, +// dai.address, +// liquidityToSwap, +// expectedDaiAmount, +// 1, +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// false +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); +// const userWethBalance = await weth.balanceOf(userAddress); + +// expect(adapterAEthBalance).to.be.eq(Zero); +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped)); +// expect(userWethBalance).to.be.eq(userWethBalanceBefore); +// }); + +// it('should correctly swap tokens and repay the whole stable debt', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + +// const daiStableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).stableDebtTokenAddress; + +// const daiStableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiStableDebtTokenAddress +// ); + +// const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + +// // Add a % to repay on top of the debt +// const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) +// .multipliedBy(1.1) +// .toFixed(0); + +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // Add a % to repay on top of the debt +// const amountToRepay = new BigNumber(expectedDaiAmount.toString()) +// .multipliedBy(1.1) +// .toFixed(0); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); +// await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + +// await uniswapRepayAdapter.connect(user).swapAndRepay( +// weth.address, +// dai.address, +// liquidityToSwap, +// amountToRepay, +// 1, +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// false +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + +// expect(adapterAEthBalance).to.be.eq(Zero); +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiStableDebtAmount).to.be.eq(Zero); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should correctly swap tokens and repay the whole variable debt', async () => { +// const { +// users, +// pool, +// weth, +// aWETH, +// oracle, +// dai, +// uniswapRepayAdapter, +// helpersContract, +// } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + +// const daiPrice = await oracle.getAssetPrice(dai.address); +// const expectedDaiAmount = await convertToCurrencyDecimals( +// dai.address, +// new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) +// ); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + +// const daiStableVariableTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).variableDebtTokenAddress; + +// const daiVariableDebtContract = await getContract( +// eContractid.VariableDebtToken, +// daiStableVariableTokenAddress +// ); + +// const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( +// userAddress +// ); + +// // Add a % to repay on top of the debt +// const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) +// .multipliedBy(1.1) +// .toFixed(0); + +// await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); +// const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + +// // Add a % to repay on top of the debt +// const amountToRepay = new BigNumber(expectedDaiAmount.toString()) +// .multipliedBy(1.1) +// .toFixed(0); + +// await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); +// await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + +// await uniswapRepayAdapter.connect(user).swapAndRepay( +// weth.address, +// dai.address, +// liquidityToSwap, +// amountToRepay, +// 2, +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// false +// ); + +// const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); +// const userAEthBalance = await aWETH.balanceOf(userAddress); +// const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + +// expect(adapterAEthBalance).to.be.eq(Zero); +// expect(adapterWethBalance).to.be.eq(Zero); +// expect(adapterDaiBalance).to.be.eq(Zero); +// expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); +// expect(userDaiVariableDebtAmount).to.be.eq(Zero); +// expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); +// expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); +// }); + +// it('should correctly repay debt using the same asset as collateral', async () => { +// const { users, pool, dai, uniswapRepayAdapter, helpersContract, aDai } = testEnv; +// const user = users[0].signer; +// const userAddress = users[0].address; + +// // Add deposit for user +// await dai.mint(parseEther('30')); +// await dai.approve(pool.address, parseEther('30')); +// await pool.deposit(dai.address, parseEther('30'), userAddress, 0); + +// const amountCollateralToSwap = parseEther('4'); + +// const debtAmount = parseEther('3'); + +// // Open user Debt +// await pool.connect(user).borrow(dai.address, debtAmount, 2, 0, userAddress); + +// const daiVariableDebtTokenAddress = ( +// await helpersContract.getReserveTokensAddresses(dai.address) +// ).variableDebtTokenAddress; + +// const daiVariableDebtContract = await getContract( +// eContractid.StableDebtToken, +// daiVariableDebtTokenAddress +// ); + +// const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( +// userAddress +// ); + +// await aDai.connect(user).approve(uniswapRepayAdapter.address, amountCollateralToSwap); +// const userADaiBalanceBefore = await aDai.balanceOf(userAddress); +// const userDaiBalanceBefore = await dai.balanceOf(userAddress); + +// await uniswapRepayAdapter.connect(user).swapAndRepay( +// dai.address, +// dai.address, +// amountCollateralToSwap, +// amountCollateralToSwap, +// 2, +// { +// amount: 0, +// deadline: 0, +// v: 0, +// r: '0x0000000000000000000000000000000000000000000000000000000000000000', +// s: '0x0000000000000000000000000000000000000000000000000000000000000000', +// }, +// false +// ); + +// const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); +// const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); +// const userADaiBalance = await aDai.balanceOf(userAddress); +// const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); +// const userDaiBalance = await dai.balanceOf(userAddress); + +// expect(adapterADaiBalance).to.be.eq(Zero, 'adapter aADAI should be zero'); +// expect(adapterDaiBalance).to.be.eq(Zero, 'adapter DAI should be zero'); +// expect(userDaiVariableDebtAmountBefore).to.be.gte( +// debtAmount, +// 'user dai variable debt before should be gte debtAmount' +// ); +// expect(userDaiVariableDebtAmount).to.be.lt( +// debtAmount, +// 'current user dai variable debt amount should be less than debtAmount' +// ); +// expect(userADaiBalance).to.be.lt( +// userADaiBalanceBefore, +// 'current user aDAI balance should be less than prior balance' +// ); +// expect(userADaiBalance).to.be.gte( +// userADaiBalanceBefore.sub(amountCollateralToSwap), +// 'current user aDAI balance should be gte user balance sub swapped collateral' +// ); +// expect(userDaiBalance).to.be.eq( +// userDaiBalanceBefore, +// 'user DAI balance should remain equal' +// ); +// }); +// }); +// }); +// }); diff --git a/test-suites/test-amm/upgradeability.spec.ts b/test-suites/test-amm/upgradeability.spec.ts new file mode 100644 index 00000000..89626b41 --- /dev/null +++ b/test-suites/test-amm/upgradeability.spec.ts @@ -0,0 +1,239 @@ +import { expect } from 'chai'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, eContractid } from '../../helpers/types'; +import { deployContract, getContract } from '../../helpers/contracts-helpers'; +import { MockAToken } from '../../types/MockAToken'; +import { MockStableDebtToken } from '../../types/MockStableDebtToken'; +import { MockVariableDebtToken } from '../../types/MockVariableDebtToken'; +import { ZERO_ADDRESS } from '../../helpers/constants'; +import { + getAToken, + getMockStableDebtToken, + getMockVariableDebtToken, + getStableDebtToken, + getVariableDebtToken, +} from '../../helpers/contracts-getters'; +import { + deployMockAToken, + deployMockStableDebtToken, + deployMockVariableDebtToken, +} from '../../helpers/contracts-deployments'; + +makeSuite('Upgradeability', (testEnv: TestEnv) => { + const { CALLER_NOT_POOL_ADMIN } = ProtocolErrors; + let newATokenAddress: string; + let newStableTokenAddress: string; + let newVariableTokenAddress: string; + + before('deploying instances', async () => { + const { dai, pool } = testEnv; + const aTokenInstance = await deployMockAToken([ + pool.address, + dai.address, + ZERO_ADDRESS, + ZERO_ADDRESS, + 'Aave AMM Market DAI updated', + 'aAmmDAI', + '0x10' + ]); + + const stableDebtTokenInstance = await deployMockStableDebtToken([ + pool.address, + dai.address, + ZERO_ADDRESS, + 'Aave AMM Market stable debt DAI updated', + 'stableDebtAmmDAI', + '0x10' + ]); + + const variableDebtTokenInstance = await deployMockVariableDebtToken([ + pool.address, + dai.address, + ZERO_ADDRESS, + 'Aave AMM Market variable debt DAI updated', + 'variableDebtAmmDAI', + '0x10' + ]); + + newATokenAddress = aTokenInstance.address; + newVariableTokenAddress = variableDebtTokenInstance.address; + newStableTokenAddress = stableDebtTokenInstance.address; + }); + + it('Tries to update the DAI Atoken implementation with a different address than the lendingPoolManager', async () => { + const { dai, configurator, users } = testEnv; + + const name = await (await getAToken(newATokenAddress)).name(); + const symbol = await (await getAToken(newATokenAddress)).symbol(); + + const updateATokenInputParams: { + asset: string; + treasury: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + params: string; + } = { + asset: dai.address, + treasury: ZERO_ADDRESS, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newATokenAddress, + params: '0x10' + }; + await expect( + configurator.connect(users[1].signer).updateAToken(updateATokenInputParams) + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Upgrades the DAI Atoken implementation ', async () => { + const { dai, configurator, aDai } = testEnv; + + const name = await (await getAToken(newATokenAddress)).name(); + const symbol = await (await getAToken(newATokenAddress)).symbol(); + + const updateATokenInputParams: { + asset: string; + treasury: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + } = { + asset: dai.address, + treasury: ZERO_ADDRESS, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newATokenAddress, + }; + await configurator.updateAToken(updateATokenInputParams); + + const tokenName = await aDai.name(); + + expect(tokenName).to.be.eq('Aave AMM Market DAI updated', 'Invalid token name'); + }); + + it('Tries to update the DAI Stable debt token implementation with a different address than the lendingPoolManager', async () => { + const { dai, configurator, users } = testEnv; + + const name = await (await getStableDebtToken(newStableTokenAddress)).name(); + const symbol = await (await getStableDebtToken(newStableTokenAddress)).symbol(); + + + const updateDebtTokenInput: { + asset: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + } = { + asset: dai.address, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newStableTokenAddress, + } + + await expect( + configurator + .connect(users[1].signer) + .updateStableDebtToken(updateDebtTokenInput) + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Upgrades the DAI stable debt token implementation ', async () => { + const { dai, configurator, pool, helpersContract } = testEnv; + + const name = await (await getStableDebtToken(newStableTokenAddress)).name(); + const symbol = await (await getStableDebtToken(newStableTokenAddress)).symbol(); + + + const updateDebtTokenInput: { + asset: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + } = { + asset: dai.address, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newStableTokenAddress, + } + + await configurator.updateStableDebtToken(updateDebtTokenInput); + + const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(dai.address); + + const debtToken = await getMockStableDebtToken(stableDebtTokenAddress); + + const tokenName = await debtToken.name(); + + expect(tokenName).to.be.eq('Aave AMM Market stable debt DAI updated', 'Invalid token name'); + }); + + it('Tries to update the DAI variable debt token implementation with a different address than the lendingPoolManager', async () => { + const {dai, configurator, users} = testEnv; + + const name = await (await getVariableDebtToken(newVariableTokenAddress)).name(); + const symbol = await (await getVariableDebtToken(newVariableTokenAddress)).symbol(); + + const updateDebtTokenInput: { + asset: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + } = { + asset: dai.address, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newVariableTokenAddress, + } + + await expect( + configurator + .connect(users[1].signer) + .updateVariableDebtToken(updateDebtTokenInput) + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Upgrades the DAI variable debt token implementation ', async () => { + const {dai, configurator, pool, helpersContract} = testEnv; + + const name = await (await getVariableDebtToken(newVariableTokenAddress)).name(); + const symbol = await (await getVariableDebtToken(newVariableTokenAddress)).symbol(); + + const updateDebtTokenInput: { + asset: string; + incentivesController: string; + name: string; + symbol: string; + implementation: string; + } = { + asset: dai.address, + incentivesController: ZERO_ADDRESS, + name: name, + symbol: symbol, + implementation: newVariableTokenAddress, + } + //const name = await (await getAToken(newATokenAddress)).name(); + + await configurator.updateVariableDebtToken(updateDebtTokenInput); + + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + dai.address + ); + + const debtToken = await getMockVariableDebtToken(variableDebtTokenAddress); + + const tokenName = await debtToken.name(); + + expect(tokenName).to.be.eq('Aave AMM Market variable debt DAI updated', 'Invalid token name'); + }); +}); diff --git a/test-suites/test-amm/variable-debt-token.spec.ts b/test-suites/test-amm/variable-debt-token.spec.ts new file mode 100644 index 00000000..d4afbbf0 --- /dev/null +++ b/test-suites/test-amm/variable-debt-token.spec.ts @@ -0,0 +1,36 @@ +import { expect } from 'chai'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, TokenContractId, eContractid } from '../../helpers/types'; +import { getVariableDebtToken } from '../../helpers/contracts-getters'; + +makeSuite('Variable debt token tests', (testEnv: TestEnv) => { + const { CT_CALLER_MUST_BE_LENDING_POOL } = ProtocolErrors; + + it('Tries to invoke mint not being the LendingPool', async () => { + const { deployer, pool, dai, helpersContract } = testEnv; + + const daiVariableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).variableDebtTokenAddress; + + const variableDebtContract = await getVariableDebtToken(daiVariableDebtTokenAddress); + + await expect( + variableDebtContract.mint(deployer.address, deployer.address, '1', '1') + ).to.be.revertedWith(CT_CALLER_MUST_BE_LENDING_POOL); + }); + + it('Tries to invoke burn not being the LendingPool', async () => { + const { deployer, pool, dai, helpersContract } = testEnv; + + const daiVariableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).variableDebtTokenAddress; + + const variableDebtContract = await getVariableDebtToken(daiVariableDebtTokenAddress); + + await expect(variableDebtContract.burn(deployer.address, '1', '1')).to.be.revertedWith( + CT_CALLER_MUST_BE_LENDING_POOL + ); + }); +}); diff --git a/test-suites/test-amm/weth-gateway.spec.ts b/test-suites/test-amm/weth-gateway.spec.ts new file mode 100644 index 00000000..80a3b169 --- /dev/null +++ b/test-suites/test-amm/weth-gateway.spec.ts @@ -0,0 +1,361 @@ +import { MAX_UINT_AMOUNT } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { parseEther } from 'ethers/lib/utils'; +import { DRE, waitForTx } from '../../helpers/misc-utils'; +import { BigNumber } from 'ethers'; +import { getStableDebtToken, getVariableDebtToken } from '../../helpers/contracts-getters'; +import { deploySelfdestructTransferMock } from '../../helpers/contracts-deployments'; +import { ProtocolErrors } from '../../helpers/types'; + +const { expect } = require('chai'); + +makeSuite('Use native ETH at LendingPool via WETHGateway', (testEnv: TestEnv) => { + const zero = BigNumber.from('0'); + const depositSize = parseEther('5'); + const daiSize = parseEther('10000'); + it('Deposit WETH via WethGateway and DAI', async () => { + const { users, wethGateway, aWETH, pool } = testEnv; + + const user = users[1]; + const depositor = users[0]; + + // Deposit liquidity with native ETH + await wethGateway + .connect(depositor.signer) + .depositETH(pool.address, depositor.address, '0', { value: depositSize }); + + // Deposit with native ETH + await wethGateway.connect(user.signer).depositETH(pool.address, user.address, '0', { value: depositSize }); + + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero); + expect(aTokensBalance).to.be.gte(depositSize); + }); + + it('Withdraw WETH - Partial', async () => { + const { users, wethGateway, aWETH, pool } = testEnv; + + const user = users[1]; + const priorEthersBalance = await user.signer.getBalance(); + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero, 'User should have aTokens.'); + + // Partially withdraw native ETH + const partialWithdraw = await convertToCurrencyDecimals(aWETH.address, '2'); + + // Approve the aTokens to Gateway so Gateway can withdraw and convert to Ether + const approveTx = await aWETH + .connect(user.signer) + .approve(wethGateway.address, MAX_UINT_AMOUNT); + const { gasUsed: approveGas } = await waitForTx(approveTx); + + // Partial Withdraw and send native Ether to user + const { gasUsed: withdrawGas } = await waitForTx( + await wethGateway.connect(user.signer).withdrawETH(pool.address, partialWithdraw, user.address) + ); + + const afterPartialEtherBalance = await user.signer.getBalance(); + const afterPartialATokensBalance = await aWETH.balanceOf(user.address); + const gasCosts = approveGas.add(withdrawGas).mul(approveTx.gasPrice); + + expect(afterPartialEtherBalance).to.be.equal( + priorEthersBalance.add(partialWithdraw).sub(gasCosts), + 'User ETHER balance should contain the partial withdraw' + ); + expect(afterPartialATokensBalance).to.be.equal( + aTokensBalance.sub(partialWithdraw), + 'User aWETH balance should be substracted' + ); + }); + + it('Withdraw WETH - Full', async () => { + const { users, aWETH, wethGateway, pool } = testEnv; + + const user = users[1]; + const priorEthersBalance = await user.signer.getBalance(); + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero, 'User should have aTokens.'); + + // Approve the aTokens to Gateway so Gateway can withdraw and convert to Ether + const approveTx = await aWETH + .connect(user.signer) + .approve(wethGateway.address, MAX_UINT_AMOUNT); + const { gasUsed: approveGas } = await waitForTx(approveTx); + + // Full withdraw + const { gasUsed: withdrawGas } = await waitForTx( + await wethGateway.connect(user.signer).withdrawETH(pool.address, MAX_UINT_AMOUNT, user.address) + ); + + const afterFullEtherBalance = await user.signer.getBalance(); + const afterFullATokensBalance = await aWETH.balanceOf(user.address); + const gasCosts = approveGas.add(withdrawGas).mul(approveTx.gasPrice); + + expect(afterFullEtherBalance).to.be.eq( + priorEthersBalance.add(aTokensBalance).sub(gasCosts), + 'User ETHER balance should contain the full withdraw' + ); + expect(afterFullATokensBalance).to.be.eq(0, 'User aWETH balance should be zero'); + }); + + it('Borrowing stable WETH should fail since stable borrowing is disabled', async () => { + const { users, wethGateway, aDai, weth, dai, pool, helpersContract } = testEnv; + const borrowSize = parseEther('1'); + const repaySize = borrowSize.add(borrowSize.mul(5).div(100)); + const user = users[1]; + const depositor = users[0]; + + // Deposit with native ETH + await wethGateway + .connect(depositor.signer) + .depositETH(pool.address, depositor.address, '0', { value: depositSize }); + + const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const stableDebtToken = await getStableDebtToken(stableDebtTokenAddress); + + // Deposit 10000 DAI + await dai.connect(user.signer).mint(daiSize); + await dai.connect(user.signer).approve(pool.address, daiSize); + await pool.connect(user.signer).deposit(dai.address, daiSize, user.address, '0'); + + const aTokensBalance = await aDai.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero); + expect(aTokensBalance).to.be.gte(daiSize); + + // Borrow WETH with WETH as collateral + + await expect(pool.connect(user.signer).borrow(weth.address, borrowSize, '1', '0', user.address)) + .to.be.revertedWith(ProtocolErrors.VL_STABLE_BORROWING_NOT_ENABLED); + + + // const debtBalance = await stableDebtToken.balanceOf(user.address); + + // expect(debtBalance).to.be.gt(zero); + + // // Full Repay WETH with native ETH + // await waitForTx( + // await wethGateway + // .connect(user.signer) + // .repayETH(MAX_UINT_AMOUNT, '1', user.address, { value: repaySize }) + // ); + + // const debtBalanceAfterRepay = await stableDebtToken.balanceOf(user.address); + // expect(debtBalanceAfterRepay).to.be.eq(zero); + + // // Withdraw DAI + // await aDai.connect(user.signer).approve(pool.address, MAX_UINT_AMOUNT); + // await pool.connect(user.signer).withdraw(dai.address, MAX_UINT_AMOUNT, user.address); + }); + + it('Borrow variable WETH and Full Repay with ETH', async () => { + const { users, wethGateway, aWETH, weth, pool, helpersContract } = testEnv; + const borrowSize = parseEther('1'); + const repaySize = borrowSize.add(borrowSize.mul(5).div(100)); + const user = users[1]; + + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const varDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + // Deposit with native ETH + await wethGateway.connect(user.signer).depositETH(pool.address, user.address, '0', { value: depositSize }); + + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero); + expect(aTokensBalance).to.be.gte(depositSize); + + // Borrow WETH with WETH as collateral + await waitForTx( + await pool.connect(user.signer).borrow(weth.address, borrowSize, '2', '0', user.address) + ); + + const debtBalance = await varDebtToken.balanceOf(user.address); + + expect(debtBalance).to.be.gt(zero); + + // Partial Repay WETH loan with native ETH + const partialPayment = repaySize.div(2); + await waitForTx( + await wethGateway + .connect(user.signer) + .repayETH(pool.address, partialPayment, '2', user.address, { value: partialPayment }) + ); + + const debtBalanceAfterPartialRepay = await varDebtToken.balanceOf(user.address); + expect(debtBalanceAfterPartialRepay).to.be.lt(debtBalance); + + // Full Repay WETH loan with native ETH + await waitForTx( + await wethGateway + .connect(user.signer) + .repayETH(pool.address, MAX_UINT_AMOUNT, '2', user.address, { value: repaySize }) + ); + const debtBalanceAfterFullRepay = await varDebtToken.balanceOf(user.address); + expect(debtBalanceAfterFullRepay).to.be.eq(zero); + }); + + it('Borrow ETH via delegateApprove ETH and repays back', async () => { + const { users, wethGateway, aWETH, weth, helpersContract, pool } = testEnv; + const borrowSize = parseEther('1'); + const user = users[2]; + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + const varDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + const priorDebtBalance = await varDebtToken.balanceOf(user.address); + expect(priorDebtBalance).to.be.eq(zero); + + // Deposit WETH with native ETH + await wethGateway.connect(user.signer).depositETH(pool.address, user.address, '0', { value: depositSize }); + + const aTokensBalance = await aWETH.balanceOf(user.address); + + expect(aTokensBalance).to.be.gt(zero); + expect(aTokensBalance).to.be.gte(depositSize); + + // Delegates borrowing power of WETH to WETHGateway + await waitForTx( + await varDebtToken.connect(user.signer).approveDelegation(wethGateway.address, borrowSize) + ); + + // Borrows ETH with WETH as collateral + await waitForTx(await wethGateway.connect(user.signer).borrowETH(pool.address, borrowSize, '2', '0')); + + const debtBalance = await varDebtToken.balanceOf(user.address); + + expect(debtBalance).to.be.gt(zero); + + // Full Repay WETH loan with native ETH + await waitForTx( + await wethGateway + .connect(user.signer) + .repayETH(pool.address, MAX_UINT_AMOUNT, '2', user.address, { value: borrowSize.mul(2) }) + ); + const debtBalanceAfterFullRepay = await varDebtToken.balanceOf(user.address); + expect(debtBalanceAfterFullRepay).to.be.eq(zero); + }); + + it('Should revert if receiver function receives Ether if not WETH', async () => { + const { users, wethGateway } = testEnv; + const user = users[0]; + const amount = parseEther('1'); + + // Call receiver function (empty data + value) + await expect( + user.signer.sendTransaction({ + to: wethGateway.address, + value: amount, + gasLimit: DRE.network.config.gas, + }) + ).to.be.revertedWith('Receive not allowed'); + }); + + it('Should revert if fallback functions is called with Ether', async () => { + const { users, wethGateway } = testEnv; + const user = users[0]; + const amount = parseEther('1'); + const fakeABI = ['function wantToCallFallback()']; + const abiCoder = new DRE.ethers.utils.Interface(fakeABI); + const fakeMethodEncoded = abiCoder.encodeFunctionData('wantToCallFallback', []); + + // Call fallback function with value + await expect( + user.signer.sendTransaction({ + to: wethGateway.address, + data: fakeMethodEncoded, + value: amount, + gasLimit: DRE.network.config.gas, + }) + ).to.be.revertedWith('Fallback not allowed'); + }); + + it('Should revert if fallback functions is called', async () => { + const { users, wethGateway } = testEnv; + const user = users[0]; + + const fakeABI = ['function wantToCallFallback()']; + const abiCoder = new DRE.ethers.utils.Interface(fakeABI); + const fakeMethodEncoded = abiCoder.encodeFunctionData('wantToCallFallback', []); + + // Call fallback function without value + await expect( + user.signer.sendTransaction({ + to: wethGateway.address, + data: fakeMethodEncoded, + gasLimit: DRE.network.config.gas, + }) + ).to.be.revertedWith('Fallback not allowed'); + }); + + + it('Owner can do emergency token recovery', async () => { + const { users, dai, wethGateway, deployer } = testEnv; + const user = users[0]; + const amount = parseEther('1'); + + await dai.connect(user.signer).mint(amount); + const daiBalanceAfterMint = await dai.balanceOf(user.address); + + await dai.connect(user.signer).transfer(wethGateway.address, amount); + const daiBalanceAfterBadTransfer = await dai.balanceOf(user.address); + expect(daiBalanceAfterBadTransfer).to.be.eq( + daiBalanceAfterMint.sub(amount), + 'User should have lost the funds here.' + ); + + await wethGateway + .connect(deployer.signer) + .emergencyTokenTransfer(dai.address, user.address, amount); + const daiBalanceAfterRecovery = await dai.balanceOf(user.address); + + expect(daiBalanceAfterRecovery).to.be.eq( + daiBalanceAfterMint, + 'User should recover the funds due emergency token transfer' + ); + }); + + it('Owner can do emergency native ETH recovery', async () => { + const { users, wethGateway, deployer } = testEnv; + const user = users[0]; + const amount = parseEther('1'); + const userBalancePriorCall = await user.signer.getBalance(); + + // Deploy contract with payable selfdestruct contract + const selfdestructContract = await deploySelfdestructTransferMock(); + + // Selfdestruct the mock, pointing to WETHGateway address + const callTx = await selfdestructContract + .connect(user.signer) + .destroyAndTransfer(wethGateway.address, { value: amount }); + const { gasUsed } = await waitForTx(callTx); + const gasFees = gasUsed.mul(callTx.gasPrice); + const userBalanceAfterCall = await user.signer.getBalance(); + + expect(userBalanceAfterCall).to.be.eq(userBalancePriorCall.sub(amount).sub(gasFees), ''); + ('User should have lost the funds'); + + // Recover the funds from the contract and sends back to the user + await wethGateway.connect(deployer.signer).emergencyEtherTransfer(user.address, amount); + + const userBalanceAfterRecovery = await user.signer.getBalance(); + const wethGatewayAfterRecovery = await DRE.ethers.provider.getBalance(wethGateway.address); + + expect(userBalanceAfterRecovery).to.be.eq( + userBalancePriorCall.sub(gasFees), + 'User should recover the funds due emergency eth transfer.' + ); + expect(wethGatewayAfterRecovery).to.be.eq('0', 'WETHGateway ether balance should be zero.'); + }); +}); diff --git a/test/rate-strategy.spec.ts b/test/rate-strategy.spec.ts deleted file mode 100644 index b56c754e..00000000 --- a/test/rate-strategy.spec.ts +++ /dev/null @@ -1,165 +0,0 @@ -// import { -// IReserveParams, -// iAavePoolAssets, -// iAssetsWithoutETH, -// ITestEnvWithoutInstances, -// } from "../utils/types" -// import { - -// LendingPoolAddressesProviderInstance, - -// DefaultReserveInterestRateStrategyInstance, -// MintableERC20Instance, -// } from "../utils/typechain-types/truffle-contracts" -// import { testEnvProviderWithoutInstances} from "../utils/truffle/dlp-tests-env" -// import {RAY} from "../utils/constants" -// import BigNumber from "bignumber.js" - -// const {expect} = require("chai") - -// contract("Interest rate strategy", async ([deployer, ...users]) => { -// let _testEnvProvider: ITestEnvWithoutInstances -// let _strategyInstance: DefaultReserveInterestRateStrategyInstance -// let _tokenInstances: iAssetsWithoutETH -// let _addressesProviderInstance: LendingPoolAddressesProviderInstance -// let _reservesParams: iAavePoolAssets - -// before("Initializing test variables", async () => { -// console.time('setup-test'); -// _testEnvProvider = await testEnvProviderWithoutInstances( -// artifacts, -// [deployer, ...users], -// ) - -// const { -// getAllAssetsInstances, -// getLendingPoolAddressesProviderInstance, -// getAavePoolReservesParams, -// } = _testEnvProvider - -// const instances = await Promise.all([ -// getAllAssetsInstances(), -// getLendingPoolAddressesProviderInstance() -// ]) - -// _tokenInstances = instances[0] -// _addressesProviderInstance = instances[1] -// _reservesParams = await getAavePoolReservesParams() -// console.timeEnd('setup-test'); -// }) - -// it("Deploys a new instance of a DefaultReserveInterestRateStrategy contract", async () => { -// const {DAI: daiInstance} = _tokenInstances - -// const {DAI: daiConfiguration} = _reservesParams - -// const contract: any = await artifacts.require("DefaultReserveInterestRateStrategy") -// const mathLibrary = await artifacts.require("WadRayMath") -// const mathLibraryInstance = await mathLibrary.new() - -// await contract.link("WadRayMath", mathLibraryInstance.address) - -// _strategyInstance = await contract.new( -// daiInstance.address, -// _addressesProviderInstance.address, -// daiConfiguration.baseVariableBorrowRate, -// daiConfiguration.variableRateSlope1, -// daiConfiguration.variableRateSlope2, -// daiConfiguration.stableRateSlope1, -// daiConfiguration.stableRateSlope2, -// ) -// }) - -// it("Checks rates at 0% utilization rate", async () => { -// const {DAI: daiInstance} = _tokenInstances -// const {DAI: daiConfiguration} = _reservesParams -// const data: any = await _strategyInstance.calculateInterestRates( -// daiInstance.address, -// "1000000000000000000", -// "0", -// "0", -// "0", -// ) - -// expect(data.currentLiquidityRate.toString()).to.be.equal("0", "Invalid liquidity rate") -// expect(data.currentStableBorrowRate.toString()).to.be.equal( -// new BigNumber(0.039).times(RAY).toFixed(0), -// "Invalid stable rate", -// ) -// expect(data.currentVariableBorrowRate.toString()).to.be.equal( -// daiConfiguration.baseVariableBorrowRate, -// "Invalid variable rate", -// ) -// }) - -// it("Checks rates at 80% utilization rate", async () => { -// const {DAI: daiInstance} = _tokenInstances -// const {DAI: daiConfiguration} = _reservesParams -// const data: any = await _strategyInstance.calculateInterestRates( -// daiInstance.address, -// "200000000000000000", -// "0", -// "800000000000000000", -// "0", -// ) - -// const expectedVariableRate = new BigNumber(daiConfiguration.baseVariableBorrowRate) -// .plus(daiConfiguration.variableRateSlope1) - -// expect(data.currentLiquidityRate.toString()).to.be.equal( -// expectedVariableRate.times(0.8).toFixed(0), -// "Invalid liquidity rate", -// ) - -// expect(data.currentVariableBorrowRate.toString()).to.be.equal( -// new BigNumber(daiConfiguration.baseVariableBorrowRate) -// .plus(daiConfiguration.variableRateSlope1) -// .toFixed(0), -// "Invalid variable rate", -// ) - -// expect(data.currentStableBorrowRate.toString()).to.be.equal( -// new BigNumber(0.039) -// .times(RAY) -// .plus(daiConfiguration.stableRateSlope1) -// .toFixed(0), -// "Invalid stable rate", -// ) -// }) - -// it("Checks rates at 100% utilization rate", async () => { -// const {DAI: daiInstance} = _tokenInstances -// const {DAI: daiConfiguration} = _reservesParams -// const data: any = await _strategyInstance.calculateInterestRates( -// daiInstance.address, -// "0", -// "0", -// "1000000000000000000", -// "0", -// ) - -// const expectedVariableRate = new BigNumber(daiConfiguration.baseVariableBorrowRate) -// .plus(daiConfiguration.variableRateSlope1) -// .plus(daiConfiguration.variableRateSlope2) -// .toFixed(0) - -// expect(data.currentLiquidityRate.toString()).to.be.equal( -// expectedVariableRate, -// "Invalid liquidity rate", -// ) - -// expect(data.currentVariableBorrowRate.toString()).to.be.equal( -// expectedVariableRate, -// "Invalid variable rate", -// ) - -// expect(data.currentStableBorrowRate.toString()).to.be.equal( -// new BigNumber(0.039) -// .times(RAY) -// .plus(daiConfiguration.stableRateSlope1) -// .plus(daiConfiguration.stableRateSlope2) -// .toFixed(0), -// "Invalid stable rate", -// ) -// }) -// }) diff --git a/test/upgradeability.spec.ts b/test/upgradeability.spec.ts deleted file mode 100644 index 28e1c335..00000000 --- a/test/upgradeability.spec.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { expect } from 'chai'; -import { makeSuite, TestEnv } from './helpers/make-suite'; -import { ProtocolErrors, eContractid } from '../helpers/types'; -import { deployContract, getContract } from '../helpers/contracts-helpers'; -import { MockAToken } from '../types/MockAToken'; -import { MockStableDebtToken } from '../types/MockStableDebtToken'; -import { MockVariableDebtToken } from '../types/MockVariableDebtToken'; -import { ZERO_ADDRESS } from '../helpers/constants'; -import { - getAToken, - getMockStableDebtToken, - getMockVariableDebtToken, - getVariableDebtToken, -} from '../helpers/contracts-getters'; -import { - deployMockAToken, - deployMockStableDebtToken, - deployMockVariableDebtToken, -} from '../helpers/contracts-deployments'; - -makeSuite('Upgradeability', (testEnv: TestEnv) => { - const { CALLER_NOT_POOL_ADMIN } = ProtocolErrors; - let newATokenAddress: string; - let newStableTokenAddress: string; - let newVariableTokenAddress: string; - - before('deploying instances', async () => { - const { dai, pool } = testEnv; - const aTokenInstance = await deployMockAToken([ - pool.address, - dai.address, - ZERO_ADDRESS, - 'Aave Interest bearing DAI updated', - 'aDAI', - ZERO_ADDRESS, - ]); - - const stableDebtTokenInstance = await deployMockStableDebtToken([ - pool.address, - dai.address, - 'Aave stable debt bearing DAI updated', - 'stableDebtDAI', - ZERO_ADDRESS, - ]); - - const variableDebtTokenInstance = await deployMockVariableDebtToken([ - pool.address, - dai.address, - 'Aave variable debt bearing DAI updated', - 'variableDebtDAI', - ZERO_ADDRESS, - ]); - - newATokenAddress = aTokenInstance.address; - newVariableTokenAddress = variableDebtTokenInstance.address; - newStableTokenAddress = stableDebtTokenInstance.address; - }); - - it('Tries to update the DAI Atoken implementation with a different address than the lendingPoolManager', async () => { - const { dai, configurator, users } = testEnv; - - await expect( - configurator.connect(users[1].signer).updateAToken(dai.address, newATokenAddress) - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); - }); - - it('Upgrades the DAI Atoken implementation ', async () => { - const { dai, configurator, aDai } = testEnv; - - const name = await (await getAToken(newATokenAddress)).name(); - - await configurator.updateAToken(dai.address, newATokenAddress); - - const tokenName = await aDai.name(); - - expect(tokenName).to.be.eq('Aave Interest bearing DAI updated', 'Invalid token name'); - }); - - it('Tries to update the DAI Stable debt token implementation with a different address than the lendingPoolManager', async () => { - const { dai, configurator, users } = testEnv; - - await expect( - configurator - .connect(users[1].signer) - .updateStableDebtToken(dai.address, newStableTokenAddress) - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); - }); - - it('Upgrades the DAI stable debt token implementation ', async () => { - const { dai, configurator, pool, helpersContract } = testEnv; - - const name = await (await getAToken(newATokenAddress)).name(); - - await configurator.updateStableDebtToken(dai.address, newStableTokenAddress); - - const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(dai.address); - - const debtToken = await getMockStableDebtToken(stableDebtTokenAddress); - - const tokenName = await debtToken.name(); - - expect(tokenName).to.be.eq('Aave stable debt bearing DAI updated', 'Invalid token name'); - }); - - it('Tries to update the DAI variable debt token implementation with a different address than the lendingPoolManager', async () => { - const { dai, configurator, users } = testEnv; - - await expect( - configurator - .connect(users[1].signer) - .updateVariableDebtToken(dai.address, newVariableTokenAddress) - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); - }); - - it('Upgrades the DAI variable debt token implementation ', async () => { - const { dai, configurator, pool, helpersContract } = testEnv; - - const name = await (await getAToken(newATokenAddress)).name(); - - await configurator.updateVariableDebtToken(dai.address, newVariableTokenAddress); - - const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( - dai.address - ); - - const debtToken = await getMockVariableDebtToken(variableDebtTokenAddress); - - const tokenName = await debtToken.name(); - - expect(tokenName).to.be.eq('Aave variable debt bearing DAI updated', 'Invalid token name'); - }); -}); diff --git a/tsconfig.json b/tsconfig.json index 5d3b034e..9974c3b6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "noImplicitAny": false, "resolveJsonModule": true }, - "include": ["./scripts", "./test", "./tasks"], + "include": ["./scripts", "./test", "./tasks", "test-suites/test-aave/uniswapAdapters.repay.spec.ts", "test-suites/test-aave/upgradeability.spec.ts", "test-suites/test-aave/variable-debt-token.spec.ts", "test-suites/test-aave/weth-gateway.spec.ts"], "files": [ "./hardhat.config.ts", "./modules/tenderly/tenderly.d.ts",