mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'protocol-2.5' into feat/gas-optimization-1
This commit is contained in:
commit
759e163cb3
|
|
@ -95,6 +95,27 @@ interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
|
||||||
**/
|
**/
|
||||||
function handleRepayment(address user, uint256 amount) external;
|
function handleRepayment(address user, uint256 amount) external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev implements the permit function as for
|
||||||
|
* https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
|
||||||
|
* @param owner The owner of the funds
|
||||||
|
* @param spender The spender
|
||||||
|
* @param value The amount
|
||||||
|
* @param deadline The deadline timestamp, type(uint256).max for max deadline
|
||||||
|
* @param v Signature param
|
||||||
|
* @param s Signature param
|
||||||
|
* @param r Signature param
|
||||||
|
*/
|
||||||
|
function permit(
|
||||||
|
address owner,
|
||||||
|
address spender,
|
||||||
|
uint256 value,
|
||||||
|
uint256 deadline,
|
||||||
|
uint8 v,
|
||||||
|
bytes32 r,
|
||||||
|
bytes32 s
|
||||||
|
) external;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Returns the address of the incentives controller contract
|
* @dev Returns the address of the incentives controller contract
|
||||||
**/
|
**/
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -144,43 +156,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
address to
|
address to
|
||||||
) external override whenNotPaused returns (uint256) {
|
) external override whenNotPaused returns (uint256) {
|
||||||
DataTypes.ReserveData storage reserve = _reserves[asset];
|
return _executeWithdraw(asset, amount, to);
|
||||||
|
|
||||||
address aToken = reserve.aTokenAddress;
|
|
||||||
|
|
||||||
uint256 userBalance = IAToken(aToken).balanceOf(msg.sender);
|
|
||||||
|
|
||||||
uint256 amountToWithdraw = amount;
|
|
||||||
|
|
||||||
if (amount == type(uint256).max) {
|
|
||||||
amountToWithdraw = userBalance;
|
|
||||||
}
|
|
||||||
|
|
||||||
ValidationLogic.validateWithdraw(
|
|
||||||
asset,
|
|
||||||
amountToWithdraw,
|
|
||||||
userBalance,
|
|
||||||
_reserves,
|
|
||||||
_usersConfig[msg.sender],
|
|
||||||
_reservesList,
|
|
||||||
_reservesCount,
|
|
||||||
_addressesProvider.getPriceOracle()
|
|
||||||
);
|
|
||||||
|
|
||||||
reserve.updateState();
|
|
||||||
|
|
||||||
reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw);
|
|
||||||
|
|
||||||
if (amountToWithdraw == userBalance) {
|
|
||||||
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false);
|
|
||||||
emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex);
|
|
||||||
|
|
||||||
emit Withdraw(asset, msg.sender, to, amountToWithdraw);
|
|
||||||
|
|
||||||
return amountToWithdraw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -206,7 +182,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
address onBehalfOf
|
address onBehalfOf
|
||||||
) external override whenNotPaused {
|
) external override whenNotPaused {
|
||||||
DataTypes.ReserveData storage reserve = _reserves[asset];
|
DataTypes.ReserveData storage reserve = _reserves[asset];
|
||||||
|
|
||||||
_executeBorrow(
|
_executeBorrow(
|
||||||
ExecuteBorrowParams(
|
ExecuteBorrowParams(
|
||||||
asset,
|
asset,
|
||||||
|
|
@ -239,54 +214,37 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
uint256 rateMode,
|
uint256 rateMode,
|
||||||
address onBehalfOf
|
address onBehalfOf
|
||||||
) external override whenNotPaused returns (uint256) {
|
) external override whenNotPaused returns (uint256) {
|
||||||
DataTypes.ReserveData storage reserve = _reserves[asset];
|
return _executeRepay(asset, amount, rateMode, onBehalfOf);
|
||||||
|
}
|
||||||
|
|
||||||
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
|
/**
|
||||||
|
* @notice Repay with transfer approval of asset to be repaid done via permit function
|
||||||
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
|
* 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
|
||||||
ValidationLogic.validateRepay(
|
* @param amount The amount to repay
|
||||||
reserve,
|
* - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
|
||||||
amount,
|
* @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
|
||||||
interestRateMode,
|
* @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
|
||||||
onBehalfOf,
|
* user calling the function if he wants to reduce/remove his own debt, or the address of any other
|
||||||
stableDebt,
|
* other borrower whose debt should be removed
|
||||||
variableDebt
|
* @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
|
||||||
uint256 paybackAmount =
|
* @param permitS S parameter of ERC712 permit sig
|
||||||
interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt;
|
* @return The final amount repaid
|
||||||
|
**/
|
||||||
if (amount < paybackAmount) {
|
function repayWithPermit(
|
||||||
paybackAmount = amount;
|
address asset,
|
||||||
}
|
uint256 amount,
|
||||||
|
uint256 rateMode,
|
||||||
reserve.updateState();
|
address onBehalfOf,
|
||||||
|
uint256 deadline,
|
||||||
if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
|
uint8 permitV,
|
||||||
IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount);
|
bytes32 permitR,
|
||||||
} else {
|
bytes32 permitS
|
||||||
IVariableDebtToken(reserve.variableDebtTokenAddress).burn(
|
) external override returns (uint256) {
|
||||||
onBehalfOf,
|
IERC20WithPermit(asset).permit(msg.sender, address(this), amount, deadline, permitV, permitR, permitS);
|
||||||
paybackAmount,
|
return _executeRepay(asset, amount, rateMode, onBehalfOf);
|
||||||
reserve.variableBorrowIndex
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
address aToken = reserve.aTokenAddress;
|
|
||||||
reserve.updateInterestRates(asset, aToken, paybackAmount, 0);
|
|
||||||
|
|
||||||
if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
|
|
||||||
_usersConfig[onBehalfOf].setBorrowing(reserve.id, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount);
|
|
||||||
|
|
||||||
IAToken(aToken).handleRepayment(msg.sender, paybackAmount);
|
|
||||||
|
|
||||||
emit Repay(asset, onBehalfOf, msg.sender, paybackAmount);
|
|
||||||
|
|
||||||
return paybackAmount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -929,6 +887,133 @@ 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(
|
||||||
|
address asset,
|
||||||
|
uint256 amount,
|
||||||
|
address to
|
||||||
|
) internal returns (uint256) {
|
||||||
|
DataTypes.ReserveData storage reserve = _reserves[asset];
|
||||||
|
|
||||||
|
address aToken = reserve.aTokenAddress;
|
||||||
|
|
||||||
|
uint256 userBalance = IAToken(aToken).balanceOf(msg.sender);
|
||||||
|
|
||||||
|
uint256 amountToWithdraw = amount;
|
||||||
|
|
||||||
|
if (amount == type(uint256).max) {
|
||||||
|
amountToWithdraw = userBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationLogic.validateWithdraw(
|
||||||
|
asset,
|
||||||
|
amountToWithdraw,
|
||||||
|
userBalance,
|
||||||
|
_reserves,
|
||||||
|
_usersConfig[msg.sender],
|
||||||
|
_reservesList,
|
||||||
|
_reservesCount,
|
||||||
|
_addressesProvider.getPriceOracle()
|
||||||
|
);
|
||||||
|
|
||||||
|
reserve.updateState();
|
||||||
|
|
||||||
|
reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw);
|
||||||
|
|
||||||
|
if (amountToWithdraw == userBalance) {
|
||||||
|
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false);
|
||||||
|
emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex);
|
||||||
|
|
||||||
|
emit Withdraw(asset, msg.sender, to, amountToWithdraw);
|
||||||
|
|
||||||
|
return amountToWithdraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _executeRepay(
|
||||||
|
address asset,
|
||||||
|
uint256 amount,
|
||||||
|
uint256 rateMode,
|
||||||
|
address onBehalfOf
|
||||||
|
) internal returns (uint256) {
|
||||||
|
DataTypes.ReserveData storage reserve = _reserves[asset];
|
||||||
|
|
||||||
|
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
|
||||||
|
|
||||||
|
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
|
||||||
|
|
||||||
|
ValidationLogic.validateRepay(
|
||||||
|
reserve,
|
||||||
|
amount,
|
||||||
|
interestRateMode,
|
||||||
|
onBehalfOf,
|
||||||
|
stableDebt,
|
||||||
|
variableDebt
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 paybackAmount =
|
||||||
|
interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt;
|
||||||
|
|
||||||
|
if (amount < paybackAmount) {
|
||||||
|
paybackAmount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
reserve.updateState();
|
||||||
|
|
||||||
|
if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
|
||||||
|
IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount);
|
||||||
|
} else {
|
||||||
|
IVariableDebtToken(reserve.variableDebtTokenAddress).burn(
|
||||||
|
onBehalfOf,
|
||||||
|
paybackAmount,
|
||||||
|
reserve.variableBorrowIndex
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
address aToken = reserve.aTokenAddress;
|
||||||
|
reserve.updateInterestRates(asset, aToken, paybackAmount, 0);
|
||||||
|
|
||||||
|
if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
|
||||||
|
_usersConfig[onBehalfOf].setBorrowing(reserve.id, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount);
|
||||||
|
|
||||||
|
IAToken(aToken).handleRepayment(msg.sender, paybackAmount);
|
||||||
|
|
||||||
|
emit Repay(asset, onBehalfOf, msg.sender, paybackAmount);
|
||||||
|
|
||||||
|
return paybackAmount;
|
||||||
|
}
|
||||||
|
|
||||||
function _addReserveToList(address asset) internal {
|
function _addReserveToList(address asset) internal {
|
||||||
uint256 reservesCount = _reservesCount;
|
uint256 reservesCount = _reservesCount;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ contract AToken is
|
||||||
uint8 v,
|
uint8 v,
|
||||||
bytes32 r,
|
bytes32 r,
|
||||||
bytes32 s
|
bytes32 s
|
||||||
) external {
|
) external override {
|
||||||
require(owner != address(0), 'INVALID_OWNER');
|
require(owner != address(0), 'INVALID_OWNER');
|
||||||
//solium-disable-next-line
|
//solium-disable-next-line
|
||||||
require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
|
require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
21
package-lock.json
generated
21
package-lock.json
generated
|
|
@ -7709,6 +7709,15 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^5.0.0",
|
"bn.js": "^5.0.0",
|
||||||
"randombytes": "^2.0.1"
|
"randombytes": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bn.js": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserify-sign": {
|
"browserify-sign": {
|
||||||
|
|
@ -7819,9 +7828,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-gyp-build": {
|
"node-gyp-build": {
|
||||||
"version": "3.7.0",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
|
||||||
"integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==",
|
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14241,9 +14250,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-gyp-build": {
|
"node-gyp-build": {
|
||||||
"version": "3.7.0",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
|
||||||
"integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==",
|
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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}`);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user