Merge branch 'protocol-2.5' into feat/gas-optimization-3

This commit is contained in:
The3D 2021-05-07 16:42:52 +02:00
commit 550a4abe7d
21 changed files with 1202 additions and 233 deletions

View File

@ -1,4 +1,4 @@
name: Aave Actions name: Build
on: on:
push: push:

View File

@ -1,4 +1,5 @@
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![Build pass](https://github.com/AAVE/protocol-v2/actions/workflows/node.js.yml/badge.svg)](https://github.com/aave/protocol-v2/actions/workflows/node.js.yml)
``` ```
.///. .///. //. .// `/////////////- .///. .///. //. .// `/////////////-
`++:++` .++:++` :++` `++: `++:......---.` `++:++` .++:++` :++` `++: `++:......---.`

View File

@ -3,9 +3,126 @@ pragma solidity 0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
interface IAaveIncentivesController { interface IAaveIncentivesController {
event RewardsAccrued(address indexed user, uint256 amount);
event RewardsClaimed(address indexed user, address indexed to, uint256 amount);
event RewardsClaimed(
address indexed user,
address indexed to,
address indexed claimer,
uint256 amount
);
event ClaimerSet(address indexed user, address indexed claimer);
/*
* @dev Returns the configuration of the distribution for a certain asset
* @param asset The address of the reference asset of the distribution
* @return The asset index, the emission per second and the last updated timestamp
**/
function getAssetData(address asset)
external
view
returns (
uint256,
uint256,
uint256
);
/**
* @dev Whitelists an address to claim the rewards on behalf of another address
* @param user The address of the user
* @param claimer The address of the claimer
*/
function setClaimer(address user, address claimer) external;
/**
* @dev Returns the whitelisted claimer for a certain address (0x0 if not set)
* @param user The address of the user
* @return The claimer address
*/
function getClaimer(address user) external view returns (address);
/**
* @dev Configure assets for a certain rewards emission
* @param assets The assets to incentivize
* @param emissionsPerSecond The emission for each asset
*/
function configureAssets(address[] calldata assets, uint256[] calldata emissionsPerSecond)
external;
/**
* @dev Called by the corresponding asset on any update that affects the rewards distribution
* @param asset The address of the user
* @param userBalance The balance of the user of the asset in the lending pool
* @param totalSupply The total supply of the asset in the lending pool
**/
function handleAction( function handleAction(
address user, address asset,
uint256 userBalance, uint256 userBalance,
uint256 totalSupply uint256 totalSupply
) external; ) external;
/**
* @dev Returns the total of rewards of an user, already accrued + not yet accrued
* @param user The address of the user
* @return The rewards
**/
function getRewardsBalance(address[] calldata assets, address user)
external
view
returns (uint256);
/**
* @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards
* @param amount Amount of rewards to claim
* @param to Address that will be receiving the rewards
* @return Rewards claimed
**/
function claimRewards(
address[] calldata assets,
uint256 amount,
address to
) external returns (uint256);
/**
* @dev Claims reward for an user on behalf, on all the assets of the lending pool, accumulating the pending rewards. The caller must
* be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
* @param amount Amount of rewards to claim
* @param user Address to check and claim rewards
* @param to Address that will be receiving the rewards
* @return Rewards claimed
**/
function claimRewardsOnBehalf(
address[] calldata assets,
uint256 amount,
address user,
address to
) external returns (uint256);
/**
* @dev returns the unclaimed rewards of the user
* @param user the address of the user
* @return the unclaimed user rewards
*/
function getUserUnclaimedRewards(address user) external view returns (uint256);
/**
* @dev returns the unclaimed rewards of the user
* @param user the address of the user
* @param asset The asset to incentivize
* @return the user index for the asset
*/
function getUserAssetData(address user, address asset) external view returns (uint256);
/**
* @dev for backward compatibility with previous implementation of the Incentives controller
*/
function REWARD_TOKEN() external view returns (address);
/**
* @dev for backward compatibility with previous implementation of the Incentives controller
*/
function PRECISION() external view returns (uint8);
} }

View File

@ -254,6 +254,57 @@ interface ILendingPool {
address onBehalfOf address onBehalfOf
) external returns (uint256); ) external returns (uint256);
/**
* @notice Deposit with transfer approval of asset to be deposited done via permit function
* see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
* @param asset The address of the underlying asset to deposit
* @param amount The amount to be deposited
* @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
* is a different wallet
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
* @param permitV V parameter of ERC712 permit sig
* @param permitR R parameter of ERC712 permit sig
* @param permitS S parameter of ERC712 permit sig
**/
function depositWithPermit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) external;
/**
* @notice Repay with transfer approval of asset to be repaid done via permit function
* see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
* @param asset The address of the borrowed underlying asset previously borrowed
* @param amount The amount to repay
* - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
* @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
* @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
* user calling the function if he wants to reduce/remove his own debt, or the address of any other
* other borrower whose debt should be removed
* @param permitV V parameter of ERC712 permit sig
* @param permitR R parameter of ERC712 permit sig
* @param permitS S parameter of ERC712 permit sig
* @return The final amount repaid
**/
function repayWithPermit(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) external returns (uint256);
/** /**
* @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
* @param asset The address of the underlying asset borrowed * @param asset The address of the underlying asset borrowed

View File

@ -4,6 +4,7 @@ pragma experimental ABIEncoderV2;
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol';
import {IUiPoolDataProvider} from './interfaces/IUiPoolDataProvider.sol'; import {IUiPoolDataProvider} from './interfaces/IUiPoolDataProvider.sol';
import {ILendingPool} from '../interfaces/ILendingPool.sol'; import {ILendingPool} from '../interfaces/ILendingPool.sol';
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
@ -24,6 +25,13 @@ contract UiPoolDataProvider is IUiPoolDataProvider {
using UserConfiguration for DataTypes.UserConfigurationMap; using UserConfiguration for DataTypes.UserConfigurationMap;
address public constant MOCK_USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; address public constant MOCK_USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96;
IAaveIncentivesController public immutable incentivesController;
IPriceOracleGetter public immutable oracle;
constructor(IAaveIncentivesController _incentivesController, IPriceOracleGetter _oracle) public {
incentivesController = _incentivesController;
oracle = _oracle;
}
function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy) function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy)
internal internal
@ -50,11 +58,11 @@ contract UiPoolDataProvider is IUiPoolDataProvider {
returns ( returns (
AggregatedReserveData[] memory, AggregatedReserveData[] memory,
UserReserveData[] memory, UserReserveData[] memory,
uint256,
uint256 uint256
) )
{ {
ILendingPool lendingPool = ILendingPool(provider.getLendingPool()); ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
IPriceOracleGetter oracle = IPriceOracleGetter(provider.getPriceOracle());
address[] memory reserves = lendingPool.getReservesList(); address[] memory reserves = lendingPool.getReservesList();
DataTypes.UserConfigurationMap memory userConfig = lendingPool.getUserConfiguration(user); DataTypes.UserConfigurationMap memory userConfig = lendingPool.getUserConfiguration(user);
@ -122,7 +130,43 @@ contract UiPoolDataProvider is IUiPoolDataProvider {
DefaultReserveInterestRateStrategy(reserveData.interestRateStrategyAddress) DefaultReserveInterestRateStrategy(reserveData.interestRateStrategyAddress)
); );
// incentives
if (address(0) != address(incentivesController)) {
(
reserveData.aEmissionPerSecond,
reserveData.aIncentivesLastUpdateTimestamp,
reserveData.aTokenIncentivesIndex
) = incentivesController.getAssetData(reserveData.aTokenAddress);
(
reserveData.sEmissionPerSecond,
reserveData.sIncentivesLastUpdateTimestamp,
reserveData.sTokenIncentivesIndex
) = incentivesController.getAssetData(reserveData.stableDebtTokenAddress);
(
reserveData.vEmissionPerSecond,
reserveData.vIncentivesLastUpdateTimestamp,
reserveData.vTokenIncentivesIndex
) = incentivesController.getAssetData(reserveData.variableDebtTokenAddress);
}
if (user != address(0)) { if (user != address(0)) {
// incentives
if (address(0) != address(incentivesController)) {
userReservesData[i].aTokenincentivesUserIndex = incentivesController.getUserAssetData(
user,
reserveData.aTokenAddress
);
userReservesData[i].vTokenincentivesUserIndex = incentivesController.getUserAssetData(
user,
reserveData.variableDebtTokenAddress
);
userReservesData[i].sTokenincentivesUserIndex = incentivesController.getUserAssetData(
user,
reserveData.stableDebtTokenAddress
);
}
// user reserve data // user reserve data
userReservesData[i].underlyingAsset = reserveData.underlyingAsset; userReservesData[i].underlyingAsset = reserveData.underlyingAsset;
userReservesData[i].scaledATokenBalance = IAToken(reserveData.aTokenAddress) userReservesData[i].scaledATokenBalance = IAToken(reserveData.aTokenAddress)
@ -155,6 +199,12 @@ contract UiPoolDataProvider is IUiPoolDataProvider {
} }
} }
} }
return (reservesData, userReservesData, oracle.getAssetPrice(MOCK_USD_ADDRESS));
return (
reservesData,
userReservesData,
oracle.getAssetPrice(MOCK_USD_ADDRESS),
incentivesController.getUserUnclaimedRewards(user)
);
} }
} }

View File

@ -131,6 +131,41 @@ contract WETHGateway is IWETHGateway, Ownable {
_safeTransferETH(msg.sender, amount); _safeTransferETH(msg.sender, amount);
} }
/**
* @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
* @param deadline validity deadline of permit and so depositWithPermit signature
* @param permitV V parameter of ERC712 permit sig
* @param permitR R parameter of ERC712 permit sig
* @param permitS S parameter of ERC712 permit sig
*/
function withdrawETHWithPermit(
address lendingPool,
uint256 amount,
address to,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) external override {
IAToken aWETH = IAToken(ILendingPool(lendingPool).getReserveData(address(WETH)).aTokenAddress);
uint256 userBalance = aWETH.balanceOf(msg.sender);
uint256 amountToWithdraw = amount;
// if amount is equal to uint(-1), the user wants to redeem everything
if (amount == type(uint256).max) {
amountToWithdraw = userBalance;
}
// chosing to permit `amount`and not `amountToWithdraw`, easier for frontends, intregrators.
aWETH.permit(msg.sender, address(this), amount, deadline, permitV, permitR, permitS);
aWETH.transferFrom(msg.sender, address(this), amountToWithdraw);
ILendingPool(lendingPool).withdraw(address(WETH), amountToWithdraw, address(this));
WETH.withdraw(amountToWithdraw);
_safeTransferETH(to, amountToWithdraw);
}
/** /**
* @dev transfer ETH to an address, revert if it fails. * @dev transfer ETH to an address, revert if it fails.
* @param to recipient of the transfer * @param to recipient of the transfer

View File

@ -3,6 +3,7 @@ pragma solidity 0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
interface IUiPoolDataProvider { interface IUiPoolDataProvider {
struct AggregatedReserveData { struct AggregatedReserveData {
@ -41,12 +42,17 @@ interface IUiPoolDataProvider {
uint256 variableRateSlope2; uint256 variableRateSlope2;
uint256 stableRateSlope1; uint256 stableRateSlope1;
uint256 stableRateSlope2; uint256 stableRateSlope2;
// incentives
uint256 aEmissionPerSecond;
uint256 vEmissionPerSecond;
uint256 sEmissionPerSecond;
uint256 aIncentivesLastUpdateTimestamp;
uint256 vIncentivesLastUpdateTimestamp;
uint256 sIncentivesLastUpdateTimestamp;
uint256 aTokenIncentivesIndex;
uint256 vTokenIncentivesIndex;
uint256 sTokenIncentivesIndex;
} }
//
// struct ReserveData {
// uint256 averageStableBorrowRate;
// uint256 totalLiquidity;
// }
struct UserReserveData { struct UserReserveData {
address underlyingAsset; address underlyingAsset;
@ -56,38 +62,19 @@ interface IUiPoolDataProvider {
uint256 scaledVariableDebt; uint256 scaledVariableDebt;
uint256 principalStableDebt; uint256 principalStableDebt;
uint256 stableBorrowLastUpdateTimestamp; uint256 stableBorrowLastUpdateTimestamp;
// incentives
uint256 aTokenincentivesUserIndex;
uint256 vTokenincentivesUserIndex;
uint256 sTokenincentivesUserIndex;
} }
//
// struct ATokenSupplyData {
// string name;
// string symbol;
// uint8 decimals;
// uint256 totalSupply;
// address aTokenAddress;
// }
function getReservesData(ILendingPoolAddressesProvider provider, address user) function getReservesData(ILendingPoolAddressesProvider provider, address user)
external external
view view
returns ( returns (
AggregatedReserveData[] memory, AggregatedReserveData[] memory,
UserReserveData[] memory, UserReserveData[] memory,
uint256,
uint256 uint256
); );
// function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
// external
// view
// returns (UserReserveData[] memory);
//
// function getAllATokenSupply(ILendingPoolAddressesProvider provider)
// external
// view
// returns (ATokenSupplyData[] memory);
//
// function getATokenSupply(address[] calldata aTokens)
// external
// view
// returns (ATokenSupplyData[] memory);
} }

View File

@ -27,4 +27,14 @@ interface IWETHGateway {
uint256 interesRateMode, uint256 interesRateMode,
uint16 referralCode uint16 referralCode
) external; ) external;
function withdrawETHWithPermit(
address lendingPool,
uint256 amount,
address to,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) external;
} }

View File

@ -8,14 +8,67 @@ import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
* @dev ERC20 minting logic * @dev ERC20 minting logic
*/ */
contract MintableERC20 is ERC20 { contract MintableERC20 is ERC20 {
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_TYPEHASH =
keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
mapping(address => uint256) public _nonces;
bytes32 public DOMAIN_SEPARATOR;
constructor( constructor(
string memory name, string memory name,
string memory symbol, string memory symbol,
uint8 decimals uint8 decimals
) public ERC20(name, symbol) { ) public ERC20(name, symbol) {
uint256 chainId;
assembly {
chainId := chainid()
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
EIP712_DOMAIN,
keccak256(bytes(name)),
keccak256(EIP712_REVISION),
chainId,
address(this)
)
);
_setupDecimals(decimals); _setupDecimals(decimals);
} }
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
require(owner != address(0), 'INVALID_OWNER');
//solium-disable-next-line
require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
uint256 currentValidNonce = _nonces[owner];
bytes32 digest =
keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
)
);
require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE');
_nonces[owner] = currentValidNonce.add(1);
_approve(owner, spender, value);
}
/** /**
* @dev Function to mint tokens * @dev Function to mint tokens
* @param value The amount of tokens to mint. * @param value The amount of tokens to mint.

View File

@ -4,6 +4,7 @@ pragma experimental ABIEncoderV2;
import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
import {IERC20WithPermit} from '../../interfaces/IERC20WithPermit.sol';
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol'; import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
import {Address} from '../../dependencies/openzeppelin/contracts/Address.sol'; import {Address} from '../../dependencies/openzeppelin/contracts/Address.sol';
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
@ -107,25 +108,36 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
address onBehalfOf, address onBehalfOf,
uint16 referralCode uint16 referralCode
) external override whenNotPaused { ) external override whenNotPaused {
DataTypes.ReserveData storage reserve = _reserves[asset]; _executeDeposit(asset, amount, onBehalfOf, referralCode);
}
ValidationLogic.validateDeposit(reserve, amount); /**
* @notice Deposit with transfer approval of asset to be deposited done via permit function
address aToken = reserve.aTokenAddress; * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
* @param asset The address of the underlying asset to deposit
reserve.updateState(); * @param amount The amount to be deposited
reserve.updateInterestRates(asset, aToken, amount, 0); * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
IERC20(asset).safeTransferFrom(msg.sender, aToken, amount); * is a different wallet
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); * 0 if the action is executed directly by the user, without any middle-man
* @param deadline validity deadline of permit and so depositWithPermit signature
if (isFirstDeposit) { * @param permitV V parameter of ERC712 permit sig
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); * @param permitR R parameter of ERC712 permit sig
emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); * @param permitS S parameter of ERC712 permit sig
} **/
function depositWithPermit(
emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode); address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) external override {
IERC20WithPermit(asset).permit(msg.sender, address(this), amount, deadline, permitV, permitR, permitS);
_executeDeposit(asset, amount, onBehalfOf, referralCode);
} }
/** /**
@ -205,6 +217,36 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
return _executeRepay(asset, amount, rateMode, onBehalfOf); return _executeRepay(asset, amount, rateMode, onBehalfOf);
} }
/**
* @notice Repay with transfer approval of asset to be repaid done via permit function
* see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
* @param asset The address of the borrowed underlying asset previously borrowed
* @param amount The amount to repay
* - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
* @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
* @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
* user calling the function if he wants to reduce/remove his own debt, or the address of any other
* other borrower whose debt should be removed
* @param deadline validity deadline of permit and so depositWithPermit signature
* @param permitV V parameter of ERC712 permit sig
* @param permitR R parameter of ERC712 permit sig
* @param permitS S parameter of ERC712 permit sig
* @return The final amount repaid
**/
function repayWithPermit(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) external override returns (uint256) {
IERC20WithPermit(asset).permit(msg.sender, address(this), amount, deadline, permitV, permitR, permitS);
return _executeRepay(asset, amount, rateMode, onBehalfOf);
}
/** /**
* @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
* @param asset The address of the underlying asset borrowed * @param asset The address of the underlying asset borrowed
@ -865,6 +907,33 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
); );
} }
function _executeDeposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) internal {
DataTypes.ReserveData storage reserve = _reserves[asset];
ValidationLogic.validateDeposit(reserve, amount);
address aToken = reserve.aTokenAddress;
reserve.updateState();
reserve.updateInterestRates(asset, aToken, amount, 0);
IERC20(asset).safeTransferFrom(msg.sender, aToken, amount);
bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
if (isFirstDeposit) {
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
}
emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode);
}
function _executeWithdraw( function _executeWithdraw(
address asset, address asset,
uint256 amount, uint256 amount,

View File

@ -4,6 +4,7 @@ pragma experimental ABIEncoderV2;
import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
import {ReserveLogic} from './ReserveLogic.sol'; import {ReserveLogic} from './ReserveLogic.sol';
import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
import {UserConfiguration} from '../configuration/UserConfiguration.sol'; import {UserConfiguration} from '../configuration/UserConfiguration.sol';
@ -116,10 +117,12 @@ library GenericLogic {
} }
struct CalculateUserAccountDataVars { struct CalculateUserAccountDataVars {
uint256 reserveUnitPrice; uint256 assetPrice;
uint256 tokenUnit; uint256 assetUnit;
uint256 compoundedLiquidityBalance; uint256 userBalance;
uint256 compoundedBorrowBalance; uint256 userBalanceETH;
uint256 userDebt;
uint256 userDebtETH;
uint256 decimals; uint256 decimals;
uint256 ltv; uint256 ltv;
uint256 liquidationThreshold; uint256 liquidationThreshold;
@ -130,6 +133,8 @@ library GenericLogic {
uint256 avgLtv; uint256 avgLtv;
uint256 avgLiquidationThreshold; uint256 avgLiquidationThreshold;
uint256 reservesLength; uint256 reservesLength;
uint256 normalizedIncome;
uint256 normalizedDebt;
bool healthFactorBelowThreshold; bool healthFactorBelowThreshold;
address currentReserveAddress; address currentReserveAddress;
bool usageAsCollateralEnabled; bool usageAsCollateralEnabled;
@ -182,34 +187,41 @@ library GenericLogic {
.configuration .configuration
.getParams(); .getParams();
vars.tokenUnit = 10**vars.decimals; vars.assetUnit = 10**vars.decimals;
vars.reserveUnitPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress); vars.assetPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress);
if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) { if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) {
vars.compoundedLiquidityBalance = IERC20(currentReserve.aTokenAddress).balanceOf(user); vars.userBalance = IScaledBalanceToken(currentReserve.aTokenAddress).scaledBalanceOf(user);
if (vars.userBalance > 0) {
vars.normalizedIncome = currentReserve.getNormalizedIncome();
vars.userBalance = vars.userBalance.rayMul(vars.normalizedIncome);
}
uint256 liquidityBalanceETH = vars.userBalanceETH = vars.assetPrice.mul(vars.userBalance).div(vars.assetUnit);
vars.reserveUnitPrice.mul(vars.compoundedLiquidityBalance).div(vars.tokenUnit);
vars.totalCollateralInETH = vars.totalCollateralInETH.add(liquidityBalanceETH); vars.totalCollateralInETH = vars.totalCollateralInETH.add(vars.userBalanceETH);
vars.avgLtv = vars.avgLtv.add(liquidityBalanceETH.mul(vars.ltv)); vars.avgLtv = vars.avgLtv.add(vars.userBalanceETH.mul(vars.ltv));
vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add( vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add(
liquidityBalanceETH.mul(vars.liquidationThreshold) vars.userBalanceETH.mul(vars.liquidationThreshold)
); );
} }
if (userConfig.isBorrowing(vars.i)) { if (userConfig.isBorrowing(vars.i)) {
vars.compoundedBorrowBalance = IERC20(currentReserve.stableDebtTokenAddress).balanceOf( vars.userDebt = IScaledBalanceToken(currentReserve.variableDebtTokenAddress)
user .scaledBalanceOf(user);
);
vars.compoundedBorrowBalance = vars.compoundedBorrowBalance.add(
IERC20(currentReserve.variableDebtTokenAddress).balanceOf(user)
);
vars.totalDebtInETH = vars.totalDebtInETH.add( if (vars.userDebt > 0) {
vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit) vars.normalizedDebt = currentReserve.getNormalizedDebt();
vars.userDebt = vars.userDebt.rayMul(vars.normalizedDebt);
}
vars.userDebt = vars.userDebt.add(
IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user)
); );
vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit);
vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH);
} }
} }

View File

@ -55,12 +55,28 @@ import {
registerContractInJsonDb, registerContractInJsonDb,
linkBytecode, linkBytecode,
insertContractAddressInDb, insertContractAddressInDb,
deployContract,
} from './contracts-helpers'; } from './contracts-helpers';
import { StableAndVariableTokensHelperFactory } from '../types/StableAndVariableTokensHelperFactory'; import { StableAndVariableTokensHelperFactory } from '../types/StableAndVariableTokensHelperFactory';
import { MintableDelegationERC20 } from '../types/MintableDelegationERC20'; import { MintableDelegationERC20 } from '../types/MintableDelegationERC20';
import { readArtifact as buidlerReadArtifact } from '@nomiclabs/buidler/plugins'; import { readArtifact as buidlerReadArtifact } from '@nomiclabs/buidler/plugins';
import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { LendingPoolLibraryAddresses } from '../types/LendingPoolFactory'; import { LendingPoolLibraryAddresses } from '../types/LendingPoolFactory';
import { UiPoolDataProvider } from '../types';
import { verifyContract } from './etherscan-verification';
export const deployUiPoolDataProvider = async (
[incentivesController, aaveOracle]: [tEthereumAddress, tEthereumAddress],
verify?: boolean
) => {
const id = eContractid.UiPoolDataProvider;
const args: string[] = [incentivesController, aaveOracle];
const instance = await deployContract<UiPoolDataProvider>(id, args);
if (verify) {
await verifyContract(instance.address, args);
}
return instance;
};
const readArtifact = async (id: string) => { const readArtifact = async (id: string) => {
if (DRE.network.name === eEthereumNetwork.buidlerevm) { if (DRE.network.name === eEthereumNetwork.buidlerevm) {
@ -546,7 +562,15 @@ export const deployMockVariableDebtToken = async (
}; };
export const deployMockAToken = async ( export const deployMockAToken = async (
args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string, string], args: [
tEthereumAddress,
tEthereumAddress,
tEthereumAddress,
tEthereumAddress,
string,
string,
string
],
verify?: boolean verify?: boolean
) => { ) => {
const instance = await withSaveAndVerify( const instance = await withSaveAndVerify(

View File

@ -363,3 +363,5 @@ export const getFlashLiquidationAdapter = async (address?: tEthereumAddress) =>
.address, .address,
await getFirstSigner() await getFirstSigner()
); );
export const getChainId = async () => (await DRE.ethers.provider.getNetwork()).chainId;

310
package-lock.json generated
View File

@ -3118,7 +3118,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dev": true,
"requires": { "requires": {
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2" "get-intrinsic": "^1.0.2"
@ -5530,8 +5529,7 @@
"function-bind": { "function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"dev": true
}, },
"functional-red-black-tree": { "functional-red-black-tree": {
"version": "1.0.1", "version": "1.0.1",
@ -6370,36 +6368,6 @@
"@ethersproject/strings": ">=5.0.0-beta.130" "@ethersproject/strings": ">=5.0.0-beta.130"
} }
}, },
"@ethersproject/abstract-provider": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.0.8.tgz",
"integrity": "sha512-fqJXkewcGdi8LogKMgRyzc/Ls2js07yor7+g9KfPs09uPOcQLg7cc34JN+lk34HH9gg2HU0DIA5797ZR8znkfw==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/bignumber": "^5.0.13",
"@ethersproject/bytes": "^5.0.9",
"@ethersproject/logger": "^5.0.8",
"@ethersproject/networks": "^5.0.7",
"@ethersproject/properties": "^5.0.7",
"@ethersproject/transactions": "^5.0.9",
"@ethersproject/web": "^5.0.12"
}
},
"@ethersproject/abstract-signer": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.0.10.tgz",
"integrity": "sha512-irx7kH7FDAeW7QChDPW19WsxqeB1d3XLyOLSXm0bfPqL1SS07LXWltBJUBUxqC03ORpAOcM3JQj57DU8JnVY2g==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/abstract-provider": "^5.0.8",
"@ethersproject/bignumber": "^5.0.13",
"@ethersproject/bytes": "^5.0.9",
"@ethersproject/logger": "^5.0.8",
"@ethersproject/properties": "^5.0.7"
}
},
"@ethersproject/address": { "@ethersproject/address": {
"version": "5.0.9", "version": "5.0.9",
"resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.9.tgz", "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.9.tgz",
@ -6414,16 +6382,6 @@
"@ethersproject/rlp": "^5.0.7" "@ethersproject/rlp": "^5.0.7"
} }
}, },
"@ethersproject/base64": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.0.7.tgz",
"integrity": "sha512-S5oh5DVfCo06xwJXT8fQC68mvJfgScTl2AXvbYMsHNfIBTDb084Wx4iA9MNlEReOv6HulkS+gyrUM/j3514rSw==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/bytes": "^5.0.9"
}
},
"@ethersproject/bignumber": { "@ethersproject/bignumber": {
"version": "5.0.13", "version": "5.0.13",
"resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.13.tgz", "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.13.tgz",
@ -6491,16 +6449,6 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"@ethersproject/networks": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.0.7.tgz",
"integrity": "sha512-dI14QATndIcUgcCBL1c5vUr/YsI5cCHLN81rF7PU+yS7Xgp2/Rzbr9+YqpC6NBXHFUASjh6GpKqsVMpufAL0BQ==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/logger": "^5.0.8"
}
},
"@ethersproject/properties": { "@ethersproject/properties": {
"version": "5.0.7", "version": "5.0.7",
"resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.7.tgz", "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.7.tgz",
@ -6565,20 +6513,6 @@
"@ethersproject/signing-key": "^5.0.8" "@ethersproject/signing-key": "^5.0.8"
} }
}, },
"@ethersproject/web": {
"version": "5.0.12",
"resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.0.12.tgz",
"integrity": "sha512-gVxS5iW0bgidZ76kr7LsTxj4uzN5XpCLzvZrLp8TP+4YgxHfCeetFyQkRPgBEAJdNrexdSBayvyJvzGvOq0O8g==",
"dev": true,
"optional": true,
"requires": {
"@ethersproject/base64": "^5.0.7",
"@ethersproject/bytes": "^5.0.9",
"@ethersproject/logger": "^5.0.8",
"@ethersproject/properties": "^5.0.7",
"@ethersproject/strings": "^5.0.8"
}
},
"@sindresorhus/is": { "@sindresorhus/is": {
"version": "0.14.0", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -7778,9 +7712,9 @@
}, },
"dependencies": { "dependencies": {
"bn.js": { "bn.js": {
"version": "5.1.3", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
"integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==",
"dev": true, "dev": true,
"optional": true "optional": true
} }
@ -7891,6 +7825,14 @@
"dev": true, "dev": true,
"requires": { "requires": {
"node-gyp-build": "^4.2.0" "node-gyp-build": "^4.2.0"
},
"dependencies": {
"node-gyp-build": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
"dev": true
}
} }
}, },
"bytes": { "bytes": {
@ -7991,16 +7933,6 @@
} }
} }
}, },
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
}
},
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001174", "version": "1.0.30001174",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001174.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001174.tgz",
@ -8519,7 +8451,6 @@
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": { "requires": {
"object-keys": "^1.0.12" "object-keys": "^1.0.12"
} }
@ -8745,7 +8676,6 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": { "requires": {
"is-callable": "^1.1.4", "is-callable": "^1.1.4",
"is-date-object": "^1.0.1", "is-date-object": "^1.0.1",
@ -10915,8 +10845,7 @@
"function-bind": { "function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"dev": true
}, },
"functional-red-black-tree": { "functional-red-black-tree": {
"version": "1.0.1", "version": "1.0.1",
@ -10924,17 +10853,6 @@
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true "dev": true
}, },
"get-intrinsic": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz",
"integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1"
}
},
"get-stream": { "get-stream": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
@ -10982,6 +10900,14 @@
"requires": { "requires": {
"min-document": "^2.19.0", "min-document": "^2.19.0",
"process": "^0.11.10" "process": "^0.11.10"
},
"dependencies": {
"process": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
"integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=",
"dev": true
}
} }
}, },
"got": { "got": {
@ -11042,7 +10968,6 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": { "requires": {
"function-bind": "^1.1.1" "function-bind": "^1.1.1"
} }
@ -11080,8 +11005,7 @@
"has-symbols": { "has-symbols": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
"dev": true
}, },
"has-to-string-tag-x": { "has-to-string-tag-x": {
"version": "1.4.1", "version": "1.4.1",
@ -11354,8 +11278,7 @@
"is-callable": { "is-callable": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
"integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
"dev": true
}, },
"is-ci": { "is-ci": {
"version": "2.0.0", "version": "2.0.0",
@ -11378,8 +11301,7 @@
"is-date-object": { "is-date-object": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
"dev": true
}, },
"is-descriptor": { "is-descriptor": {
"version": "1.0.2", "version": "1.0.2",
@ -11428,8 +11350,7 @@
"is-negative-zero": { "is-negative-zero": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
"integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w=="
"dev": true
}, },
"is-object": { "is-object": {
"version": "1.0.2", "version": "1.0.2",
@ -11458,7 +11379,6 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
"integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
"dev": true,
"requires": { "requires": {
"has-symbols": "^1.0.1" "has-symbols": "^1.0.1"
} }
@ -11474,7 +11394,6 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
"integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
"dev": true,
"requires": { "requires": {
"has-symbols": "^1.0.1" "has-symbols": "^1.0.1"
} }
@ -12317,8 +12236,7 @@
"object-inspect": { "object-inspect": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
"integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw=="
"dev": true
}, },
"object-is": { "object-is": {
"version": "1.1.4", "version": "1.1.4",
@ -12328,13 +12246,33 @@
"requires": { "requires": {
"call-bind": "^1.0.0", "call-bind": "^1.0.0",
"define-properties": "^1.1.3" "define-properties": "^1.1.3"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
} }
}, },
"object-keys": { "object-keys": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
"dev": true
}, },
"object-visit": { "object-visit": {
"version": "1.0.1", "version": "1.0.1",
@ -12349,12 +12287,32 @@
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
"integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
"dev": true,
"requires": { "requires": {
"call-bind": "^1.0.0", "call-bind": "^1.0.0",
"define-properties": "^1.1.3", "define-properties": "^1.1.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"object-keys": "^1.1.1" "object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
} }
}, },
"object.getownpropertydescriptors": { "object.getownpropertydescriptors": {
@ -12626,12 +12584,6 @@
"integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
"dev": true "dev": true
}, },
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
"dev": true
},
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -12892,22 +12844,71 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.7", "version": "1.18.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz",
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==",
"dev": true, "dev": true,
"requires": { "requires": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"get-intrinsic": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.2",
"is-callable": "^1.2.2", "is-callable": "^1.2.3",
"is-regex": "^1.1.1", "is-negative-zero": "^2.0.1",
"object-inspect": "^1.8.0", "is-regex": "^1.1.2",
"is-string": "^1.0.5",
"object-inspect": "^1.9.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.1", "object.assign": "^4.1.2",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.4",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.4",
"unbox-primitive": "^1.0.0"
},
"dependencies": {
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"is-callable": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
"integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
"dev": true
},
"is-regex": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz",
"integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"has-symbols": "^1.0.1"
}
},
"string.prototype.trimend": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
"integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"string.prototype.trimstart": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
"integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
}
} }
} }
} }
@ -12995,6 +12996,15 @@
"uuid": "^3.3.2" "uuid": "^3.3.2"
} }
}, },
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"resolve-url": { "resolve-url": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@ -13653,13 +13663,34 @@
"call-bind": "^1.0.0", "call-bind": "^1.0.0",
"define-properties": "^1.1.3", "define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.1" "es-abstract": "^1.18.0-next.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
} }
}, },
"string.prototype.trimend": { "string.prototype.trimend": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz",
"integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==",
"dev": true,
"requires": { "requires": {
"call-bind": "^1.0.0", "call-bind": "^1.0.0",
"define-properties": "^1.1.3" "define-properties": "^1.1.3"
@ -13669,7 +13700,6 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz",
"integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==",
"dev": true,
"requires": { "requires": {
"call-bind": "^1.0.0", "call-bind": "^1.0.0",
"define-properties": "^1.1.3" "define-properties": "^1.1.3"
@ -13856,15 +13886,6 @@
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
"dev": true "dev": true
},
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
} }
} }
}, },
@ -14226,6 +14247,14 @@
"dev": true, "dev": true,
"requires": { "requires": {
"node-gyp-build": "^4.2.0" "node-gyp-build": "^4.2.0"
},
"dependencies": {
"node-gyp-build": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
"dev": true
}
} }
}, },
"utf8": { "utf8": {
@ -15204,7 +15233,6 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"dev": true,
"requires": { "requires": {
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
@ -15761,7 +15789,6 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": { "requires": {
"function-bind": "^1.1.1" "function-bind": "^1.1.1"
} }
@ -15787,8 +15814,7 @@
"has-symbols": { "has-symbols": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
"dev": true
}, },
"has-to-string-tag-x": { "has-to-string-tag-x": {
"version": "1.4.1", "version": "1.4.1",

View File

@ -21,7 +21,7 @@
"test": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-aave/*.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": "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-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-scenarios": "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-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-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-liquidate-underlying": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/liquidation-underlying.spec.ts",
@ -60,8 +60,11 @@
"ci:clean": "rm -rf ./artifacts ./cache ./types", "ci:clean": "rm -rf ./artifacts ./cache ./types",
"print-contracts:kovan": "npm run hardhat:kovan -- print-contracts", "print-contracts:kovan": "npm run hardhat:kovan -- print-contracts",
"print-contracts:main": "npm run hardhat:main -- print-contracts", "print-contracts:main": "npm run hardhat:main -- print-contracts",
"print-contracts:ropsten": "npm run hardhat:ropsten -- print-contracts", "print-contracts:ropsten": "npm run hardhat:main -- print-contracts",
"dev:deployUIProvider": "npm run hardhat:kovan deploy-UiPoolDataProvider", "dev:deployUIProvider": "hardhat --network kovan deploy-UiPoolDataProvider --verify",
"main:deployUIProvider": "hardhat --network main deploy-UiPoolDataProvider --verify",
"matic:deployUIProvider": "hardhat --network matic deploy-UiPoolDataProvider",
"mumbai:deployUIProvider": "hardhat --network mumbai deploy-UiPoolDataProvider",
"dev:deployUniswapRepayAdapter": "hardhat --network kovan deploy-UniswapRepayAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c", "dev:deployUniswapRepayAdapter": "hardhat --network kovan deploy-UniswapRepayAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c",
"dev:UniswapLiquiditySwapAdapter": "hardhat --network kovan deploy-UniswapLiquiditySwapAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c", "dev:UniswapLiquiditySwapAdapter": "hardhat --network kovan deploy-UniswapLiquiditySwapAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c",
"main:deployUniswapRepayAdapter": "hardhat --network main deploy-UniswapRepayAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D --weth 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "main:deployUniswapRepayAdapter": "hardhat --network main deploy-UniswapRepayAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D --weth 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",

View File

@ -1,27 +1,44 @@
import { task } from 'hardhat/config'; import { task } from 'hardhat/config';
import { eContractid, eEthereumNetwork, ePolygonNetwork } from '../../helpers/types';
import { UiPoolDataProviderFactory } from '../../types'; import { deployUiPoolDataProvider } from '../../helpers/contracts-deployments';
import { verifyContract } from '../../helpers/etherscan-verification';
import { eContractid } from '../../helpers/types';
task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider contract`) task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider contract`)
.addFlag('verify', 'Verify UiPoolDataProvider contract via Etherscan API.') .addFlag('verify', 'Verify UiPoolDataProvider contract via Etherscan API.')
.setAction(async ({ verify }, localBRE) => { .setAction(async ({ verify }, localBRE) => {
await localBRE.run('set-DRE'); await localBRE.run('set-DRE');
if (!localBRE.network.config.chainId) { if (!localBRE.network.config.chainId) {
throw new Error('INVALID_CHAIN_ID'); throw new Error('INVALID_CHAIN_ID');
} }
const addressesByNetwork = {
[eEthereumNetwork.kovan]: {
incentivesController: '0x0000000000000000000000000000000000000000',
aaveOracle: '0x8fb777d67e9945e2c01936e319057f9d41d559e6',
},
[eEthereumNetwork.main]: {
incentivesController: '0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5',
aaveOracle: '0xa50ba011c48153de246e5192c8f9258a2ba79ca9',
},
[ePolygonNetwork.matic]: {
incentivesController: '0x357D51124f59836DeD84c8a1730D72B749d8BC23',
aaveOracle: '0x21451bD7b528896B4AB2b9764b521D6ed641708d',
},
[ePolygonNetwork.mumbai]: {
incentivesController: '0xd41aE58e803Edf4304334acCE4DC4Ec34a63C644',
aaveOracle: '0xC365C653f7229894F93994CD0b30947Ab69Ff1D5',
},
};
console.log(`\n- UiPoolDataProvider deployment`); console.log(`\n- UiPoolDataProvider deployment`);
console.log(`\tDeploying UiPoolDataProvider implementation ...`); console.log(`\tDeploying UiPoolDataProvider implementation ...`);
const uiPoolDataProvider = await new UiPoolDataProviderFactory( const UiPoolDataProvider = await deployUiPoolDataProvider(
await localBRE.ethers.provider.getSigner() [
).deploy(); addressesByNetwork[localBRE.network.name].incentivesController,
await uiPoolDataProvider.deployTransaction.wait(); addressesByNetwork[localBRE.network.name].aaveOracle,
console.log('uiPoolDataProvider.address', uiPoolDataProvider.address); ],
await verifyContract(uiPoolDataProvider.address, []); verify
);
console.log(`\tFinished UiPoolDataProvider proxy and implementation deployment`); console.log(`\tFinished UiPoolDataProvider deployment: ${UiPoolDataProvider.address}`);
}); });

View File

@ -16,6 +16,7 @@ import {
calcExpectedUserDataAfterWithdraw, calcExpectedUserDataAfterWithdraw,
} from './utils/calculations'; } from './utils/calculations';
import { getReserveAddressFromSymbol, getReserveData, getUserData } from './utils/helpers'; import { getReserveAddressFromSymbol, getReserveData, getUserData } from './utils/helpers';
import { buildPermitParams, getSignatureFromTypedData } from '../../../helpers/contracts-helpers';
import { convertToCurrencyDecimals } from '../../../helpers/contracts-helpers'; import { convertToCurrencyDecimals } from '../../../helpers/contracts-helpers';
import { import {
@ -23,6 +24,7 @@ import {
getMintableERC20, getMintableERC20,
getStableDebtToken, getStableDebtToken,
getVariableDebtToken, getVariableDebtToken,
getChainId,
} from '../../../helpers/contracts-getters'; } from '../../../helpers/contracts-getters';
import { MAX_UINT_AMOUNT, ONE_YEAR } from '../../../helpers/constants'; import { MAX_UINT_AMOUNT, ONE_YEAR } from '../../../helpers/constants';
import { SignerWithAddress, TestEnv } from './make-suite'; import { SignerWithAddress, TestEnv } from './make-suite';
@ -30,9 +32,10 @@ import { advanceTimeAndBlock, DRE, timeLatest, waitForTx } from '../../../helper
import chai from 'chai'; import chai from 'chai';
import { ReserveData, UserReserveData } from './utils/interfaces'; import { ReserveData, UserReserveData } from './utils/interfaces';
import { ContractReceipt } from 'ethers'; import { ContractReceipt, Wallet } from 'ethers';
import { AToken } from '../../../types/AToken'; import { AToken } from '../../../types/AToken';
import { RateMode, tEthereumAddress } from '../../../helpers/types'; import { RateMode, tEthereumAddress } from '../../../helpers/types';
import { MintableERC20Factory } from '../../../types';
const { expect } = chai; const { expect } = chai;
@ -349,7 +352,7 @@ export const borrow = async (
); );
const amountToBorrow = await convertToCurrencyDecimals(reserve, amount); const amountToBorrow = await convertToCurrencyDecimals(reserve, amount);
if (expectedResult === 'success') { if (expectedResult === 'success') {
const txResult = await waitForTx( const txResult = await waitForTx(
await pool await pool
@ -515,6 +518,257 @@ export const repay = async (
} }
}; };
export const depositWithPermit = async (
reserveSymbol: string,
amount: string,
sender: SignerWithAddress,
senderPk: string,
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 chainId = await getChainId();
const token = new MintableERC20Factory(sender.signer).attach(reserve);
const highDeadline = '100000000000000000000000000';
const nonce = await token._nonces(sender.address);
const msgParams = buildPermitParams(
chainId,
reserve,
'1',
reserveSymbol,
sender.address,
pool.address,
nonce.toNumber(),
highDeadline,
amountToDeposit.toString()
);
const { v, r, s } = getSignatureFromTypedData(senderPk, msgParams);
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)
.depositWithPermit(
reserve,
amountToDeposit,
onBehalfOf,
'0',
highDeadline,
v,
r,
s,
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)
.depositWithPermit(
reserve,
amountToDeposit,
onBehalfOf,
'0',
highDeadline,
v,
r,
s,
txOptions
),
revertMessage
).to.be.reverted;
}
};
export const repayWithPermit = async (
reserveSymbol: string,
amount: string,
rateMode: string,
user: SignerWithAddress,
userPk: string,
onBehalfOf: SignerWithAddress,
sendValue: string,
expectedResult: string,
testEnv: TestEnv,
revertMessage?: string
) => {
const { pool } = testEnv;
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
const highDeadline = '100000000000000000000000000';
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 chainId = await getChainId();
const token = new MintableERC20Factory(user.signer).attach(reserve);
const nonce = await token._nonces(user.address);
const msgParams = buildPermitParams(
chainId,
reserve,
'1',
reserveSymbol,
user.address,
pool.address,
nonce.toNumber(),
highDeadline,
amountToRepay
);
const { v, r, s } = getSignatureFromTypedData(userPk, msgParams);
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)
.repayWithPermit(
reserve,
amountToRepay,
rateMode,
onBehalfOf.address,
highDeadline,
v,
r,
s,
txOptions
)
);
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
const {
reserveData: reserveDataAfter,
userData: userDataAfter,
timestamp,
} = await getContractsData(reserve, onBehalfOf.address, testEnv);
const expectedReserveData = calcExpectedReserveDataAfterRepay(
amountToRepay,
<RateMode>rateMode,
reserveDataBefore,
userDataBefore,
txTimestamp,
timestamp
);
const expectedUserData = calcExpectedUserDataAfterRepay(
amountToRepay,
<RateMode>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)
.repayWithPermit(
reserve,
amountToRepay,
rateMode,
onBehalfOf.address,
highDeadline,
v,
r,
s,
txOptions
),
revertMessage
).to.be.reverted;
}
};
export const setUseAsCollateral = async ( export const setUseAsCollateral = async (
reserveSymbol: string, reserveSymbol: string,
user: SignerWithAddress, user: SignerWithAddress,

View File

@ -10,8 +10,11 @@ import {
swapBorrowRateMode, swapBorrowRateMode,
rebalanceStableBorrowRate, rebalanceStableBorrowRate,
delegateBorrowAllowance, delegateBorrowAllowance,
repayWithPermit,
depositWithPermit,
} from './actions'; } from './actions';
import { RateMode } from '../../../helpers/types'; import { RateMode } from '../../../helpers/types';
import { Wallet } from '@ethersproject/wallet';
export interface Action { export interface Action {
name: string; name: string;
@ -73,6 +76,12 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
const user = users[parseInt(userIndex)]; const user = users[parseInt(userIndex)];
const userPrivateKey = require('../../../test-wallets.js').accounts[parseInt(userIndex) + 1]
.secretKey;
if (!userPrivateKey) {
throw new Error('INVALID_OWNER_PK');
}
switch (name) { switch (name) {
case 'mint': case 'mint':
const { amount } = action.args; const { amount } = action.args;
@ -111,6 +120,30 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
); );
} }
break; break;
case 'depositWithPermit':
{
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 depositWithPermit(
reserve,
amount,
user,
userPrivateKey,
onBehalfOf,
sendValue,
expected,
testEnv,
revertMessage
);
}
break;
case 'delegateBorrowAllowance': case 'delegateBorrowAllowance':
{ {
@ -203,6 +236,40 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
} }
break; break;
case 'repayWithPermit':
{
const { amount, borrowRateMode, sendValue, deadline } = 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 repayWithPermit(
reserve,
amount,
rateMode,
user,
userPrivateKey,
userToRepayOnBehalf,
sendValue,
expected,
testEnv,
revertMessage
);
}
break;
case 'setUseAsCollateral': case 'setUseAsCollateral':
{ {
const { useAsCollateral } = action.args; const { useAsCollateral } = action.args;

View File

@ -0,0 +1,191 @@
{
"title": "LendingPool: Borrow/repay with permit with Permit (variable rate)",
"description": "Test cases for the borrow function, variable mode.",
"stories": [
{
"description": "User 2 deposits with permit 1 DAI to account for rounding errors",
"actions": [
{
"name": "mint",
"args": {
"reserve": "DAI",
"amount": "1",
"user": "2"
},
"expected": "success"
},
{
"name": "depositWithPermit",
"args": {
"reserve": "DAI",
"amount": "1",
"user": "2"
},
"expected": "success"
}
]
},
{
"description": "User 0 deposits with permit 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": "depositWithPermit",
"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 with permit 0 DAI (revert expected)",
"actions": [
{
"name": "repayWithPermit",
"args": {
"reserve": "DAI",
"amount": "0",
"user": "1",
"onBehalfOf": "1"
},
"expected": "revert",
"revertMessage": "Amount must be greater than 0"
}
]
},
{
"description": "User 1 repays with permit a small amount of DAI, enough to cover a small part of the interest",
"actions": [
{
"name": "repayWithPermit",
"args": {
"reserve": "DAI",
"amount": "1.25",
"user": "1",
"onBehalfOf": "1",
"borrowRateMode": "variable"
},
"expected": "success"
}
]
},
{
"description": "User 1 repays with permit 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": "repayWithPermit",
"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"
}
]
}
]
}

View File

@ -35,7 +35,7 @@ fs.readdirSync(scenarioFolder).forEach((file) => {
for (const story of scenario.stories) { for (const story of scenario.stories) {
it(story.description, async function () { it(story.description, async function () {
// Retry the test scenarios up to 4 times if an error happens, due erratic HEVM network errors // Retry the test scenarios up to 4 times in case random HEVM network errors happen
this.retries(4); this.retries(4);
await executeStory(story, testEnv); await executeStory(story, testEnv);
}); });

View File

@ -35,7 +35,7 @@ fs.readdirSync(scenarioFolder).forEach((file) => {
for (const story of scenario.stories) { for (const story of scenario.stories) {
it(story.description, async function () { it(story.description, async function () {
// Retry the test scenarios up to 4 times if an error happens, due erratic HEVM network errors // Retry the test scenarios up to 4 times in case random HEVM network errors happen
this.retries(4); this.retries(4);
await executeStory(story, testEnv); await executeStory(story, testEnv);
}); });