diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index b5753258..82bc7389 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -366,9 +366,9 @@ interface ILendingPool { /** * @dev Returns the user account data across all the reserves * @param user The address of the user - * @return totalCollateralETH the total collateral in ETH of the user - * @return totalDebtETH the total debt in ETH of the user - * @return availableBorrowsETH the borrowing power left of the user + * @return totalCollateralBase the total collateral of the user in the base currency used by the price feed + * @return totalDebtBase the total debt of the user in the base currency used by the price feed + * @return availableBorrowsBase the borrowing power left of the user in the base currency used by the price feed * @return currentLiquidationThreshold the liquidation threshold of the user * @return ltv the loan to value of the user * @return healthFactor the current health factor of the user @@ -377,9 +377,9 @@ interface ILendingPool { external view returns ( - uint256 totalCollateralETH, - uint256 totalDebtETH, - uint256 availableBorrowsETH, + uint256 totalCollateralBase, + uint256 totalDebtBase, + uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor diff --git a/contracts/interfaces/IStableDebtToken.sol b/contracts/interfaces/IStableDebtToken.sol index e39cf8b5..082a32db 100644 --- a/contracts/interfaces/IStableDebtToken.sol +++ b/contracts/interfaces/IStableDebtToken.sol @@ -69,6 +69,26 @@ interface IStableDebtToken is IInitializableDebtToken { uint256 rate ) external returns (bool); + /** + * @dev implements the credit delegation with ERC712 signature + * @param delegator The delegator of the credit + * @param delegatee The delegatee that can use the credit + * @param value The amount to be delegated + * @param deadline The deadline timestamp, type(uint256).max for max deadline + * @param v Signature param + * @param s Signature param + * @param r Signature param + */ + function permitDelegation( + address delegator, + address delegatee, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + /** * @dev Burns debt of `user` * - The resulting rate is the weighted average between the rate of the new debt diff --git a/contracts/interfaces/IVariableDebtToken.sol b/contracts/interfaces/IVariableDebtToken.sol index d88c25fc..b839f8ee 100644 --- a/contracts/interfaces/IVariableDebtToken.sol +++ b/contracts/interfaces/IVariableDebtToken.sol @@ -36,6 +36,26 @@ interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken { uint256 index ) external returns (bool); + /** + * @dev implements the credit delegation with ERC712 signature + * @param delegator The delegator of the credit + * @param delegatee The delegatee that can use the credit + * @param value The amount to be delegated + * @param deadline The deadline timestamp, type(uint256).max for max deadline + * @param v Signature param + * @param s Signature param + * @param r Signature param + */ + function permitDelegation( + address delegator, + address delegatee, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + /** * @dev Emitted when variable debt is burnt * @param user The user which debt has been burned diff --git a/contracts/misc/AaveOracle.sol b/contracts/misc/AaveOracle.sol index 0cb8e180..bc921468 100644 --- a/contracts/misc/AaveOracle.sol +++ b/contracts/misc/AaveOracle.sol @@ -18,29 +18,34 @@ import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; contract AaveOracle is IPriceOracleGetter, Ownable { using SafeERC20 for IERC20; - event WethSet(address indexed weth); + event BaseCurrencySet(address indexed baseCurrency, uint256 baseCurrencyUnit); event AssetSourceUpdated(address indexed asset, address indexed source); event FallbackOracleUpdated(address indexed fallbackOracle); mapping(address => IChainlinkAggregator) private assetsSources; IPriceOracleGetter private _fallbackOracle; - address public immutable WETH; + address public immutable BASE_CURRENCY; + uint256 public immutable BASE_CURRENCY_UNIT; /// @notice Constructor /// @param assets The addresses of the assets /// @param sources The address of the source of each asset /// @param fallbackOracle The address of the fallback oracle to use if the data of an /// aggregator is not consistent + /// @param baseCurrency the base currency used for the price quotes. If USD is used, base currency is 0x0 + /// @param baseCurrencyUnit the unit of the base currency constructor( address[] memory assets, address[] memory sources, address fallbackOracle, - address weth + address baseCurrency, + uint256 baseCurrencyUnit ) public { _setFallbackOracle(fallbackOracle); _setAssetsSources(assets, sources); - WETH = weth; - emit WethSet(weth); + BASE_CURRENCY = baseCurrency; + BASE_CURRENCY_UNIT = baseCurrencyUnit; + emit BaseCurrencySet(baseCurrency, baseCurrencyUnit); } /// @notice External function called by the Aave governance to set or replace sources of assets @@ -83,8 +88,8 @@ contract AaveOracle is IPriceOracleGetter, Ownable { function getAssetPrice(address asset) public view override returns (uint256) { IChainlinkAggregator source = assetsSources[asset]; - if (asset == WETH) { - return 1 ether; + if (asset == BASE_CURRENCY) { + return BASE_CURRENCY_UNIT; } else if (address(source) == address(0)) { return _fallbackOracle.getAssetPrice(asset); } else { diff --git a/contracts/mocks/upgradeability/MockStableDebtToken.sol b/contracts/mocks/upgradeability/MockStableDebtToken.sol index cbc66664..69f5861f 100644 --- a/contracts/mocks/upgradeability/MockStableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockStableDebtToken.sol @@ -5,6 +5,6 @@ import {StableDebtToken} from '../../protocol/tokenization/StableDebtToken.sol'; contract MockStableDebtToken is StableDebtToken { function getRevision() internal pure override returns (uint256) { - return 0x2; + return 0x3; } } diff --git a/contracts/mocks/upgradeability/MockVariableDebtToken.sol b/contracts/mocks/upgradeability/MockVariableDebtToken.sol index 497682c3..01b9f05f 100644 --- a/contracts/mocks/upgradeability/MockVariableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockVariableDebtToken.sol @@ -5,6 +5,6 @@ import {VariableDebtToken} from '../../protocol/tokenization/VariableDebtToken.s contract MockVariableDebtToken is VariableDebtToken { function getRevision() internal pure override returns (uint256) { - return 0x2; + return 0x3; } } diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 661dff93..b3108e3f 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -473,17 +473,17 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage view override returns ( - uint256 totalCollateralETH, - uint256 totalDebtETH, - uint256 availableBorrowsETH, + uint256 totalCollateralBase, + uint256 totalDebtBase, + uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ) { ( - totalCollateralETH, - totalDebtETH, + totalCollateralBase, + totalDebtBase, ltv, currentLiquidationThreshold, healthFactor, @@ -497,9 +497,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage _addressesProvider.getPriceOracle() ); - availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH( - totalCollateralETH, - totalDebtETH, + availableBorrowsBase = GenericLogic.calculateAvailableBorrows( + totalCollateralBase, + totalDebtBase, ltv ); } diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 36ac923a..0fab3f73 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -112,7 +112,6 @@ library Errors { string public constant RL_ATOKEN_SUPPLY_NOT_ZERO = '88'; string public constant RL_STABLE_DEBT_NOT_ZERO = '89'; string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90'; - string public constant LP_CALLER_NOT_EOA = '91'; string public constant VL_LTV_VALIDATION_FAILED = '93'; string public constant VL_SAME_BLOCK_BORROW_REPAY = '94'; string public constant LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95'; diff --git a/contracts/protocol/libraries/logic/GenericLogic.sol b/contracts/protocol/libraries/logic/GenericLogic.sol index bcb49b24..05bc8034 100644 --- a/contracts/protocol/libraries/logic/GenericLogic.sol +++ b/contracts/protocol/libraries/logic/GenericLogic.sol @@ -32,17 +32,17 @@ library GenericLogic { uint256 assetPrice; uint256 assetUnit; uint256 userBalance; - uint256 userBalanceETH; + uint256 userBalanceInBaseCurrency; uint256 userDebt; uint256 userStableDebt; - uint256 userDebtETH; + uint256 userDebtInBaseCurrency; uint256 decimals; uint256 ltv; uint256 liquidationThreshold; uint256 i; uint256 healthFactor; - uint256 totalCollateralInETH; - uint256 totalDebtInETH; + uint256 totalCollateralInBaseCurrency; + uint256 totalDebtInBaseCurrency; uint256 avgLtv; uint256 avgLiquidationThreshold; uint256 normalizedIncome; @@ -53,14 +53,15 @@ library GenericLogic { /** * @dev Calculates the user data across the reserves. - * this includes the total liquidity/collateral/borrow balances in ETH, + * this includes the total liquidity/collateral/borrow balances in the base currency used by the price feed, * the average Loan To Value, the average Liquidation Ratio, and the Health factor. * @param user The address of the user * @param reservesData Data of all the reserves * @param userConfig The configuration of the user * @param reserves The list of the available reserves * @param oracle The price oracle address - * @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold, the HF and the uncapped avg ltv + * @return The total collateral and total debt of the user in the base currency used by the price feed, + * the avg ltv, liquidation threshold, the HF and the uncapped avg ltv **/ function calculateUserAccountData( address user, @@ -109,13 +110,16 @@ library GenericLogic { vars.userBalance = IScaledBalanceToken(currentReserve.aTokenAddress).scaledBalanceOf(user); vars.userBalance = vars.userBalance.rayMul(vars.normalizedIncome); - vars.userBalanceETH = vars.assetPrice.mul(vars.userBalance).div(vars.assetUnit); - vars.totalCollateralInETH = vars.totalCollateralInETH.add(vars.userBalanceETH); + vars.userBalanceInBaseCurrency = vars.assetPrice.mul(vars.userBalance).div(vars.assetUnit); + vars.totalCollateralInBaseCurrency = vars.totalCollateralInBaseCurrency.add( + vars.userBalanceInBaseCurrency + ); - vars.avgLtv = vars.avgLtv.add(vars.userBalanceETH.mul(vars.ltv)); + vars.avgLtv = vars.avgLtv.add(vars.userBalanceInBaseCurrency.mul(vars.ltv)); vars.hasZeroLtvCollateral = vars.hasZeroLtvCollateral || vars.ltv == 0; + vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add( - vars.userBalanceETH.mul(vars.liquidationThreshold) + vars.userBalanceInBaseCurrency.mul(vars.liquidationThreshold) ); } @@ -129,24 +133,28 @@ library GenericLogic { vars.userDebt = vars.userDebt.rayMul(vars.normalizedDebt); } vars.userDebt = vars.userDebt.add(vars.userStableDebt); - vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit); - vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH); + vars.userDebtInBaseCurrency = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit); + vars.totalDebtInBaseCurrency = vars.totalDebtInBaseCurrency.add( + vars.userDebtInBaseCurrency + ); } } - vars.avgLtv = vars.totalCollateralInETH > 0 ? vars.avgLtv.div(vars.totalCollateralInETH) : 0; - vars.avgLiquidationThreshold = vars.totalCollateralInETH > 0 - ? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH) + vars.avgLtv = vars.totalCollateralInBaseCurrency > 0 + ? vars.avgLtv.div(vars.totalCollateralInBaseCurrency) + : 0; + vars.avgLiquidationThreshold = vars.totalCollateralInBaseCurrency > 0 + ? vars.avgLiquidationThreshold.div(vars.totalCollateralInBaseCurrency) : 0; vars.healthFactor = calculateHealthFactorFromBalances( - vars.totalCollateralInETH, - vars.totalDebtInETH, + vars.totalCollateralInBaseCurrency, + vars.totalDebtInBaseCurrency, vars.avgLiquidationThreshold ); return ( - vars.totalCollateralInETH, - vars.totalDebtInETH, + vars.totalCollateralInBaseCurrency, + vars.totalDebtInBaseCurrency, vars.avgLtv, vars.avgLiquidationThreshold, vars.healthFactor, @@ -156,43 +164,46 @@ library GenericLogic { /** * @dev Calculates the health factor from the corresponding balances - * @param totalCollateralInETH The total collateral in ETH - * @param totalDebtInETH The total debt in ETH + * @param totalCollateralInBaseCurrency The total collateral in the base currency used by the price feed + * @param totalDebtInBaseCurrency The total debt in the base currency used by the price feed * @param liquidationThreshold The avg liquidation threshold * @return The health factor calculated from the balances provided **/ function calculateHealthFactorFromBalances( - uint256 totalCollateralInETH, - uint256 totalDebtInETH, + uint256 totalCollateralInBaseCurrency, + uint256 totalDebtInBaseCurrency, uint256 liquidationThreshold ) internal pure returns (uint256) { - if (totalDebtInETH == 0) return uint256(-1); + if (totalDebtInBaseCurrency == 0) return uint256(-1); - return (totalCollateralInETH.percentMul(liquidationThreshold)).wadDiv(totalDebtInETH); + return + (totalCollateralInBaseCurrency.percentMul(liquidationThreshold)).wadDiv( + totalDebtInBaseCurrency + ); } /** - * @dev Calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the + * @dev Calculates the maximum amount that can be borrowed depending on the available collateral, the total debt and the * average Loan To Value - * @param totalCollateralInETH The total collateral in ETH - * @param totalDebtInETH The total borrow balance + * @param totalCollateralInBaseCurrency The total collateral in the base currency used by the price feed + * @param totalDebtInBaseCurrency The total borrow balance in the base currency used by the price feed * @param ltv The average loan to value - * @return the amount available to borrow in ETH for the user + * @return the amount available to borrow in the base currency of the used by the price feed **/ - function calculateAvailableBorrowsETH( - uint256 totalCollateralInETH, - uint256 totalDebtInETH, + function calculateAvailableBorrows( + uint256 totalCollateralInBaseCurrency, + uint256 totalDebtInBaseCurrency, uint256 ltv ) internal pure returns (uint256) { - uint256 availableBorrowsETH = totalCollateralInETH.percentMul(ltv); + uint256 availableBorrowsInBaseCurrency = totalCollateralInBaseCurrency.percentMul(ltv); - if (availableBorrowsETH < totalDebtInETH) { + if (availableBorrowsInBaseCurrency < totalDebtInBaseCurrency) { return 0; } - availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH); - return availableBorrowsETH; + availableBorrowsInBaseCurrency = availableBorrowsInBaseCurrency.sub(totalDebtInBaseCurrency); + return availableBorrowsInBaseCurrency; } /** diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index bbcc6fb8..70f64e9e 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -218,7 +218,7 @@ library ReserveLogic { ); } - struct MintToTreasuryLocalVars { + struct AccrueToTreasuryLocalVars { uint256 prevTotalStableDebt; uint256 prevTotalVariableDebt; uint256 currTotalVariableDebt; @@ -240,7 +240,7 @@ library ReserveLogic { DataTypes.ReserveData storage reserve, DataTypes.ReserveCache memory reserveCache ) internal { - MintToTreasuryLocalVars memory vars; + AccrueToTreasuryLocalVars memory vars; vars.reserveFactor = reserveCache.reserveConfiguration.getReserveFactorMemory(); diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 4fc4596e..012bf49e 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -92,16 +92,16 @@ library ValidationLogic { struct ValidateBorrowLocalVars { uint256 currentLtv; uint256 currentLiquidationThreshold; - uint256 amountOfCollateralNeededETH; - uint256 userCollateralBalanceETH; - uint256 userBorrowBalanceETH; + uint256 collateralNeededInBaseCurrency; + uint256 userCollateralInBaseCurrency; + uint256 userDebtInBaseCurrency; uint256 availableLiquidity; uint256 healthFactor; uint256 totalDebt; uint256 totalSupplyVariableDebt; uint256 reserveDecimals; uint256 borrowCap; - uint256 amountInETH; + uint256 amountInBaseCurrency; bool isActive; bool isFrozen; bool isPaused; @@ -181,8 +181,8 @@ library ValidationLogic { } ( - vars.userCollateralBalanceETH, - vars.userBorrowBalanceETH, + vars.userCollateralInBaseCurrency, + vars.userDebtInBaseCurrency, vars.currentLtv, vars.currentLiquidationThreshold, vars.healthFactor, @@ -196,23 +196,23 @@ library ValidationLogic { oracle ); - require(vars.userCollateralBalanceETH > 0, Errors.VL_COLLATERAL_BALANCE_IS_0); + require(vars.userCollateralInBaseCurrency > 0, Errors.VL_COLLATERAL_BALANCE_IS_0); require( vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD ); - vars.amountInETH = IPriceOracleGetter(oracle).getAssetPrice(asset); - vars.amountInETH = vars.amountInETH.mul(amount).div(10**vars.reserveDecimals); + vars.amountInBaseCurrency = IPriceOracleGetter(oracle).getAssetPrice(asset); + vars.amountInBaseCurrency = vars.amountInBaseCurrency.mul(amount).div(10**vars.reserveDecimals); //add the current already borrowed amount to the amount requested to calculate the total collateral needed. - vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(vars.amountInETH).percentDiv( + vars.collateralNeededInBaseCurrency = vars.userDebtInBaseCurrency.add(vars.amountInBaseCurrency).percentDiv( vars.currentLtv ); //LTV is calculated in percentage require( - vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH, + vars.collateralNeededInBaseCurrency <= vars.userCollateralInBaseCurrency, Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW ); @@ -355,9 +355,6 @@ library ValidationLogic { IERC20 variableDebtToken, address aTokenAddress ) external view { - // to avoid potential abuses using flashloans, the rebalance stable rate must happen through an EOA - require(!address(msg.sender).isContract(), Errors.LP_CALLER_NOT_EOA); - (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); diff --git a/contracts/protocol/tokenization/StableDebtToken.sol b/contracts/protocol/tokenization/StableDebtToken.sol index 2212e9cf..49ebc9c8 100644 --- a/contracts/protocol/tokenization/StableDebtToken.sol +++ b/contracts/protocol/tokenization/StableDebtToken.sol @@ -18,7 +18,15 @@ import {Errors} from '../libraries/helpers/Errors.sol'; contract StableDebtToken is IStableDebtToken, DebtTokenBase { using WadRayMath for uint256; - uint256 public constant DEBT_TOKEN_REVISION = 0x1; + bytes public constant EIP712_REVISION = bytes('1'); + bytes32 internal constant EIP712_DOMAIN = + keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'); + bytes32 public constant PERMIT_DELEGATION_TYPEHASH = + keccak256( + 'PermitDelegation(address delegator,address delegatee,uint256 value,uint256 nonce,uint256 deadline)' + ); + + uint256 public constant DEBT_TOKEN_REVISION = 0x2; uint256 internal _avgStableRate; mapping(address => uint40) internal _timestamps; @@ -29,6 +37,9 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { address internal _underlyingAsset; IAaveIncentivesController internal _incentivesController; + mapping(address => uint256) public _nonces; + bytes32 public DOMAIN_SEPARATOR; + /** * @dev Initializes the debt token. * @param pool The address of the lending pool where this aToken will be used @@ -47,6 +58,13 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { string memory debtTokenSymbol, bytes calldata params ) public override initializer { + uint256 chainId; + + //solium-disable-next-line + assembly { + chainId := chainid() + } + _setName(debtTokenName); _setSymbol(debtTokenSymbol); _setDecimals(debtTokenDecimals); @@ -55,6 +73,16 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { _underlyingAsset = underlyingAsset; _incentivesController = incentivesController; + DOMAIN_SEPARATOR = keccak256( + abi.encode( + EIP712_DOMAIN, + keccak256(bytes(debtTokenName)), + keccak256(EIP712_REVISION), + chainId, + address(this) + ) + ); + emit Initialized( underlyingAsset, address(pool), @@ -256,6 +284,51 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { emit Transfer(user, address(0), amount); } + /** + * @dev implements the credit delegation with ERC712 signature + * @param delegator The delegator of the credit + * @param delegatee The delegatee that can use the credit + * @param value The amount to be delegated + * @param deadline The deadline timestamp, type(uint256).max for max deadline + * @param v Signature param + * @param s Signature param + * @param r Signature param + */ + function permitDelegation( + address delegator, + address delegatee, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external override { + require(delegator != address(0), 'INVALID_DELEGATOR'); + //solium-disable-next-line + require(block.timestamp <= deadline, 'INVALID_EXPIRATION'); + uint256 currentValidNonce = _nonces[delegator]; + bytes32 digest = + keccak256( + abi.encodePacked( + '\x19\x01', + DOMAIN_SEPARATOR, + keccak256( + abi.encode( + PERMIT_DELEGATION_TYPEHASH, + delegator, + delegatee, + value, + currentValidNonce, + deadline + ) + ) + ) + ); + require(delegator == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE'); + _nonces[delegator] = currentValidNonce.add(1); + _approveDelegation(delegator, delegatee, value); + } + /** * @dev Calculates the increase in balance since the last user interaction * @param user The address of the user for which the interest is being accumulated diff --git a/contracts/protocol/tokenization/VariableDebtToken.sol b/contracts/protocol/tokenization/VariableDebtToken.sol index a7a28176..b6c590d6 100644 --- a/contracts/protocol/tokenization/VariableDebtToken.sol +++ b/contracts/protocol/tokenization/VariableDebtToken.sol @@ -17,12 +17,23 @@ import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesControl contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { using WadRayMath for uint256; - uint256 public constant DEBT_TOKEN_REVISION = 0x1; + bytes public constant EIP712_REVISION = bytes('1'); + bytes32 internal constant EIP712_DOMAIN = + keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'); + bytes32 public constant PERMIT_DELEGATION_TYPEHASH = + keccak256( + 'PermitDelegation(address delegator,address delegatee,uint256 value,uint256 nonce,uint256 deadline)' + ); + + uint256 public constant DEBT_TOKEN_REVISION = 0x2; ILendingPool internal _pool; address internal _underlyingAsset; IAaveIncentivesController internal _incentivesController; + mapping(address => uint256) public _nonces; + bytes32 public DOMAIN_SEPARATOR; + /** * @dev Initializes the debt token. * @param pool The address of the lending pool where this aToken will be used @@ -41,6 +52,13 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { string memory debtTokenSymbol, bytes calldata params ) public override initializer { + uint256 chainId; + + //solium-disable-next-line + assembly { + chainId := chainid() + } + _setName(debtTokenName); _setSymbol(debtTokenSymbol); _setDecimals(debtTokenDecimals); @@ -49,6 +67,16 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { _underlyingAsset = underlyingAsset; _incentivesController = incentivesController; + DOMAIN_SEPARATOR = keccak256( + abi.encode( + EIP712_DOMAIN, + keccak256(bytes(debtTokenName)), + keccak256(EIP712_REVISION), + chainId, + address(this) + ) + ); + emit Initialized( underlyingAsset, address(pool), @@ -135,6 +163,51 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { emit Burn(user, amount, index); } + /** + * @dev implements the credit delegation with ERC712 signature + * @param delegator The delegator of the credit + * @param delegatee The delegatee that can use the credit + * @param value The amount to be delegated + * @param deadline The deadline timestamp, type(uint256).max for max deadline + * @param v Signature param + * @param s Signature param + * @param r Signature param + */ + function permitDelegation( + address delegator, + address delegatee, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external override { + require(delegator != address(0), 'INVALID_DELEGATOR'); + //solium-disable-next-line + require(block.timestamp <= deadline, 'INVALID_EXPIRATION'); + uint256 currentValidNonce = _nonces[delegator]; + bytes32 digest = + keccak256( + abi.encodePacked( + '\x19\x01', + DOMAIN_SEPARATOR, + keccak256( + abi.encode( + PERMIT_DELEGATION_TYPEHASH, + delegator, + delegatee, + value, + currentValidNonce, + deadline + ) + ) + ) + ); + require(delegator == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE'); + _nonces[delegator] = currentValidNonce.add(1); + _approveDelegation(delegator, delegatee, value); + } + /** * @dev Returns the principal debt balance of the user from * @return The debt balance of the user since the last burn/mint action diff --git a/contracts/protocol/tokenization/base/DebtTokenBase.sol b/contracts/protocol/tokenization/base/DebtTokenBase.sol index 4d75bc2f..11f43916 100644 --- a/contracts/protocol/tokenization/base/DebtTokenBase.sol +++ b/contracts/protocol/tokenization/base/DebtTokenBase.sol @@ -38,8 +38,7 @@ abstract contract DebtTokenBase is * force a delegator HF to go below 1) **/ function approveDelegation(address delegatee, uint256 amount) external override { - _borrowAllowances[_msgSender()][delegatee] = amount; - emit BorrowAllowanceDelegated(_msgSender(), delegatee, _getUnderlyingAssetAddress(), amount); + _approveDelegation(_msgSender(), delegatee, amount); } /** @@ -118,6 +117,15 @@ abstract contract DebtTokenBase is revert('ALLOWANCE_NOT_SUPPORTED'); } + function _approveDelegation( + address delegator, + address delegatee, + uint256 amount + ) internal { + _borrowAllowances[delegator][delegatee] = amount; + emit BorrowAllowanceDelegated(delegator, delegatee, _getUnderlyingAssetAddress(), amount); + } + function _decreaseBorrowAllowance( address delegator, address delegatee, diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index 05f79206..f1ad477e 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -1,4 +1,4 @@ -import { Contract } from 'ethers'; +import { BigNumberish, Contract } from 'ethers'; import { DRE } from './misc-utils'; import { tEthereumAddress, @@ -188,8 +188,8 @@ export const deployAaveLibraries = async ( return { ['__$de8c0cf1a7d7c36c802af9a64fb9d86036$__']: validationLogic.address, ['__$22cd43a9dda9ce44e9b92ba393b88fb9ac$__']: reserveLogic.address, - ["__$52a8a86ab43135662ff256bbc95497e8e3$__"]: genericLogic.address, - } + ['__$52a8a86ab43135662ff256bbc95497e8e3$__']: genericLogic.address, + }; }; export const deployLendingPool = async (verify?: boolean) => { @@ -224,7 +224,7 @@ export const deployMockAggregator = async (price: tStringTokenSmallUnits, verify ); export const deployAaveOracle = async ( - args: [tEthereumAddress[], tEthereumAddress[], tEthereumAddress, tEthereumAddress], + args: [tEthereumAddress[], tEthereumAddress[], tEthereumAddress, tEthereumAddress, string], verify?: boolean ) => withSaveAndVerify( diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index cb7c016e..1c43a23a 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -142,14 +142,8 @@ export const linkBytecode = (artifact: BuidlerArtifact | Artifact, libraries: an }; export const getParamPerNetwork = (param: iParamsPerNetwork, network: eNetwork) => { - const { - main, - ropsten, - kovan, - coverage, - buidlerevm, - tenderlyMain, - } = param as iEthereumParamsPerNetwork; + const { main, ropsten, kovan, coverage, buidlerevm, tenderlyMain } = + param as iEthereumParamsPerNetwork; const { matic, mumbai } = param as iPolygonParamsPerNetwork; const { xdai } = param as iXDaiParamsPerNetwork; if (process.env.FORK) { @@ -327,6 +321,48 @@ export const buildFlashLiquidationAdapterParams = ( ); }; +export const buildPermitDelegationParams = ( + chainId: number, + token: tEthereumAddress, + revision: string, + tokenName: string, + delegator: tEthereumAddress, + delegatee: tEthereumAddress, + nonce: number, + deadline: string, + value: tStringTokenSmallUnits +) => ({ + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, + ], + PermitDelegation: [ + { name: 'delegator', type: 'address' }, + { name: 'delegatee', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + primaryType: 'PermitDelegation' as const, + domain: { + name: tokenName, + version: revision, + chainId: chainId, + verifyingContract: token, + }, + message: { + delegator, + delegatee, + value, + nonce, + deadline, + }, +}); + export const verifyContract = async ( id: string, instance: Contract, diff --git a/helpers/types.ts b/helpers/types.ts index 68d1ac23..17d1f8af 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -187,7 +187,6 @@ export enum ProtocolErrors { RL_ATOKEN_SUPPLY_NOT_ZERO = '88', RL_STABLE_DEBT_NOT_ZERO = '89', RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90', - LP_CALLER_NOT_EOA = '91', VL_LTV_VALIDATION_FAILED = '93', VL_SAME_BLOCK_BORROW_REPAY = '94', LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95', diff --git a/specs/harness/LendingPoolHarnessForVariableDebtToken.sol b/specs/harness/LendingPoolHarnessForVariableDebtToken.sol index e013885c..6ad68411 100644 --- a/specs/harness/LendingPoolHarnessForVariableDebtToken.sol +++ b/specs/harness/LendingPoolHarnessForVariableDebtToken.sol @@ -100,9 +100,9 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool { view override returns ( - uint256 totalCollateralETH, - uint256 totalDebtETH, - uint256 availableBorrowsETH, + uint256 totalCollateralBase, + uint256 totalDebtBase, + uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor diff --git a/tasks/dev/4_oracles.ts b/tasks/dev/4_oracles.ts index 23c24133..23be80f2 100644 --- a/tasks/dev/4_oracles.ts +++ b/tasks/dev/4_oracles.ts @@ -18,6 +18,7 @@ import { getLendingPoolAddressesProvider, getPairsTokenAggregator, } from '../../helpers/contracts-getters'; +import { ethers } from 'ethers'; task('dev:deploy-oracles', 'Deploy oracles for dev enviroment') .addFlag('verify', 'Verify contracts at Etherscan') @@ -58,7 +59,13 @@ task('dev:deploy-oracles', 'Deploy oracles for dev enviroment') ); await deployAaveOracle( - [tokens, aggregators, fallbackOracle.address, await getWethAddress(poolConfig)], + [ + tokens, + aggregators, + fallbackOracle.address, + await getWethAddress(poolConfig), + ethers.constants.WeiPerEther.toString(), + ], verify ); await waitForTx(await addressesProvider.setPriceOracle(fallbackOracle.address)); diff --git a/tasks/full/3_oracles.ts b/tasks/full/3_oracles.ts index df06f34c..dfc6f247 100644 --- a/tasks/full/3_oracles.ts +++ b/tasks/full/3_oracles.ts @@ -18,6 +18,7 @@ import { getPairsTokenAggregator, } from '../../helpers/contracts-getters'; import { AaveOracle, LendingRateOracle } from '../../types'; +import { ethers } from 'ethers'; task('full:deploy-oracles', 'Deploy oracles for dev enviroment') .addFlag('verify', 'Verify contracts at Etherscan') @@ -55,7 +56,13 @@ task('full:deploy-oracles', 'Deploy oracles for dev enviroment') aaveOracle = await await getAaveOracle(aaveOracleAddress); } else { aaveOracle = await deployAaveOracle( - [tokens, aggregators, fallbackOracleAddress, await getWethAddress(poolConfig)], + [ + tokens, + aggregators, + fallbackOracleAddress, + await getWethAddress(poolConfig), + ethers.constants.WeiPerEther.toString(), + ], verify ); await waitForTx(await aaveOracle.setAssetSources(tokens, aggregators)); diff --git a/test-suites/test-aave/__setup.spec.ts b/test-suites/test-aave/__setup.spec.ts index 2ef2f3a0..ba58252e 100644 --- a/test-suites/test-aave/__setup.spec.ts +++ b/test-suites/test-aave/__setup.spec.ts @@ -30,7 +30,7 @@ import { authorizeWETHGateway, } from '../../helpers/contracts-deployments'; import { eEthereumNetwork } from '../../helpers/types'; -import { Signer } from 'ethers'; +import { ethers, Signer } from 'ethers'; import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../../helpers/types'; import { MintableERC20 } from '../../types/MintableERC20'; import { @@ -215,7 +215,13 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const [tokens, aggregators] = getPairsTokenAggregator(allTokenAddresses, allAggregatorsAddresses); - await deployAaveOracle([tokens, aggregators, fallbackOracle.address, mockTokens.WETH.address]); + await deployAaveOracle([ + tokens, + aggregators, + fallbackOracle.address, + mockTokens.WETH.address, + ethers.constants.WeiPerEther.toString(), + ]); await waitForTx(await addressesProvider.setPriceOracle(fallbackOracle.address)); const lendingRateOracle = await deployLendingRateOracle(); @@ -243,12 +249,8 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const config = loadPoolConfig(ConfigNames.Aave); - const { - ATokenNamePrefix, - StableDebtTokenNamePrefix, - VariableDebtTokenNamePrefix, - SymbolPrefix, - } = config; + const { ATokenNamePrefix, StableDebtTokenNamePrefix, VariableDebtTokenNamePrefix, SymbolPrefix } = + config; const treasuryAddress = await getTreasuryAddress(config); await initReservesByHelper( diff --git a/test-suites/test-aave/delegation-permit.spec.ts b/test-suites/test-aave/delegation-permit.spec.ts new file mode 100644 index 00000000..6f9b9754 --- /dev/null +++ b/test-suites/test-aave/delegation-permit.spec.ts @@ -0,0 +1,170 @@ +import { MAX_UINT_AMOUNT, ZERO_ADDRESS } from '../../helpers/constants'; +import { BUIDLEREVM_CHAINID } from '../../helpers/buidler-constants'; +import { + buildPermitDelegationParams, + buildPermitParams, + convertToCurrencyDecimals, + getSignatureFromTypedData, +} from '../../helpers/contracts-helpers'; +import { expect } from 'chai'; +import { BigNumber, 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('Permit Delegation', (testEnv: TestEnv) => { + const mintedAmount = '1000'; + let daiMintedAmount: BigNumber; + let wethMintedAmount: BigNumber; + + it('Checks the domain separator', async () => { + const { variableDebtDai, stableDebtDai, weth, dai } = testEnv; + const variableSeparator = await variableDebtDai.DOMAIN_SEPARATOR(); + const stableSeparator = await stableDebtDai.DOMAIN_SEPARATOR(); + + const variableDomain = { + name: await variableDebtDai.name(), + version: '1', + chainId: DRE.network.config.chainId, + verifyingContract: variableDebtDai.address, + }; + const stableDomain = { + name: await stableDebtDai.name(), + version: '1', + chainId: DRE.network.config.chainId, + verifyingContract: stableDebtDai.address, + }; + const variableDomainSeparator = _TypedDataEncoder.hashDomain(variableDomain); + const stableDomainSeparator = _TypedDataEncoder.hashDomain(stableDomain); + + expect(variableSeparator).to.be.equal( + variableDomainSeparator, + 'Invalid variable domain separator' + ); + expect(stableSeparator).to.be.equal(stableDomainSeparator, 'Invalid stable domain separator'); + }); + + it('Setup the lending pool', async () => { + const { + pool, + weth, + dai, + deployer: user1, + users: [user2, user3], + } = testEnv; + daiMintedAmount = await convertToCurrencyDecimals(dai.address, mintedAmount); + wethMintedAmount = await convertToCurrencyDecimals(weth.address, mintedAmount); + await dai.mint(daiMintedAmount); + await dai.approve(pool.address, daiMintedAmount); + await pool.deposit(dai.address, daiMintedAmount, user1.address, 0); + await weth.connect(user2.signer).mint(wethMintedAmount); + await weth.connect(user2.signer).approve(pool.address, wethMintedAmount); + await pool.connect(user2.signer).deposit(weth.address, wethMintedAmount, user2.address, 0); + }); + it('User 3 borrows variable interest dai on behalf of user 2 via permit', async () => { + const { + pool, + variableDebtDai, + stableDebtDai, + weth, + dai, + deployer: user1, + users: [user2, user3], + } = testEnv; + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const expiration = MAX_UINT_AMOUNT; + const nonce = (await variableDebtDai._nonces(user2.address)).toNumber(); + const permitAmount = daiMintedAmount.div(3); + const msgParams = buildPermitDelegationParams( + chainId, + variableDebtDai.address, + '1', + await variableDebtDai.name(), + user2.address, + user3.address, + nonce, + expiration, + permitAmount.toString() + ); + + const user2PrivateKey = require('../../test-wallets.js').accounts[1].secretKey; + if (!user2PrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + expect( + (await variableDebtDai.borrowAllowance(user2.address, user3.address)).toString() + ).to.be.equal('0'); + + const { v, r, s } = getSignatureFromTypedData(user2PrivateKey, msgParams); + + await variableDebtDai + .connect(user1.signer) + .permitDelegation(user2.address, user3.address, permitAmount, expiration, v, r, s); + + expect( + (await variableDebtDai.borrowAllowance(user2.address, user3.address)).toString() + ).to.be.equal(permitAmount); + + await pool.connect(user3.signer).borrow(dai.address, permitAmount, 2, 0, user2.address); + expect( + (await variableDebtDai.borrowAllowance(user2.address, user3.address)).toString() + ).to.be.equal('0'); + }); + it('User 3 borrows stable interest dai on behalf of user 2 via permit', async () => { + const { + pool, + variableDebtDai, + stableDebtDai, + weth, + dai, + deployer: user1, + users: [user2, user3], + } = testEnv; + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const expiration = MAX_UINT_AMOUNT; + const nonce = (await stableDebtDai._nonces(user2.address)).toNumber(); + const permitAmount = daiMintedAmount.div(3); + const msgParams = buildPermitDelegationParams( + chainId, + stableDebtDai.address, + '1', + await stableDebtDai.name(), + user2.address, + user3.address, + nonce, + expiration, + permitAmount.toString() + ); + + const user2PrivateKey = require('../../test-wallets.js').accounts[1].secretKey; + if (!user2PrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + expect( + (await stableDebtDai.borrowAllowance(user2.address, user3.address)).toString() + ).to.be.equal('0'); + + const { v, r, s } = getSignatureFromTypedData(user2PrivateKey, msgParams); + + await stableDebtDai + .connect(user1.signer) + .permitDelegation(user2.address, user3.address, permitAmount, expiration, v, r, s); + + expect( + (await stableDebtDai.borrowAllowance(user2.address, user3.address)).toString() + ).to.be.equal(permitAmount); + + await pool + .connect(user3.signer) + .borrow(dai.address, daiMintedAmount.div(10), 1, 0, user2.address); + + expect( + (await stableDebtDai.borrowAllowance(user2.address, user3.address)).toString() + ).to.be.equal(permitAmount.sub(daiMintedAmount.div(10))); + }); +}); diff --git a/test-suites/test-aave/helpers/make-suite.ts b/test-suites/test-aave/helpers/make-suite.ts index 130635f1..15150cd1 100644 --- a/test-suites/test-aave/helpers/make-suite.ts +++ b/test-suites/test-aave/helpers/make-suite.ts @@ -14,6 +14,8 @@ import { getUniswapLiquiditySwapAdapter, getUniswapRepayAdapter, getFlashLiquidationAdapter, + getVariableDebtToken, + getStableDebtToken, } from '../../../helpers/contracts-getters'; import { eEthereumNetwork, eNetwork, tEthereumAddress } from '../../../helpers/types'; import { LendingPool } from '../../../types/LendingPool'; @@ -37,7 +39,7 @@ import { WETH9Mocked } from '../../../types/WETH9Mocked'; import { WETHGateway } from '../../../types/WETHGateway'; import { solidity } from 'ethereum-waffle'; import { AaveConfig } from '../../../markets/aave'; -import { FlashLiquidationAdapter } from '../../../types'; +import { FlashLiquidationAdapter, StableDebtToken, VariableDebtToken } from '../../../types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { usingTenderly } from '../../../helpers/tenderly-utils'; @@ -63,6 +65,8 @@ export interface TestEnv { aWETH: AToken; dai: MintableERC20; aDai: AToken; + variableDebtDai: VariableDebtToken; + stableDebtDai: StableDebtToken; aUsdc: AToken; usdc: MintableERC20; aave: MintableERC20; @@ -93,6 +97,8 @@ const testEnv: TestEnv = { aWETH: {} as AToken, dai: {} as MintableERC20, aDai: {} as AToken, + variableDebtDai: {} as VariableDebtToken, + stableDebtDai: {} as StableDebtToken, aUsdc: {} as AToken, usdc: {} as MintableERC20, aave: {} as MintableERC20, @@ -147,6 +153,10 @@ export async function initializeMakeSuite() { const reservesTokens = await testEnv.helpersContract.getAllReservesTokens(); const daiAddress = reservesTokens.find((token) => token.symbol === 'DAI')?.tokenAddress; + const { + variableDebtTokenAddress: variableDebtDaiAddress, + stableDebtTokenAddress: stableDebtDaiAddress, + } = await testEnv.helpersContract.getReserveTokensAddresses(daiAddress || ''); const usdcAddress = reservesTokens.find((token) => token.symbol === 'USDC')?.tokenAddress; const aaveAddress = reservesTokens.find((token) => token.symbol === 'AAVE')?.tokenAddress; const wethAddress = reservesTokens.find((token) => token.symbol === 'WETH')?.tokenAddress; @@ -159,6 +169,8 @@ export async function initializeMakeSuite() { } testEnv.aDai = await getAToken(aDaiAddress); + testEnv.variableDebtDai = await getVariableDebtToken(variableDebtDaiAddress); + testEnv.stableDebtDai = await getStableDebtToken(stableDebtDaiAddress); testEnv.aUsdc = await getAToken(aUsdcAddress); testEnv.aWETH = await getAToken(aWEthAddress); diff --git a/test-suites/test-aave/liquidation-atoken.spec.ts b/test-suites/test-aave/liquidation-atoken.spec.ts index 00ad07be..e5ca0e3b 100644 --- a/test-suites/test-aave/liquidation-atoken.spec.ts +++ b/test-suites/test-aave/liquidation-atoken.spec.ts @@ -55,7 +55,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => const amountDAIToBorrow = await convertToCurrencyDecimals( dai.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(daiPrice.toString()) .multipliedBy(0.95) .toFixed(0) @@ -269,7 +269,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => const amountUSDCToBorrow = await convertToCurrencyDecimals( usdc.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(usdcPrice.toString()) .multipliedBy(0.9502) .toFixed(0) diff --git a/test-suites/test-aave/liquidation-underlying.spec.ts b/test-suites/test-aave/liquidation-underlying.spec.ts index b0951ce3..72143ba2 100644 --- a/test-suites/test-aave/liquidation-underlying.spec.ts +++ b/test-suites/test-aave/liquidation-underlying.spec.ts @@ -83,7 +83,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', const amountDAIToBorrow = await convertToCurrencyDecimals( dai.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(daiPrice.toString()) .multipliedBy(0.95) .toFixed(0) @@ -267,7 +267,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', const amountUSDCToBorrow = await convertToCurrencyDecimals( usdc.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(usdcPrice.toString()) .multipliedBy(0.9502) .toFixed(0) diff --git a/test-suites/test-aave/pausable-functions.spec.ts b/test-suites/test-aave/pausable-functions.spec.ts index 54b318e7..ae555736 100644 --- a/test-suites/test-aave/pausable-functions.spec.ts +++ b/test-suites/test-aave/pausable-functions.spec.ts @@ -224,7 +224,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => { const amountUSDCToBorrow = await convertToCurrencyDecimals( usdc.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(usdcPrice.toString()) .multipliedBy(0.9502) .toFixed(0) diff --git a/test-suites/test-aave/reserve-pause.spec.ts b/test-suites/test-aave/reserve-pause.spec.ts index 771fd0fd..f489a621 100644 --- a/test-suites/test-aave/reserve-pause.spec.ts +++ b/test-suites/test-aave/reserve-pause.spec.ts @@ -224,7 +224,7 @@ makeSuite('Pause One Reserve', (testEnv: TestEnv) => { const amountUSDCToBorrow = await convertToCurrencyDecimals( usdc.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(usdcPrice.toString()) .multipliedBy(0.9502) .toFixed(0) diff --git a/test-suites/test-aave/uniswapAdapters.flashLiquidation.spec.ts b/test-suites/test-aave/uniswapAdapters.flashLiquidation.spec.ts index cc161b4b..6437605e 100644 --- a/test-suites/test-aave/uniswapAdapters.flashLiquidation.spec.ts +++ b/test-suites/test-aave/uniswapAdapters.flashLiquidation.spec.ts @@ -61,7 +61,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const amountDAIToBorrow = await convertToCurrencyDecimals( dai.address, - new BigNumber(userGlobalDataBefore.availableBorrowsETH.toString()) + new BigNumber(userGlobalDataBefore.availableBorrowsBase.toString()) .div(daiPrice.toString()) .multipliedBy(0.95) .toFixed(0) @@ -128,7 +128,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const amountDAIToBorrow = await convertToCurrencyDecimals( dai.address, - new BigNumber(userGlobalDataBefore.availableBorrowsETH.toString()) + new BigNumber(userGlobalDataBefore.availableBorrowsBase.toString()) .div(daiPrice.toString()) .multipliedBy(0.8) .toFixed(0) @@ -141,7 +141,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userGlobalDataBefore2 = await pool.getUserAccountData(borrower.address); - const amountWETHToBorrow = new BigNumber(userGlobalDataBefore2.availableBorrowsETH.toString()) + const amountWETHToBorrow = new BigNumber(userGlobalDataBefore2.availableBorrowsBase.toString()) .multipliedBy(0.8) .toFixed(0); diff --git a/test-suites/test-amm/__setup.spec.ts b/test-suites/test-amm/__setup.spec.ts index 0fae3acd..db75799f 100644 --- a/test-suites/test-amm/__setup.spec.ts +++ b/test-suites/test-amm/__setup.spec.ts @@ -29,7 +29,7 @@ import { deployFlashLiquidationAdapter, authorizeWETHGateway, } from '../../helpers/contracts-deployments'; -import { Signer } from 'ethers'; +import { ethers, Signer } from 'ethers'; import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../../helpers/types'; import { MintableERC20 } from '../../types/MintableERC20'; import { @@ -212,7 +212,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const [tokens, aggregators] = getPairsTokenAggregator(allTokenAddresses, allAggregatorsAddresses); - await deployAaveOracle([tokens, aggregators, fallbackOracle.address, mockTokens.WETH.address]); + await deployAaveOracle([tokens, aggregators, fallbackOracle.address, mockTokens.WETH.address, ethers.constants.WeiPerEther.toString()]); await waitForTx(await addressesProvider.setPriceOracle(fallbackOracle.address)); const lendingRateOracle = await deployLendingRateOracle(); diff --git a/test-suites/test-amm/liquidation-atoken.spec.ts b/test-suites/test-amm/liquidation-atoken.spec.ts index 775fe769..d229bf29 100644 --- a/test-suites/test-amm/liquidation-atoken.spec.ts +++ b/test-suites/test-amm/liquidation-atoken.spec.ts @@ -55,7 +55,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => const amountDAIToBorrow = await convertToCurrencyDecimals( dai.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(daiPrice.toString()) .multipliedBy(0.95) .toFixed(0) @@ -269,7 +269,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => const amountUSDCToBorrow = await convertToCurrencyDecimals( usdc.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(usdcPrice.toString()) .multipliedBy(0.9502) .toFixed(0) diff --git a/test-suites/test-amm/liquidation-underlying.spec.ts b/test-suites/test-amm/liquidation-underlying.spec.ts index e44a2d80..64014ee0 100644 --- a/test-suites/test-amm/liquidation-underlying.spec.ts +++ b/test-suites/test-amm/liquidation-underlying.spec.ts @@ -83,7 +83,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', const amountDAIToBorrow = await convertToCurrencyDecimals( dai.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(daiPrice.toString()) .multipliedBy(0.95) .toFixed(0) @@ -267,7 +267,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', const amountUSDCToBorrow = await convertToCurrencyDecimals( usdc.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(usdcPrice.toString()) .multipliedBy(0.9502) .toFixed(0) diff --git a/test-suites/test-amm/pausable-functions.spec.ts b/test-suites/test-amm/pausable-functions.spec.ts index e5bd4637..1d6d21dd 100644 --- a/test-suites/test-amm/pausable-functions.spec.ts +++ b/test-suites/test-amm/pausable-functions.spec.ts @@ -224,7 +224,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => { const amountUSDCToBorrow = await convertToCurrencyDecimals( usdc.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) + new BigNumber(userGlobalData.availableBorrowsBase.toString()) .div(usdcPrice.toString()) .multipliedBy(0.9502) .toFixed(0)